/*******************************************************************
/*  ActionsUnit.cpp
/*  Author: Vadim Berman
/*
/*  Description: this unit implements two basic Brainiac classes,
/*  Action and Strategy, as well as some non-member functions related
/*  to these classes
/*
/*  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 "ActionsUnit.h"
#include "AlignmentUnit.h"
//-----------------------------------------------------------------------
//Global variables
//#define ACTION_DEBUG_MODE 1
GoalDefNodeP 		GoalListHead = NULL;
ActionDefNodeP		ActionListHead = NULL;
StrategyDefNodeP	StrategyListHead = NULL;
MindDefNodeP        MindListHead = NULL;
HMODULE hBE;
ModuleEntry   *Modules = NULL;
unsigned short nExtModules = 0;
extern clock_t start_time;
clock_t end_time;
extern BOOL UseSuddenChange,UseHistory;
extern EventsRegistry History;
//-----------------------------------------------------------------------------
//----------------------- Functions not related to any class ------------------
//-----------------------------------------------------------------------------

//***************************************************************************
//InitActionLib
//********** Description: **************
//load all the goals-actions-strategies to the linked lists. Also initialize
//some global variables
//********** Parameters:  **************
//int (*DefActionFn)(void*) - default action handler function
//---------------------------------------------------------------------------
BOOL BBECALL InitActionLib(int (BBECALL *DefActionFn)(void*))
{
    BYTE RecBuf[3000];
    HANDLE hFile;
    GoalFileRec	GoalRecordBuf;
    ActFileRec  ActRecordBuf;
    StrFileRec	StrRecordBuf;
    MindFileRec MindRecordBuf;
    ModuleRec   ModRecordBuf;
    char szPath[_MAX_PATH],szFName[_MAX_PATH];
    unsigned long nBytesRead,nRecSize, i;

    strcpy(szPath,twGetModuleDirectory());
    hBE = GetModuleHandle("bbe.dll");

    //initialize lists
	GoalListHead = NULL;
	ActionListHead = NULL;
	StrategyListHead = NULL;

    strcpy(szFName,szPath);
    strcat(szFName,"beparam.db");
    //load BeParam.db (if exists)
    hFile = CreateFile(szFName,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if(hFile != INVALID_HANDLE_VALUE)
    {
        nRecSize = sizeof(unsigned char) + sizeof(ID) + MAX_MODULE_NAME
            + sizeof(unsigned char);
        nExtModules = 0;
        do {
            ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
            if(!nBytesRead)
                break;
            if(Buf2ModRec(RecBuf,ModRecordBuf) == TRUE)
                nExtModules++;
        } while( nBytesRead );

        i = 0;
        SetFilePointer(hFile,0,NULL,FILE_BEGIN);
        Modules = new ModuleEntry[nExtModules];
        do {
            ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
            if(!nBytesRead)
                break;
            if(Buf2ModRec(RecBuf,ModRecordBuf) == TRUE)
            {
                AddNewModuleNode(ModRecordBuf, (ID)i);
                i++;
            }
        } while( nBytesRead );

    }
    CloseHandle(hFile);

    strcpy(szFName,szPath);
    strcat(szFName,"begoals.db");
    hFile = CreateFile(szFName,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);

    if(hFile == INVALID_HANDLE_VALUE)
    	return FALSE;

    nRecSize = GOAL_REC_SIZE;
    //load BeGoals.db
    do {
        ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
        if(!nBytesRead)
            break;
        Buf2GoalRec(RecBuf,GoalRecordBuf);
        if(AddNewGoalNode(GoalRecordBuf) == FALSE)
        {
        	FreeLibLists();
            CloseHandle(hFile);
        	return FALSE;
        }
    } while( nBytesRead );
    CloseHandle(hFile);

    //load BeAct.db
    strcpy(szFName,szPath);
    strcat(szFName,"beact.db");
    hFile = CreateFile(szFName,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    	return FALSE;
    nRecSize = ACT_REC_SIZE;
    do {
        ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
        if(!nBytesRead)
            break;
        Buf2ActRec(RecBuf,ActRecordBuf);
        if(AddNewActNode(ActRecordBuf,DefActionFn) == FALSE)
        {
        	FreeLibLists();
            CloseHandle(hFile);
        	return FALSE;
        }

    } while( nBytesRead );
    AddDummyActNode();
    CloseHandle(hFile);

    //load BeStr.db
    strcpy(szFName,szPath);
    strcat(szFName,"bestr.db");
    hFile = CreateFile(szFName,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    	return FALSE;
    //do not specify sizeof(StrFileRec): usually memory is WORD aligned
    nRecSize = sizeof(unsigned short) + 16 + 51 + 3*sizeof(unsigned char)
        + 100 * (sizeof(unsigned short) + 3*sizeof(unsigned char)) + 1;
    do {
        ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
        if(!nBytesRead)
            break;
        Buf2StrRec(RecBuf,StrRecordBuf);
        if(AddNewStrNode(StrRecordBuf) == FALSE)
        {
        	FreeLibLists();
            CloseHandle(hFile);
        	return FALSE;
        }
    } while( nBytesRead );
    CloseHandle(hFile);

    //load BeMinds.db
    strcpy(szFName,szPath);
    strcat(szFName,"beminds.db");
    hFile = CreateFile(szFName,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    	return TRUE;    //because it is not essential

    nRecSize = MND_REC_SIZE;
    do {
        ReadFile(hFile,&RecBuf,nRecSize,&nBytesRead,NULL);
        if(!nBytesRead)
            break;
        Buf2MindRec(RecBuf,MindRecordBuf);
        if(AddNewMindNode(MindRecordBuf) == FALSE)
        {
        	FreeLibLists();
            CloseHandle(hFile);
        	return FALSE;
        }
    } while( nBytesRead );
    CloseHandle(hFile);

    return TRUE;
}

//***************************************************************************
//FreeLibLists
//********** Description: **************
//free memory taken by the linked lists
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL FreeLibLists()
{
     GoalDefNodeP gp, gtmp;
     ActionDefNodeP ap, atmp;
     StrategyDefNodeP sp, stmp;
     MindDefNodeP   mp, mtmp;
     long i;
     FreeDynamicTACAssumptionPopulation();
     ClearHistory(); 
     for(i = 0; i < nExtModules; i++)
        if(Modules[i].hMod != NULL)
        //deallocate memory taken by these mapped modules
        //(or "decrement count", as the MicroSoft manual says)
            FreeLibrary(Modules[i].hMod);

     if (GoalListHead != NULL)
     {
        for( gp = GoalListHead; gp != NULL ; )
        {
            gtmp = gp->next;
            delete gp;
        	gp = gtmp;
        }
        GoalListHead = NULL;
     }

     if (ActionListHead != NULL)
     {
        for( ap = ActionListHead; ap != NULL ; )
        {
	       	atmp = ap->next;
            delete ap;
	        ap = atmp;
    	}
        ActionListHead = NULL;
     }

     if (StrategyListHead != NULL)
     {
        for( sp = StrategyListHead; sp != NULL ; )
    	{
	       	stmp = sp->next;
            if(sp->ActionIDs)
    		    delete []sp->ActionIDs;
            if(sp->TACs)
                delete []sp->TACs;
            if(sp->Verbal1Ind)
                delete []sp->Verbal1Ind;
            if(sp->Verbal2Ind)
                delete []sp->Verbal2Ind;
            if(sp->Verbal1)
                delete []sp->Verbal1;
            if(sp->Verbal2)
                delete []sp->Verbal2;
            if(sp->Verbal1TACs)
                delete []sp->Verbal1TACs;
            if(sp->Verbal2TACs)
                delete []sp->Verbal2TACs;
	        delete sp;
            sp = stmp;
        }
        StrategyListHead = NULL;
     }

     if (MindListHead != NULL)
     {
        for( mp = MindListHead; mp != NULL ; )
        {
	       	mtmp = mp->next;
            delete mp;
	        mp = mtmp;
    	}
        MindListHead = NULL;
     }

     delete []Modules;
     Modules = NULL;
}

//***************************************************************************
//CreateAction
//********** Description: **************
//instantiate Action by the attributes contained in the proper definition node
//********** Parameters:  **************
//ID argActionID - Action record ID
//-----------------------------------------------------------------------------
Action * BBECALL CreateAction(ID argActionID)
{
	ActionDefNodeP ActionDef;
 	Action *NewAction;

    ActionDef = GetActionNode(argActionID);
    if (ActionDef == NULL)
    //for compatibility with "dummy action mode" used in group strategies
        ActionDef = GetActionNode(SAME_ACTION);
    if (ActionDef == NULL)
        return NULL;

    NewAction = (Action*)GlobalAlloc(GPTR,sizeof(Action));
    if(NewAction == NULL)
        return NULL;

    NewAction->Init(ActionDef->_Type, ActionDef->Difficulty,
        ActionDef->Wickedness, ActionDef->Anarchy, ActionDef->MinIntel,
        ActionDef->ReqCourage, ActionDef->AttitudeChange,
        argActionID, ActionDef->MinAttitude, ActionDef->MaxAttitude,
        ActionDef->ActionFn);

    return NewAction;
}

//***************************************************************************
//ChangeAction
//********** Description: **************
//convert an existing Action according to the given definition ID
//********** Parameters:  **************
//Action* OldActionP - Action to change
//ID argActionID     - Action record ID
//-----------------------------------------------------------------------------
BOOL BBECALL ChangeAction(Action* OldActionP, ID argActionID)
{
	ActionDefNodeP ActionDef;

    ActionDef = GetActionNode(argActionID);
    if (ActionDef == NULL)
    //for compatibility with "dummy action mode" used in group strategies
        ActionDef = GetActionNode(SAME_ACTION);
    if (ActionDef == NULL)
        return FALSE;
    OldActionP->Init(ActionDef->_Type, ActionDef->Difficulty,
        ActionDef->Wickedness, ActionDef->Anarchy, ActionDef->MinIntel,
        ActionDef->ReqCourage, ActionDef->AttitudeChange, ActionDef->ActionID,
        ActionDef->MinAttitude, ActionDef->MaxAttitude, ActionDef->ActionFn);

    return TRUE;
}

//***************************************************************************
//CreateStrategy
//********** Description: **************
//create strategy skeleton, without targets yet, either by the given definition
//node or its ID
//********** Parameters:  **************
//StrategyDefNodeP argNode - definition node
//ID argStrategyID         - Strategy record ID
//-----------------------------------------------------------------------------
Strategy* BBECALL CreateStrategy(StrategyDefNodeP argNode, ID argStrategyID)
{
    StrategyDefNodeP StrategyN;
    Action *NewAction, *v1 = NULL, *v2 = NULL;
    Strategy *NewStrategy;
    HGLOBAL hActionP;
    ID i;
    BOOL Ended = FALSE;

    if (argNode == NULL)
    	StrategyN = GetStrategyNode(argStrategyID);
    else
    	StrategyN = argNode;

    if (StrategyN == NULL)
    	return NULL;

    NewStrategy = new Strategy;

    if (NewStrategy == NULL)
    	return NULL;

    NewStrategy->Init(StrategyN->TheGoal,
    	StrategyN->StrategyID);

    NewAction = CreateAction( StrategyN->ActionIDs[0] );
    if (NewAction == NULL)
    {
        delete NewStrategy;
        return NULL;
    }

    NewAction->SetTargetAssignmentCriteria(StrategyN->TACs[0]);
    NewAction->SetRefTAC(StrategyN->ReferencedTACs[0]);
    NewAction->SetFailureGoto(StrategyN->ConditionalGoto[0]);
    if(StrategyN->Verbal1Ind[ 0 ] < StrategyN->ActionsNo)
    {   //are there reported actions (1)?.. If yes, attach ones.
        v1 = CreateAction( StrategyN->Verbal1[ StrategyN->Verbal1Ind[0] ] );
        if( v1 != NULL )
        {
            v1->SetTargetAssignmentCriteria(StrategyN->Verbal1TACs[ StrategyN->Verbal1Ind[0] ]);
            v1->SetRefTAC(StrategyN->v1ReferencedTACs[ StrategyN->Verbal1Ind[0] ]);
            v1->SetFailureGoto(StrategyN->v1ConditionalGoto[ StrategyN->Verbal1Ind[0] ]);
            NewAction->SetReported1(v1);
        }
    }

    if(StrategyN->Verbal2Ind[ 0 ] < StrategyN->ActionsNo)
    {   //are there reported actions (2)?.. If yes, attach ones.
        v2 = CreateAction( StrategyN->Verbal2[ StrategyN->Verbal2Ind[0] ] );
        if( v2 != NULL )
        {
            v2->SetTargetAssignmentCriteria(StrategyN->Verbal2TACs[ StrategyN->Verbal2Ind[0] ]);
            v2->SetRefTAC(StrategyN->v2ReferencedTACs[ StrategyN->Verbal2Ind[0] ]);
            v2->SetFailureGoto(StrategyN->v2ConditionalGoto[ StrategyN->Verbal2Ind[0] ]);
            NewAction->SetReported2(v2);
        }
    }

    if(NewStrategy->AppendAction( NewAction )
    	== FALSE)
    {
        NewStrategy->FreeActionList();
        delete NewStrategy;
        return NULL;
    }


    //add actions to strategy
    for( i = 1; i < StrategyN->ActionsNo ; i++)
    {
    	if( ChangeAction(NewAction, StrategyN->ActionIDs[i]) == FALSE)
        	Ended = TRUE;
        if(!Ended)
        {
            NewAction->SetTargetAssignmentCriteria(StrategyN->TACs[i]);
            NewAction->SetRefTAC(StrategyN->ReferencedTACs[i]);
            NewAction->SetFailureGoto(StrategyN->ConditionalGoto[i]);
            if(StrategyN->Verbal1Ind[ i ] < StrategyN->ActionsNo)
            {   //are there reported actions (1)?.. If yes, attach ones.
                if(v1)
                    ChangeAction( v1, StrategyN->Verbal1[ StrategyN->Verbal1Ind[i] ] );
                else
                    v1 = CreateAction( StrategyN->Verbal1[ StrategyN->Verbal1Ind[i] ] );
                v1->SetTargetAssignmentCriteria(StrategyN->Verbal1TACs[ StrategyN->Verbal1Ind[i] ]);
                v1->SetRefTAC(StrategyN->v1ReferencedTACs[ StrategyN->Verbal1Ind[i] ]);
                v1->SetFailureGoto(StrategyN->v1ConditionalGoto[ StrategyN->Verbal1Ind[i] ]);
                NewAction->SetReported1(v1);
            }
            if(StrategyN->Verbal2Ind[ i ] < StrategyN->ActionsNo)
            {   //are there reported actions (2)?.. If yes, attach ones.
                if(v2)
                    ChangeAction( v2, StrategyN->Verbal2[ StrategyN->Verbal2Ind[i] ] );
                else
                    v2 = CreateAction( StrategyN->Verbal2[ StrategyN->Verbal2Ind[i] ] );
                v2->SetTargetAssignmentCriteria(StrategyN->Verbal2TACs[ StrategyN->Verbal2Ind[i] ]);
                v2->SetRefTAC(StrategyN->v2ReferencedTACs[ StrategyN->Verbal2Ind[i] ]);
                v2->SetFailureGoto(StrategyN->v2ConditionalGoto[ StrategyN->Verbal2Ind[i] ]);
                NewAction->SetReported2(v2);
            }

    		if (NewStrategy->AppendAction( NewAction ) == FALSE)
        		Ended = TRUE;
        }
        else
            break;
    }

    hActionP = GlobalHandle(NewAction);
    if(hActionP != NULL)
    {
        GlobalUnlock(hActionP);
        GlobalFree(hActionP);
    }
    else
    {
        GlobalFree((HGLOBAL)NewAction);
    }

    if(Ended)
    {
        NewStrategy->FreeActionList();
        delete NewStrategy;
        return NULL;
    }
    else
    	return NewStrategy;
}

//***************************************************************************
//LieCheck
//********** Description: **************
// A universal lie check. It is executed in two cases:
// 1. Before composing a statement. To do this, the Initiator must have a
// certain opinion about the Primary Objective NPC. That is, the pointer
// to the latter is saved and then replaced by a Core struct composed
// according to the Initiator's Intelligence or whatever. Then, the lie check
// is done. After that, the original scores are restored
//
// 2. When doing a statement. Here the check determines if the Primary
// Objective believes the told information.
//
//(the stupid function is probably unneeded anymore, as the concept changed
//but this concept is elegant too, so I just can't dispose of it:)).
//********** Parameters:  **************
//Action *ReportedAction - Action to check
//Core *Initiator        - pointer to initiator's Core
//Core *Objective        - pointer to objective's Core
//Core *ObjectiveItem    - pointer to objective item
//-----------------------------------------------------------------------------
BOOL BBECALL LieCheck(Action *ReportedAction, Core *Initiator,
	Core *Objective, ItemScores *ObjectiveItem)
{
	if (ReportedAction == NULL)
    	return FALSE;
    Core SavInitiator, SavObjective;
    SavInitiator = *Initiator;
    SavObjective = *Objective;

    CalcOpinion(Initiator, Objective, ObjectiveItem);

    percentage raMinIntel,raReqCourage,raActualWickedness,raActualAnarchy;
	Core *raInitiator;
    ItemScores *raObjectiveItem;
	ReportedAction->GetActualScores(raReqCourage,raActualWickedness,raActualAnarchy,raReqCourage);
	ReportedAction->GetSolidScores(raMinIntel,raReqCourage);
    raInitiator = ReportedAction->GetInitiator();
	raObjectiveItem = ReportedAction->GetObjItem();
    //check reported NPC's parameters vs. Objective Intelligence
    if ( raMinIntel >= raInitiator->Intelligence[0]
       || raInitiator->Courage < raReqCourage
       || !APPROX(raInitiator->Wickedness, raActualWickedness)
       || !APPROX(raInitiator->Anarchy, raActualAnarchy)
        )
    {
   		*Objective = SavObjective;
   		*Initiator = SavInitiator;
    	return FALSE;
    }

   	if (ObjectiveItem != NULL)
     //check reported item's parameters vs. Objective Intelligence
       	if ( !APPROX(ObjectiveItem->Meanness, raObjectiveItem->Meanness)
       || ForDiff(ObjectiveItem->Foreignness, raObjectiveItem->Foreignness) > 9
       || !APPROX(ObjectiveItem->Wealth, raObjectiveItem->Wealth)
       || !APPROX(ObjectiveItem->Beauty, raObjectiveItem->Beauty)
       || !APPROX(ObjectiveItem->Wickedness, raObjectiveItem->Wickedness)
       || !APPROX(ObjectiveItem->Anarchy, raObjectiveItem->Anarchy)
        )
        {
                *Objective = SavObjective;
                *Initiator = SavInitiator;
       		return FALSE;
        }

   	*Objective = SavObjective;
    *Initiator = SavInitiator;
    return TRUE;
}

//***************************************************************************
//CalcOpinion
//********** Description: **************
// Calculates imaginary Core structure by the evaluator's attributes
// Important: Save original scores before executing this function!!!
// BTW: Evaluator's Intelligence can be altered according to the game
// rules (classes, proficiencies, etc.)
//********** Parameters:  **************
//Core *Evaluator       - pointer to evaluator's Core
//Core *Objective       - pointer to objective's Core
//Core *ObjectiveItem   - pointer to objective item
//unsigned char UseTrue - bitmap containing the info which scores to use
//-----------------------------------------------------------------------------
void BBECALL CalcOpinion(Core *Evaluator, Core *Objective, ItemScores *ObjectiveItem, unsigned char UseTrue)
{
    percentage prcMaxError;
    prcMaxError = (percentage)ERROR_PERCENT(Evaluator->Intelligence[1]);
    unsigned char UseTrueArr[3];
    float fError;

    UseTrueArr[0] = (unsigned char)(1 & UseTrue);//Meanness - seemed or true
    UseTrueArr[1] = (unsigned char)(2 & UseTrue);//Wealth
    UseTrueArr[2] = (unsigned char)(4 & UseTrue);//Intelligence

    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
	Objective->Meanness[ UseTrueArr[0] ] +=
        ((float)Objective->Meanness[ UseTrueArr[0] ] * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
	Objective->Foreignness +=
        ((float)Objective->Foreignness * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
    Objective->Wealth[ UseTrueArr[1] ] +=
        ((float)Objective->Wealth[ UseTrueArr[1] ] * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
    Objective->Intelligence[ UseTrueArr[2] ] +=
        ((float)Objective->Intelligence[ UseTrueArr[2] ] * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
	Objective->Beauty +=
        ((float)Objective->Beauty * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
	Objective->Courage +=
        ((float)Objective->Courage * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
	Objective->Wickedness +=
        ((float)Objective->Wickedness * fError );
    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
    Objective->Anarchy +=
        ((float)Objective->Anarchy * fError );
    if( ObjectiveItem != NULL)
    {
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Meanness += ((float)ObjectiveItem->Meanness * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Foreignness += ((float)ObjectiveItem->Foreignness * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Wealth += ((float)ObjectiveItem->Wealth * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Beauty += ((float)ObjectiveItem->Beauty * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Wickedness += ((float)ObjectiveItem->Wickedness * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Anarchy += ((float)ObjectiveItem->Anarchy * fError );
	    fError = (float)Random(-prcMaxError,prcMaxError) / 100;
		ObjectiveItem->Influence += ((float)ObjectiveItem->Influence * fError );
    }
}

//***************************************************************************
//PersonalEvaluate
//********** Description: **************
// used to obtain the actual value of an action,
// including moral considerations, difficulty, etc., from the
// evaluator's point of view. Do not confuse this function with
// CalcActualScores() which plays similar but different role:
// getting actual scores for Initiator and Objective. Evaluator is
// not necessarily one of these.
// Of course, the function can be used to evaluate not only actions,
// but also possessions. In fact, if the Action Objective Item pointer is
// not a NULL, the item will be evaluated and all the rest ignored
//********** Parameters:  **************
//Action& Evaled       - evaluated Action
//Core &Evaluator      - evaluator's Core
//---------------------------------------------------------------------------
percentage BBECALL PersonalEvaluate(Action& Evaled, Core &Evaluator)
{
    percentage BasePts, AdditPts, ResPts, ActualDiff, ActualWick,
    	ActualAnar, ActualAttChg, Attitude2Evaled;
    long BigResPts;
    ItemScores *EvItem;
    Core *Objective,*Initiator;

    EvItem = Evaled.GetObjItem();
    Initiator = Evaled.GetInitiator();

    if(EvItem != NULL)
    {
        BasePts = EvItem->Wealth;
        AdditPts = ATT2ITEM2WEA(ItemPersonalEval(Evaluator, *EvItem ));
        ResPts = (percentage)(BasePts + AdditPts);
        if(ResPts < 0)
            ResPts = 0;
        if(Initiator != NULL)
        //if evaluator passes the item, the value should be negative
            if(Evaluator.attId == Initiator->attId)
                ResPts = (percentage)-ResPts;
    }
    else
    {
        Objective = Evaled.GetObjective();
        if(Objective == NULL)
            return 0;
        Evaled.GetActualScores(ActualDiff, ActualWick,
        	ActualAnar, ActualAttChg);
        if(Evaluator.attId == Objective->attId)
            Attitude2Evaled = 100;
        else
        {
            Attitude2Evaled = GetAttitude(Objective->attId,
                Evaluator.attPersonal,
    // it is TRUE attitude we need - one wouldn't lie to oneself :-)
                TRUE);
            if(Attitude2Evaled == ERROR_PERCENTAGE)
                Attitude2Evaled = 0;
        }

        BigResPts = (float)ActualAttChg
            * ABS(Evaluator.AttitudeChangeUnit)
            * Attitude2Evaled / 1000;

        if(Initiator != NULL)
        //if evaluator is going to accomplish the action,
        // the difficulty does matter. Otherwise..?
            if(Evaluator.attId == Initiator->attId)
                BigResPts -= ActualDiff;
        ResPts = VALID_PERCENTAGE(BigResPts);
    }
    return ResPts;
}

//***************************************************************************
//PersonalAttitude2Action
//********** Description: **************
//calculate personal attitude to the evaluated action. Do not confuse with
//PersonalEvaluate
//********** Parameters:  **************
//Action& Evaled  - evaluated Action
//Core &Evaluator - evaluator's Core
//BOOL bSetAtt    - whether to modify evaluator's attitude
//-----------------------------------------------------------------------------
long BBECALL PersonalAttitude2Action(Action& Evaled, Core &Evaluator,
            BOOL bSetAtt)
{
    percentage ActualDiff, ActualWick,
    	ActualAnar, ActualAttChg, Attitude2Evaled;
    short ResPts;
    Core *Objective,*Initiator;

    Objective = Evaled.GetObjective();
    Initiator = Evaled.GetInitiator();

    if(Objective == NULL || Initiator == NULL)
        return 0;
    Evaled.GetActualScores(ActualDiff, ActualWick,
        ActualAnar, ActualAttChg);
    if(Evaluator.attId == Objective->attId)
        Attitude2Evaled = 100;
    else
    {
        Attitude2Evaled = GetAttitude(Objective->attId,
                Evaluator.attPersonal,
    // it is TRUE attitude we need - one wouldn't lie to oneself :-)
                TRUE);
        if(Attitude2Evaled == ERROR_PERCENTAGE)
            Attitude2Evaled = 0;
    }

    //this should solve the 3rd party attitude problem
    ResPts = (short)(ActualAttChg * Evaluator.AttitudeChangeUnit
        * Attitude2Evaled/100);

    if(bSetAtt)//modify attitude if needed
    {
        ModifyAttitude(Initiator->attId,ResPts,Evaluator.attPersonal,BOTH);
    }

    return ResPts;
}

//-----------------------------------------------------------------------------
//---------------------- Action methods ---------------------------------------
//-----------------------------------------------------------------------------
BBECALL Action::Action()
{
    ResetTargets();
    ResetScores(); SetActionFn(NULL); IsPrimed = FALSE;
    Reported1 = NULL;   Reported2 = NULL;   //ActualLocation = NULL;
    SkipChecks = FALSE;
}

//-----------------------------------------------------------------------------
BBECALL Action::Action(Action *a)
{
    ResetTargets();
    ResetScores(); SetActionFn(NULL); IsPrimed = FALSE;
    Reported1 = NULL;   Reported2 = NULL;   //ActualLocation = NULL;
    SkipChecks = FALSE;
	Init(a);
}

//***************************************************************************
//Action::Action(ActionType,percentage,percentage,percentage,percentage,percentage,
//percentage,ID,percentage,percentage, int(BBECALL *)(void*))
//********** Description: **************
//construct Action by the given values - all but Actual
//********** Parameters:  **************
//ActionType argType                - action type
//percentage BasicDiffArg           - basic difficulty value
//percentage BasicWickArg           - basic Wickedness value
//percentage BasicAnarArg           - basic Anarchy value
//percentage MinIntArg              - minimum required intelligence
//percentage ReqCouArg              - minimum required courage
//percentage BasicAttitudeChangeArg - basic attitude change, in ACUs
//ID ActionIDArg                    - action definition ID
//percentage MinAttArg              - minimum allowed attitude
//percentage MaxAttArg              - maximum allowed attitude
//int (BBECALL *ActionFnArg)(void*) - Action handler function
//-----------------------------------------------------------------------------
BBECALL Action::Action(ActionType aType, percentage BasicDiffArg, percentage BasicWickArg,
    	percentage BasicAnarArg, percentage MinIntArg, percentage ReqCouArg,
        percentage BasicAttitudeChangeArg,  ID ActionIDArg, percentage MinAttArg,
        percentage MaxAttArg, int (BBECALL *ActionFnArg)(void*))
{
    ResetTargets();
    ResetScores(); SetActionFn(NULL); IsPrimed = FALSE;
    Reported1 = NULL;   Reported2 = NULL;   //ActualLocation = NULL;
    SkipChecks = FALSE;
	Init(aType, BasicDiffArg, BasicWickArg,
    	BasicAnarArg, MinIntArg, ReqCouArg,
        BasicAttitudeChangeArg, ActionIDArg,
        MinAttArg, MaxAttArg, ActionFnArg);
}

//-----------------------------------------------------------------------------
BBECALL Action::~Action()
{
    if(this == NULL)
        return;
	if(Reported1 != NULL)
    {
    	delete Reported1;
        Reported1 = NULL;
    }

	if(Reported2 != NULL)
    {
        delete Reported2;
        Reported2 = NULL;
    }
}

//***************************************************************************
//Action::Init
//********** Description: **************
//set Action scores by the given values - all but Actual
//********** Parameters:  **************
//ActionType argType                - action type
//percentage BasicDiffArg           - basic difficulty value
//percentage BasicWickArg           - basic Wickedness value
//percentage BasicAnarArg           - basic Anarchy value
//percentage MinIntArg              - minimum required intelligence
//percentage ReqCouArg              - minimum required courage
//percentage BasicAttitudeChangeArg - basic attitude change, in ACUs
//ID ActionIDArg                    - action definition ID
//percentage MinAttArg              - minimum allowed attitude
//percentage MaxAttArg              - maximum allowed attitude
//int (BBECALL *ActionFnArg)(void*) - Action handler function
//-----------------------------------------------------------------------------
void BBECALL Action::Init(ActionType argType, percentage BasicDiffArg, percentage BasicWickArg,
    	percentage BasicAnarArg, percentage MinIntArg, percentage ReqCouArg,
        percentage BasicAttitudeChangeArg, ID ActionIDArg,
        percentage MinAttArg, percentage MaxAttArg, int (BBECALL *ActionFnArg)(void*) )
{
    IsPrimed = FALSE;
//  byLieCheckActive = FALSE;
    InitScores(argType, BasicDiffArg, BasicWickArg, BasicAnarArg, MinIntArg,
    	ReqCouArg, BasicAttitudeChangeArg, MinAttArg, MaxAttArg);
    ActionID = ActionIDArg;
    ActionFn = ActionFnArg;
}

//***************************************************************************
//Action::SetActionFn
//********** Description: **************
//set Action function handler
//********** Parameters:  **************
//int (*fpArg)(void*) - Action function handler
//-----------------------------------------------------------------------------
void BBECALL Action::SetActionFn(int (BBECALL *fpArg)(void*))
{ ActionFn = fpArg; }

//***************************************************************************
//Action::SetTargetAssignmentCriteria
//********** Description: **************
//set Target Assignment Criteria definition ID
//********** Parameters:  **************
//unsigned char argTAC - Target Assignment Criteria definition ID
//-----------------------------------------------------------------------------
void BBECALL Action::SetTargetAssignmentCriteria(unsigned char argTAC)
{ TargetAssignmentCriteria = argTAC; }

//***************************************************************************
//Action::GetTargetAssignmentCriteria
//********** Description: **************
//return Target Assignment Criteria definition ID
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
unsigned char BBECALL Action::GetTargetAssignmentCriteria()
{ return TargetAssignmentCriteria; }

//***************************************************************************
//Action::SetRefTAC
//********** Description: **************
//set referenced Target Assignment Criteria (TAC redirected to another action node)
//********** Parameters:  **************
//unsigned char argRefTAC - referenced Target Assignment Criteria order no.
//-----------------------------------------------------------------------------
void BBECALL Action::SetRefTAC(unsigned char argRefTAC)
{ RefTAC = argRefTAC; }

//***************************************************************************
//Action::GetRefTAC
//********** Description: **************
//return referenced Target Assignment Criteria
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
unsigned char BBECALL Action::GetRefTAC()
{ return RefTAC; }

//***************************************************************************
//Action::SetFailureGoto
//********** Description: **************
//set action node order to branch on failure
//********** Parameters:  **************
//unsigned char argFailureGoto - action node order to branch on failure
//-----------------------------------------------------------------------------
void BBECALL Action::SetFailureGoto(unsigned char argFailureGoto)
{ FailureGoto = argFailureGoto; }

//***************************************************************************
//Action::GetFailureGoto
//********** Description: **************
//return action node order to branch on failure
//********** Parameters:  **************
//non
//-----------------------------------------------------------------------------
unsigned char BBECALL Action::GetFailureGoto()
{ return FailureGoto; }

//***************************************************************************
//Action::GetInitiator
//********** Description: **************
//return pointer to initiator's Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Core *BBECALL Action::GetInitiator()
{ return Initiator; }

//***************************************************************************
//Action::GetObjective
//********** Description: **************
//return pointer to objective's Core
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Core *BBECALL Action::GetObjective()
{ return Objective; }

//***************************************************************************
//Action::GetObjItem
//********** Description: **************
//return pointer to objective item
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ItemScores *BBECALL Action::GetObjItem()
{ return ObjectiveItem; }

//***************************************************************************
//Action::GetReported1Ptr
//********** Description: **************
//return pointer to Reported1 Action
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Action *BBECALL Action::GetReported1Ptr()
{ return Reported1; }

//***************************************************************************
//Action::GetReported2Ptr
//********** Description: **************
//return pointer to Reported2 Action
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Action *BBECALL Action::GetReported2Ptr()
{ return Reported2; }

//***************************************************************************
//Action::GetType
//********** Description: **************
//return the Action::_Type attribute
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ActionType BBECALL Action::GetType()
{ return _Type; }

//***************************************************************************
//Action::IsReady
//********** Description: **************
//return whether the action's needed attributes to be executed are primed
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
BOOL BBECALL Action::IsReady()
{ return IsPrimed; }

//***************************************************************************
//Action::GetID
//********** Description: **************
//return the Action's definition ID
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ID BBECALL Action::GetID()
{ return ActionID; }

//***************************************************************************
//Action::GetDealValue
//********** Description: **************
//return the overall Action's value in Wealth points from the Initiator's
//point of view
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
percentage BBECALL Action::GetDealValue()
{
    if(((Reported1 == NULL) && (Reported2 == NULL))
        || (Initiator == NULL) || (Objective == NULL))
    {
        PostError(INVALID_PROP);
        return 0;
    }

    if(ObjectiveItem != NULL)
        return (percentage)(ObjectiveItem->Wealth + ATT2ITEM2WEA(ItemPersonalEval(*Initiator, *ObjectiveItem )));

    if(Reported2 != NULL)
        return PersonalEvaluate(*Reported2, *Initiator);
    else
        return PersonalEvaluate(*Reported1, *Initiator);
}

//***************************************************************************
//Action::InitTargets
//********** Description: **************
//Set Initiator, Objective and ObjectiveItem pointers. If a parameter omitted
//or NULL is given, the attribute is not set. If Initiator and Objective aren't
//NULLs, Actual attributes are recalculated and the Action considered ready to
//be executed
//********** Parameters:  **************
//Core *argInitiator  - pointer to Initiator's Core
//Core *argPrimary    - pointer to Objective's Core
//ItemScores *argItem - pointer to Objective Item
//-----------------------------------------------------------------------------
void BBECALL Action::InitTargets(Core *argInitiator, Core *argPrimary,
    ItemScores *argItem)
{
    if (argInitiator != NULL)
		Initiator = argInitiator;

	if (argPrimary != NULL)
        Objective = argPrimary;

    if (argItem != NULL)
    	ObjectiveItem = argItem;
    if (Initiator != NULL && Objective != NULL
      && BasicWickedness != ERROR_PERCENTAGE)
    {
        CalcActualScores();
        IsPrimed = TRUE;
    }
    else
        IsPrimed = FALSE;
}

//***************************************************************************
//Action::ResetTargets
//********** Description: **************
//reset all targets to NULL. The Action is considered not ready to be executed
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Action::ResetTargets()
{
    Initiator = NULL;
    Objective = NULL;
    ObjectiveItem = NULL;
    IsPrimed = FALSE;
}

//***************************************************************************
//Action::SetReported1
//********** Description: **************
//copy the given Action and make Reported1 point to it
//********** Parameters:  **************
//Action *ActionPtr - pointer to Action to set as Reported1
//-----------------------------------------------------------------------------
void BBECALL Action::SetReported1(Action *ActionPtr)
{
	if(Reported1 != NULL)
   {
      delete Reported1;
      Reported1 = NULL;
   }

   if(ActionPtr)
      Reported1 = new Action(ActionPtr);
}

//***************************************************************************
//Action::SetReported2
//********** Description: **************
//copy the given Action and make Reported2 point to it
//********** Parameters:  **************
//Action *ActionPtr - pointer to Action to set as Reported2
//-----------------------------------------------------------------------------
void BBECALL Action::SetReported2(Action *ActionPtr)
{
	if(Reported2 != NULL)
    {
    	delete Reported2;
        Reported2 = NULL;
    }

    if(ActionPtr)
	    Reported2 = new Action(ActionPtr);
}

//***************************************************************************
//Action::Init(Action *)
//********** Description: **************
//initialize the current instance with the given Action
//********** Parameters:  **************
//Action *a - pointer to Action to initialize with
//-----------------------------------------------------------------------------
void BBECALL Action::Init(Action *a)
{
    if(a == NULL)
        return;
	Initiator = a->Initiator;
    Objective = a->Objective;
    ObjectiveItem = a->ObjectiveItem;
	InitScores(a->_Type, a->BasicDifficulty, a->BasicWickedness, a->BasicAnarchy,
	    a->MinIntel, a->ReqCourage, a->BasicAttitudeChange,
        a->MinAttitude, a->MaxAttitude);
    ActionFn = a->ActionFn;
    IsPrimed = a->IsPrimed;
    ActionID = a->ActionID;
    SkipChecks = a->SkipChecks;
    SetTargetAssignmentCriteria(a->TargetAssignmentCriteria);
    SetRefTAC(a->RefTAC);
    SetFailureGoto(a->FailureGoto);
    if(a->Reported1 != NULL)
    {
        SetReported1(a->Reported1);
    }
    if(a->Reported2 != NULL)
    {
        SetReported2(a->Reported2);
    }

    CalcActualScores();
}

//***************************************************************************
//Action::InitScores
//********** Description: **************
//initialize the current instance with the given set of attributes
//********** Parameters:  **************
//ActionType argType                - action type
//percentage BasicDiffArg           - basic difficulty value
//percentage BasicWickArg           - basic Wickedness value
//percentage BasicAnarArg           - basic Anarchy value
//percentage MinIntArg              - minimum required intelligence
//percentage ReqCouArg              - minimum required courage
//percentage BasicAttitudeChangeArg - basic attitude change, in ACUs
//percentage MinAttArg              - minimum allowed attitude
//percentage MaxAttArg              - maximum allowed attitude
//-----------------------------------------------------------------------------
void BBECALL Action::InitScores(ActionType aType, percentage BasicDiffArg, percentage BasicWickArg,
      percentage BasicAnarArg, percentage MinIntArg, percentage ReqCouArg,
      percentage BasicAttitudeChangeArg, percentage MinAttArg, percentage MaxAttArg)
{
	if(aType == VERBAL || aType == COMMERCIAL || aType == MECHANIC ||
    	    aType == VITAL)
    	_Type = aType;
    else
    	_Type = UNDEFINED;

    BasicDifficulty = BasicDiffArg;
    BasicWickedness = BasicWickArg;
    BasicAnarchy = BasicAnarArg;
    MinIntel = MinIntArg;
    ReqCourage = ReqCouArg;
    BasicAttitudeChange = BasicAttitudeChangeArg;
    MinAttitude = MinAttArg;
    MaxAttitude = MaxAttArg;

    if (Initiator != NULL
    	    && BasicWickedness != ERROR_PERCENTAGE && _Type != UNDEFINED)
    {
         CalcActualScores();
         IsPrimed = TRUE;
    }
}

//***************************************************************************
//Action::Execute
//********** Description: **************
//try to execute the Action, or simulate the attempt of execution (user-defined
//Action handler will not be called in this case)
//********** Parameters:  **************
//void* arg                      - parameters to the user-defined Action handler
//BOOL CheckMode                 - whether to execute in simulation ("check") mode
//unsigned char UseTrue          - bitmap containing the info which scores to use
//percentage NeutralSideMeanness - sum of the neutral side meanness
//-----------------------------------------------------------------------------
ActionError BBECALL Action::Execute(void* arg, BOOL CheckMode, unsigned char UseTrue,
        percentage NeutralSideMeanness)
{
    unsigned char UseTrueArr[3];
    int nRetByFn = 1;
    percentage MoralCheck;
    Core SavInitiator = *Initiator;
    percentage Attitude2Obj, SuddenChangeRoll;
    UseTrueArr[0] = (unsigned char)(1 & UseTrue);//Meanness
    UseTrueArr[1] = (unsigned char)(2 & UseTrue);//Wealth
    UseTrueArr[2] = (unsigned char)(4 & UseTrue);//Intelligence


	if (IsPrimed == FALSE || Initiator == NULL)
    {
        PostError(INVALID_PROP);
    	return ACTION_NOT_PRIMED;
    }


    if(!SkipChecks)
    {
        if(CheckMode && UseHistory)
        {
        //check history: whether the action was accomplished before
        //and failed
            if(History.IsEventRegistered(Initiator->attId,ActionID,
                    Objective->attId,ITEM_ITEMID(ObjectiveItem),
                    ACTION_FAILURE))
                return PAST_FAILURE;
        }

        if(MinIntel > Initiator->Intelligence[1])
            return LO_INT;

        if( (ForeignnessCheck() < _Type)
        //"What a strange person. I can't figure him out.
        //  I'd better leave him alone."
            && (_Type != VITAL))//vital actions: questions of life and death
    	    return TOO_FOREIGN;

        //attitude limits check
        Attitude2Obj = GetAttitude(Objective->attId,
            Initiator->attPersonal,TRUE);
        if(Attitude2Obj == ERROR_PERCENTAGE)
            Attitude2Obj = 0;
        if(Attitude2Obj > MaxAttitude)
        //"I can't do him such a thing. I like him too much..."
            return HI_ATT;
        if(Attitude2Obj < MinAttitude)
        {
        //"I can't do it. I hate him too much..."
            return LO_ATT;
        }

        MoralCheck = OutOfLimits(ActualWickedness,Initiator->Wickedness);

        if(UseSuddenChange)
            SuddenChangeRoll = (percentage)Random(0,100); //sudden change roll
        else
            SuddenChangeRoll = 100;

        if(MoralCheck != 0
                    && SuddenChangeRoll > Initiator->SuddenChange)
        {
            if( MoralCheck == 1)
                return HI_WICK;
            else
                if( MoralCheck == -1 )
                    return LO_WICK;
        }

        MoralCheck = OutOfLimits(ActualAnarchy,Initiator->Anarchy);
        if(MoralCheck != 0
                    && SuddenChangeRoll > Initiator->SuddenChange)
        {
            if( MoralCheck == 1 )
                return HI_ANA;
            else
                if( MoralCheck == -1 )
                    return LO_ANA;
        }

        if(_Type == COMMERCIAL)
        {
            if(WealthCheck(UseTrueArr[1]) == FALSE)
            {
                 //"He's too rich, he wouldn't want to trade with me."
    	        return NOT_INTERESTED_IN_DEAL;
            }
        }

        if((_Type == VITAL) && (ActualAttitudeChange < 0))
    	    if( MeannessCheck(FALSE,UseTrueArr[0],NeutralSideMeanness) == FALSE )
          //"I don't think we could beat 'em..."
             return BAD_COMBAT_ODDS;
        else
    	    if( MeannessCheck(TRUE,UseTrueArr[0],NeutralSideMeanness) == FALSE )
         //"He's too scary even to talk to... Did you see his fists?"
        	    return LO_COU;
    }

    if (!CheckMode)
    {
        UpdateObjective();
        Initiator->LastCommittedAction = ActionID;

        if(ActionFn == NULL)
        {
#ifndef ACTION_DEBUG_MODE
            return ACTION_NOT_PRIMED;  //   test purposes
#else
   	        return ACTION_SUCCESS;
#endif
        }
        else
    	    nRetByFn = (*ActionFn)(arg);
    }
    else        //that is, if CheckMode
    {
        *Initiator = SavInitiator;
        //restore original scores
        // only if in CheckMode
    }

    //if the user-defined function returns a non-zero value, it means
    //success
    if(nRetByFn)
        return ACTION_SUCCESS;
    else
        return ACTION_FAILURE;
}

//***************************************************************************
//Action::UpdateObjective
//********** Description: **************
//update Objective's attributes, according to the current action
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Action::UpdateObjective()
{
    Objective->LastTargettedInAction = ActionID;
    Objective->LastAccessedBy = Initiator;
    if(ActualAttitudeChange > 0)
        Objective->CurrentEnvironment = FRIENDLY_ENV;
    else
        if(ActualAttitudeChange < 0)
            Objective->CurrentEnvironment = HOSTILE_ENV;
        else
            Objective->CurrentEnvironment = NEUTRAL_ENV;
}

//***************************************************************************
//Action::MeannessCheck
//********** Description: **************
//check if the Initiator has enough Courage and CombatOverall to execute the
//Action
//********** Parameters:  **************
//BOOL ForceCourageCheck         - whether Courage check override is on
//BOOL CheckSolidScores          - whether to check solid scores
//percentage NeutralSideMeanness - summary Meanness of everybody who is uninvolved
//-----------------------------------------------------------------------------
BOOL BBECALL Action::MeannessCheck(BOOL ForceCourageCheck, BOOL CheckSolidScores,
        percentage NeutralSideMeanness)
{
	percentage MeannessBalance, EffectiveCourage;
	if( Initiator->Intelligence[1] <= AVG_INT )
	//the initiator doesn't consider the balance of sides
    	ForceCourageCheck = TRUE;

    if (CheckSolidScores)
	    MeannessBalance = (percentage)(Initiator->Meanness[1] -
    		Objective->Meanness[1]);
    else
	    MeannessBalance = (percentage)(Initiator->Meanness[1] -
    		Objective->Meanness[0]);

        //complete calculating meanness balance
    MeannessBalance = (percentage)(MeannessBalance
            - NeutralSideMeanness/2);

    if( !ForceCourageCheck && (MeannessBalance < 0) )
    	return FALSE;

    EffectiveCourage = (percentage)(Initiator->Courage + MeannessBalance);
    if( EffectiveCourage < ReqCourage)
	    return FALSE;
    else
    {
    	Initiator->Courage = EffectiveCourage;
        return TRUE;
    }

}

//***************************************************************************
//Action::ForeignnessCheck
//********** Description: **************
//check if the difference in the SocialGroups allows the action to be executed
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ActionType BBECALL Action::ForeignnessCheck()
{
    ActionType AllowedMaxType;
    unsigned char ForeignDiff;
    ForeignDiff = ForDiff(Initiator->Foreignness,
        Objective->Foreignness);
    if(ForeignDiff == 0)
        ForeignDiff = 1;
    AllowedMaxType = (ActionType)(Initiator->Intelligence[1]/ForeignDiff);
    if(!AllowedMaxType || (AllowedMaxType > VITAL))
        AllowedMaxType = VITAL;
	return (ActionType)AllowedMaxType;
}

//***************************************************************************
//Action::WealthCheck
//********** Description: **************
//check if the objective
//********** Parameters:  **************
//BOOL UseWhich - whether to use true Wealth points, or seemed ones
//-----------------------------------------------------------------------------
BOOL BBECALL Action::WealthCheck(BOOL UseWhich)
{
	//evaluate deal first
    if(Objective == NULL)
        return TRUE;    //let's not put obstacles on the user's way

    if (GetDealValue() < (Objective->Wealth[UseWhich] / WORTHY_PART))
    //"C'mon. It doesn't worth messing with. I am rich enough to ignore
    //that kind of proposals."
	    return FALSE;
    else
	    return TRUE;
}

//***************************************************************************
//Action::ResetScores
//********** Description: **************
//reset basic scores
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Action::ResetScores()
{
    _Type = UNDEFINED;
    BasicDifficulty     = ERROR_PERCENTAGE;
    BasicWickedness     = ERROR_PERCENTAGE;
    BasicAnarchy        = ERROR_PERCENTAGE;
    MinIntel            = ERROR_PERCENTAGE;
    ReqCourage          = ERROR_PERCENTAGE;
    BasicAttitudeChange = ERROR_PERCENTAGE;
}

//***************************************************************************
//Action::CalcActualScores
//********** Description: **************
//calculate actual values (wickedness, anarchy, etc.)
//according to the basic ones and the attitude to the subject
//********** Parameters:  **************
//BOOL UseExternalAttitude    - whether to use seemed (external) attitude;
//                              if not, true attitude is used
//percentage AvgLocWickedness - average wickedness in the current location
//percentage AvgLocAnarchy    - average anarchy in the current location
//-----------------------------------------------------------------------------
void BBECALL Action::CalcActualScores(BOOL UseExternalAttitude,percentage AvgLocWickedness,
        percentage AvgLocAnarchy)
{
    percentage AttitudeToObj;

    PostError(0);
    if(Objective == NULL ||
            Initiator == NULL)
    {
        PostError(INVALID_PROP);
        return;
    }

    //nothing to calculate. An important safety pin
    AttitudeToObj = GetAttitude(Objective->attId,
	    Initiator->attPersonal, UseExternalAttitude);

    //we don't want significant modifications here
    if(AttitudeToObj == ERROR_PERCENTAGE)
        AttitudeToObj = 0;
    if(AttitudeToObj  > LARGE_DIFF)
        AttitudeToObj = LARGE_DIFF;
    if(AttitudeToObj  < -LARGE_DIFF)
        AttitudeToObj = -LARGE_DIFF;
    if(BasicWickedness)
    {
        ActualWickedness = (percentage)(BasicWickedness
            * (1 - SOCIAL_INFLUENCE)
            + AvgLocWickedness*SOCIAL_INFLUENCE
            + AttitudeToObj);
    }
    else
    //neutral action is uninfluenced by any factor
        ActualWickedness = BasicWickedness;

    if(ActualWickedness > 100)
        ActualWickedness = 100;
    if(ActualWickedness < -100)
        ActualWickedness = -100;
    if(BasicWickedness != 0)
        if(ActualWickedness / BasicWickedness < 0)
	        ActualWickedness = 0;	//we don't want to cross the border

    if(BasicAnarchy)
        ActualAnarchy = (percentage)(BasicAnarchy*(1-SOCIAL_INFLUENCE)
                + AvgLocAnarchy*SOCIAL_INFLUENCE);
    else
        BasicAnarchy = ActualAnarchy;

    if(BasicAnarchy != 0)
        if(ActualAnarchy/BasicAnarchy < 0)
	        ActualWickedness = 0;	//we don't want to cross the border

	ActualDifficulty = (percentage)(BasicDifficulty +
	    ABS(ActualWickedness - Initiator->Wickedness)/10 +
		ABS(ActualAnarchy - Initiator->Anarchy)/10);
    if(_Type == VITAL && BasicAttitudeChange)
        ActualDifficulty +=
            (percentage)(Objective->Meanness[1] - Initiator->Meanness[1]);

    ActualAttitudeChange =  // for now it's the same
        (percentage)(BasicAttitudeChange);
}

//***************************************************************************
//Action::GetActualScores
//********** Description: **************
//return actual scores via reference variables
//********** Parameters:  **************
//percentage& ActualDiffArg - reference to actual difficulty holder
//percentage& ActualWickArg - reference to actual wickedness holder
//percentage& ActualAnarArg - reference to actual anarchy holder
//percentage& ActualAttChg  - reference to actual attitude change holder
//-----------------------------------------------------------------------------
void BBECALL Action::GetActualScores(percentage& ActualDiffArg, percentage& ActualWickArg,
    	percentage& ActualAnarArg, percentage& ActualAttChg)
{
	ActualDiffArg = ActualDifficulty;
    ActualWickArg = ActualWickedness;
    ActualAnarArg = ActualAnarchy;
    ActualAttChg = ActualAttitudeChange;
}

//***************************************************************************
//Action::GetBasicScores
//********** Description: **************
//return basic scores via reference variables
//********** Parameters:  **************
//percentage& BasicDiffArg - reference to basic difficulty holder
//percentage& BasicWickArg - reference to basic wickedness holder
//percentage& BasicAnarArg - reference to basic anarchy holder
//percentage& BasicAttChg  - reference to basic attitude change holder
//-----------------------------------------------------------------------------
void BBECALL Action::GetBasicScores(percentage& BasicDiffArg, percentage& BasicWickArg,
    	percentage& BasicAnarArg, percentage& BasicAttChg)
{
	BasicDiffArg = BasicDifficulty;
    BasicWickArg = BasicWickedness;
    BasicAnarArg = BasicAnarchy;
    BasicAttChg = BasicAttitudeChange;
}

//***************************************************************************
//Action::GetSolidScores
//********** Description: **************
//return "solid" scores (intelligence + courage) via reference variables
//********** Parameters:  **************
//percentage& MinIntArg - reference to intelligence holder
//percentage& ReqCouArg - reference to required courage holder
//-----------------------------------------------------------------------------
void BBECALL Action::GetSolidScores(percentage& MinIntArg,percentage& ReqCouArg)
{
    MinIntArg = MinIntel;
    ReqCouArg = ReqCourage;
}

//***************************************************************************
//Action::GetAttitudeLimits
//********** Description: **************
//return attitude limits via reference variables
//********** Parameters:  **************
//percentage& MinAttArg - reference to minimal attitude holder
//percentage& MaxAttArg - reference to maximal attitude holder
//-----------------------------------------------------------------------------
void BBECALL Action::GetAttitudeLimits(percentage& MinAttArg, percentage& MaxAttArg)
{
    MinAttArg = MinAttitude;
    MaxAttArg = MaxAttitude;
}

//***************************************************************************
//Action::FillSaveBuffer
//********** Description: **************
//encodes itself to the save buffer
//********** Parameters:  **************
//BYTE* SaveBuffer - pointer to the save buffer
//-----------------------------------------------------------------------------
unsigned short BBECALL Action::FillSaveBuffer(BYTE* SaveBuffer)
{
    unsigned short nAllocated = 0;
    if(this == NULL || SaveBuffer == NULL)
        return 0; //Do nothing. Let someone else GPF.
    Action SelfCopy;
    ID InitID = 0, ObjID = 0, ItemID = 0, ItemOwner = 0;

    SelfCopy = *this;
    if(Initiator != NULL)
        InitID = Initiator->attId;
    if(Objective != NULL)
        ObjID = Objective->attId;
    if(ObjectiveItem != NULL)
    {
        ItemID = ObjectiveItem->ItemID;
        ItemOwner = ObjectiveItem->Owner;
    }

    SelfCopy.Initiator = (Core*)InitID;
    SelfCopy.Objective = (Core*)ObjID;
    SelfCopy.ObjectiveItem = (ItemScores*)ItemID;
    SelfCopy.SetActionFn((int(BBECALL *)(void*))(ItemOwner));

    memcpy(SaveBuffer,&SelfCopy,sizeof(Action));
    nAllocated += (unsigned short)sizeof(Action);
    SelfCopy.Reported1 = NULL;
    SelfCopy.Reported2 = NULL;
    if(Reported1 != NULL)
    {
        Reported1->FillSaveBuffer(SaveBuffer + nAllocated);
        nAllocated += (unsigned short)sizeof(Action);
    }

    if(Reported2 != NULL)
    {
        Reported2->FillSaveBuffer(SaveBuffer + nAllocated);
        nAllocated += (unsigned short)sizeof(Action);
    }

    return nAllocated;
}

//***************************************************************************
//Action::RestoreFromBuffer
//********** Description: **************
//overwrite the current instance with the info from the save buffer, restoring
//links from IDs
//********** Parameters:  **************
//BYTE* pbBuffer            - pointer to the save buffer
//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 Action::RestoreFromBuffer(BYTE *pbBuffer, Core **pbspPopulation,
        unsigned short nPopSize,ItemPtrArr* ipapAllItems,
        unsigned short *nInvSizes)
{
    ActionDefNodeP adpActDef;
    unsigned short nCopied = 0;

    if(pbBuffer == NULL)
        return;
    if(Reported1 != NULL)
        delete Reported1;//so the memory won't get wasted
    if(Reported2 != NULL)
        delete Reported2;//so the memory won't get wasted
    memcpy(this,pbBuffer,sizeof(Action));
    nCopied += (unsigned short)sizeof(Action);
    //now restore the whole pointers bunch...(ActionFn holds inventory owner)
    InitTargets(FindBSAddress((ID)Initiator,pbspPopulation,nPopSize),
         FindBSAddress((ID)Objective,pbspPopulation,nPopSize),
         FindItemAddress((ID)ObjectiveItem,(ID)ActionFn,
            nPopSize,ipapAllItems,nInvSizes));

    //now get the function pointer
    adpActDef = GetActionNode(ActionID);
    if(adpActDef != NULL)
        ActionFn = adpActDef->ActionFn;
    else
        ActionFn = NULL;

    if(Reported1 != NULL)
    {
        Reported1 = new Action;
        Reported1->RestoreFromBuffer(pbBuffer + nCopied,
            pbspPopulation,nPopSize,ipapAllItems,nInvSizes);
        nCopied += (unsigned short)sizeof(Action);
    }
    if(Reported2 != NULL)
    {
        Reported2 = new Action;
        Reported2->RestoreFromBuffer(pbBuffer + nCopied,
            pbspPopulation,nPopSize,ipapAllItems,nInvSizes);
    }
}

//-----------------------------------------------------------------------------
//---------------------- Strategy methods--------------------------------------
//-----------------------------------------------------------------------------
BBECALL Strategy::Strategy()
{
	Goal initGoal;
    StrategyID = 0;
    initGoal.Itself = GOAL_NONE;
    initGoal.Importance = 0;
    LastAction = NULL;
    Init(initGoal);
}

//-----------------------------------------------------------------------------
BBECALL Strategy::Strategy(Goal argGoal, ID argID)
{
    Init(argGoal,argID);
}

//-----------------------------------------------------------------------------
BBECALL Strategy::~Strategy()
{
    if(this == NULL)
        return;
    //intentionally, the action list is NOT deleted. Agent destructor does it.
}

//***************************************************************************
//Strategy::FreeActionList
//********** Description: **************
//deletes all action nodes in the action list and assigns NULL to ActionList
//links from IDs
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Strategy::FreeActionList()
{
	ActionNodeP ap, tmp;
    HGLOBAL     hActionP;
    NextAction = NULL;

    if( ActionList == NULL )
        return;

    ap = ActionList;
    while(ap != NULL)
    {
        tmp = ap->next;
        hActionP = GlobalHandle(ap);
        if(hActionP != NULL)
        {
            GlobalUnlock(hActionP);
            GlobalFree(hActionP);
        }
        else
            GlobalFree((HGLOBAL)ap);

        ap = tmp;
    }

    ActionList = NULL;
}

//***************************************************************************
//Strategy::AppendAction
//********** Description: **************
//append a new action to the end of the list (duplicating it)
//********** Parameters:  **************
//Action *actArg - pointer to the new Action
//-----------------------------------------------------------------------------
BOOL BBECALL Strategy::AppendAction(Action *actArg)
{
    ActionNodeP ap, NewNode;

//    NewNode = new struct _ActionNode;
    NewNode = (ActionNodeP)GlobalAlloc(GPTR,sizeof(struct _ActionNode));
    if ( NewNode == NULL ) //allocation failure
    	return FALSE;

    NewNode->action.Init(actArg);
    NewNode->next = NULL;	//new action node will be appended to the end


    if ( ActionList == NULL )
    {
        ActionList = NewNode;
    	//prime NextAction if the added action is first
    	NextAction = ActionList;
    }
    else
    {
	    for( ap = ActionList; ap->next != NULL ; ap = ap->next )
            /*just get to the end*/   ;

        //it is crucial to APPEND the new action to the end of list
        //-- Not to its beginning!
        ap->next = NewNode;
    }
    return TRUE;
}

//***************************************************************************
//Strategy::DelAction
//********** Description: **************
//delete the FIRST action in the list
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Action BBECALL Strategy::DelAction()
{
    ActionNodeP tmp;
    Action actRetVal;
    if(ActionList == NULL)
        return NULL;
    actRetVal = ActionList->action;
   	tmp = ActionList->next;
   	delete ActionList;
    ActionList = tmp;
    NextAction = ActionList;
    return actRetVal;
}

//***************************************************************************
//Strategy::FindAction
//********** Description: **************
//return pointer to the Action node of the given order, if such found. If not
//found, return NULL. Order count starts from 1.
//********** Parameters:  **************
//unsigned char actionOrder - node order
//-----------------------------------------------------------------------------
ActionNodeP BBECALL Strategy::FindAction(unsigned char actionOrder)
{
	ActionNodeP ap;
    unsigned char i = 1, base_action;

    if(IS_REF_TO_REP(actionOrder))
    {
        base_action = (unsigned char)GET_BASE_ACT_NUM(actionOrder);
	    for( ap = ActionList; ap != NULL ; ap = ap->next )
        {
    	    if (i == base_action)
            {
                if(IS_REP1(actionOrder))
                    if(ap->action.GetReported1Ptr())
        	            return ap;
                    else
        	            return NULL;
                else
                    if(ap->action.GetReported2Ptr())
        	            return ap;
                    else
        	            return NULL;
            }
            i++;
        }
    }
    else
	    for( ap = ActionList; ap != NULL ; ap = ap->next )
        {
    	    if (i == actionOrder)
        	    return ap;
            i++;
        }

    return NULL;
}

//***************************************************************************
//Strategy::GetNextActionOrder
//********** Description: **************
//return the order of the next Action node, starting from 1
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
percentage BBECALL Strategy::GetNextActionOrder()
{
    percentage i = 1;
    ActionNodeP ap;
    if(NextAction == NULL || ActionList == NULL)
        return 0;
    for( ap = ActionList; ap != NULL ; ap = ap->next )
    {
        if (ap == NextAction)
            return i;
        i++;
    }
    return 0;
}

//***************************************************************************
//Strategy::GetLastActionOrder
//********** Description: **************
//return the order of the last executed Action node, starting from 1
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
percentage BBECALL Strategy::GetLastActionOrder()
{
    percentage i = 1;
    ActionNodeP ap;
    if(LastAction == NULL || ActionList == NULL)
        return 0;
    for( ap = ActionList; ap != NULL ; ap = ap->next )
    {
        if (ap == LastAction)
            return i;
        i++;
    }
    return 0;
}

//***************************************************************************
//Strategy::Init(Goal)
//********** Description: **************
//initialize current instance by a Goal
//********** Parameters:  **************
//Goal goalArg - goal definition to initialize with
//-----------------------------------------------------------------------------
void BBECALL Strategy::Init(Goal goalArg)
{
    Objective = NULL;
    Item = NULL;
    ActionList = NULL;
    NextAction = NULL;
    TotalDifficulty = Importance = Wickedness = Anarchy = ERROR_PERCENTAGE;
    SetGoal( goalArg );
}

//***************************************************************************
//Strategy::Init(Goal,ID)
//********** Description: **************
//initialize current instance by a Goal and a strategy ID
//********** Parameters:  **************
//Goal goalArg - goal definition to initialize with
//ID argID     - strategy ID
//-----------------------------------------------------------------------------
void BBECALL Strategy::Init(Goal goalArg,ID argID)
{
    StrategyID = argID;
    LastAction = NULL;
    Init(goalArg);
}

//***************************************************************************
//Strategy::CalcScores
//********** Description: **************
//compute the total scores for the current strategy basing its calculations
//on the included actions
//********** Parameters:  **************
//percentage ImportanceModification
//-----------------------------------------------------------------------------
void BBECALL Strategy::CalcScores(percentage ImportanceModification)
{
    ActionNodeP ap;
    percentage aDifficulty, aWickedness, aAnarchy, aAttChange;
    unsigned short WickednessSum = 0, AnarchySum = 0, i = 0;
    TotalDifficulty = 0;
    Wickedness = ERROR_PERCENTAGE;
    Anarchy = ERROR_PERCENTAGE;
    for( ap = ActionList; ap != NULL ; ap = ap->next )
    {
    	ap->action.GetActualScores(aDifficulty, aWickedness, aAnarchy, aAttChange);
    	TotalDifficulty += aDifficulty;
		WickednessSum += aWickedness;
    	AnarchySum += aAnarchy;
        i++;
    }
    if (i > 0)
    {
		Wickedness = (percentage)(WickednessSum / i);
    	Anarchy = (percentage)(AnarchySum / i);
    }
    Importance = (percentage)(StrategyGoal.Importance + ImportanceModification);
}

//***************************************************************************
//Strategy::ExecuteNextAction
//********** Description: **************
//execute next scheduled action (not in simulation mode!)
//********** Parameters:  **************
//void *GenericArg               - parameter to the user-defined Action handler
//unsigned char UseWhich         - bitmap containing info on which attributes to use
//percentage NeutralSideMeanness - sum of the neutral side meanness
//-----------------------------------------------------------------------------
ActionError BBECALL Strategy::ExecuteNextAction(void *GenericArg,
        unsigned char UseWhich, percentage NeutralSideMeanness)
{
	ActionError RetVal;

    if ( StrategyGoal.Itself == GOAL_NONE )
    	return ACTION_NOT_PRIMED;

    if(NextAction == NULL)  //this was the last scheduled action
    {
    //Important: the strategy is ended this way in order to preserve
    // all the actions list until the next call to ExecuteNextAction()
        End();
        return ACTION_NOT_PRIMED;
    }

	if ( (RetVal = NextAction->action.Execute(GenericArg, FALSE,
            UseWhich, NeutralSideMeanness)) == ACTION_SUCCESS )
    {
        LastAction = NextAction;
        NextAction = NextAction->next;
        return RetVal; //Action executed successfully
    }
    else
        if(NextAction->action.GetFailureGoto() == END_STR_ON_SUCCESS)
        {
            LastAction = NextAction;
            NextAction = NextAction->next;
            return RetVal; //Action failed, but the conditional goto means
            //you must get on with strategy
        }
        else
    	    return RetVal;
}

//***************************************************************************
//Strategy::SkipNextAction
//********** Description: **************
//increment next scheduled action pointer without executing the Action
//********** Parameters:  **************
//BOOL ForceFailure - whether to simulate skipped Action failure for conditional
//                    branching
//-----------------------------------------------------------------------------
void BBECALL Strategy::SkipNextAction(BOOL ForceFailure)
{
    if(NextAction == NULL)
        return;
    LastAction = NextAction;
    if(ForceFailure && NextAction->action.GetFailureGoto())
        NextAction = FindAction(NextAction->action.GetFailureGoto());
    else
        NextAction = NextAction->next;
}

//***************************************************************************
//Strategy::ReturnToLastAction
//********** Description: **************
//get one action back in the list, if were any
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Strategy::ReturnToLastAction()
{
    if(LastAction != NULL)
        NextAction = LastAction;
}

//***************************************************************************
//Strategy::UpdateNextActionTargets
//********** Description: **************
//set targets to the next action
//********** Parameters:  **************
//Core *Initiator     - pointer to Initiator's Core
//Core *Primary       - pointer to Objective's Core
//ItemScores *argItem - pointer to Objective Item
//-----------------------------------------------------------------------------
void BBECALL Strategy::UpdateNextActionTargets(Core *Initiator,
	Core *Primary, ItemScores *argItem)
{
	NextAction->action.InitTargets(Initiator, Primary, argItem);
}

//***************************************************************************
//Strategy::PossibilityCheck
//********** Description: **************
//try to execute all the Actions in simulation ("check") mode, return the success
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
BOOL BBECALL Strategy::PossibilityCheck()
{
	ActionNodeP ap;
    ActionError ae;

	for( ap = ActionList; ap != NULL ; ap = ap->next )
    {
        ae = ap->action.Execute(NULL, TRUE, 0);
    //"execute" next action in check mode. The action function arguments
    //have no meaning here, so we give it NULL

		if ( ae != ACTION_SUCCESS )
        {
            if(ap->action.GetFailureGoto() == 0)
                return FALSE;
            //else, we can't judge for now
        }
    }

    return TRUE;
}

//***************************************************************************
//Strategy::End
//********** Description: **************
//end current Strategy - delete all the Actions and initialize attributes and
//pointers
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
void BBECALL Strategy::End()
{
    FreeActionList();
    NextAction = NULL; LastAction = NULL;
    Objective = NULL; Item = NULL;
    StrategyGoal.Itself = GOAL_NONE; StrategyGoal.Importance = 0;
    TotalDifficulty = 0; Wickedness = 0; Anarchy = 0; MaxIntel = 0;
    Importance = 0; //that is, actual importance
}

//***************************************************************************
//Strategy::GetNextAction
//********** Description: **************
//return pointer to the next scheduled Action - if none, return NULL
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Action *BBECALL Strategy::GetNextAction()
{
   if(NextAction == NULL)
      return NULL;
   return (StrategyGoal.Itself != GOAL_NONE ? &(NextAction->action) : NULL);
}

//***************************************************************************
//Strategy::GetNextAction
//********** Description: **************
//return pointer to the last executed Action - if none, return NULL
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Action *BBECALL Strategy::GetLastAction()
{
   if(LastAction == NULL)
      return NULL;
   return (StrategyGoal.Itself != GOAL_NONE ? &(LastAction->action) : NULL);
}

//***************************************************************************
//Strategy::SetNextActionAddress
//********** Description: **************
//set NextAction pointer to the given address
//********** Parameters:  **************
//ActionNodeP pAction - the address to set
//-----------------------------------------------------------------------------
void BBECALL Strategy::SetNextActionAddress(ActionNodeP pAction)
{ NextAction = pAction; }

//***************************************************************************
//Strategy::SetLastActionAddress
//********** Description: **************
//set LastAction pointer to the given address
//********** Parameters:  **************
//ActionNodeP pActionNode - the address to set
//-----------------------------------------------------------------------------
void BBECALL Strategy::SetLastActionAddress(ActionNodeP pActionNode)
{ LastAction = pActionNode; }

//***************************************************************************
//Strategy::GetActionList
//********** Description: **************
//return the pointer to the Actions list
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ActionNodeP BBECALL Strategy::GetActionList()
{ return ActionList; }

//***************************************************************************
//Strategy::SetGoal
//********** Description: **************
//set Strategy goal by the given goal
//********** Parameters:  **************
//Goal argGoal - goal to set
//-----------------------------------------------------------------------------
void BBECALL Strategy::SetGoal(Goal argGoal)
{
    StrategyGoal.Itself = argGoal.Itself;
    StrategyGoal.Importance = argGoal.Importance;
    Importance = StrategyGoal.Importance;
}

//***************************************************************************
//Strategy::GetGoal
//********** Description: **************
//return the Strategy goal
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Goal BBECALL Strategy::GetGoal()
{ return StrategyGoal; }

//***************************************************************************
//Strategy::SetObjective
//********** Description: **************
//set Strategy Objective
//********** Parameters:  **************
//Core *argObj - pointer to the Objective's Core
//-----------------------------------------------------------------------------
void BBECALL Strategy::SetObjective(Core *argObj)
{
    Objective = argObj;
}

//***************************************************************************
//Strategy::GetObjective
//********** Description: **************
//return Strategy Objective
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
Core *BBECALL Strategy::GetObjective()
{ return Objective; }

//***************************************************************************
//Strategy::SetObjItem
//********** Description: **************
//set Strategy Objective Item
//********** Parameters:  **************
//ItemScores *argItem - pointer to the Objective Item
//-----------------------------------------------------------------------------
void BBECALL Strategy::SetObjItem(ItemScores *argItem)
{ Item = argItem; }

//***************************************************************************
//Strategy::GetObjItem
//********** Description: **************
//return Strategy Objective Item
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ItemScores *BBECALL Strategy::GetObjItem()
{ return Item; }

//***************************************************************************
//Strategy::GetImportance
//********** Description: **************
//get Strategy importance
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
percentage BBECALL Strategy::GetImportance()
{ return Importance; }

//***************************************************************************
//Strategy::IsListed
//********** Description: **************
//return whether the given ID is already appears in the Strategy as objective, or
//any of the sides in any Action
//********** Parameters:  **************
//ID argId - ID to check
//-----------------------------------------------------------------------------
BOOL BBECALL Strategy::IsListed(ID argId)
{
    ActionNodeP ap;
    Core *actBs;
    actBs = GetObjective();
    if(actBs)
        if(actBs->attId == argId)
            return TRUE;
    for(ap = ActionList; ap != NULL; ap = ap->next)
    {
        actBs = ap->action.GetObjective();
        if(actBs)
            if(actBs->attId == argId)
                return TRUE;
        actBs = ap->action.GetInitiator();
        if(actBs)
            if(actBs->attId == argId)
                return TRUE;
    }
    return FALSE;
}

//***************************************************************************
//Strategy::GetID
//********** Description: **************
//return Strategy definition ID
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------------
ID BBECALL Strategy::GetID()
{ return StrategyID; }

//***************************************************************************
//Strategy::FillSaveBuffer
//********** Description: **************
//encode the current instance to te given buffer, then encode the Action list
//********** Parameters:  **************
//BYTE* SaveBuffer - buffer to fill
//-----------------------------------------------------------------------------
unsigned short BBECALL Strategy::FillSaveBuffer(BYTE* SaveBuffer)
{
    unsigned short nAllocated = 0;
    ActionNodeP ap;
    if(this == NULL || SaveBuffer == NULL)
        return 0; //Do nothing. Let someone else GPF.
    Strategy SelfCopy;
    ID ObjID = 0, ItemID = 0, ItemOwner = 0;

    if(Objective == NULL && Item == NULL && ActionList == NULL)
    {
        return 0;
    }

    SelfCopy = *this;
    if(Objective != NULL)
        ObjID = Objective->attId;
    if(Item != NULL)
    {
        ItemID = Item->ItemID;
        ItemOwner = Item->Owner;
    }
    SelfCopy.SetNextActionAddress((ActionNodeP)GetNextActionOrder());
    SelfCopy.SetLastActionAddress((ActionNodeP)GetLastActionOrder());

    SelfCopy.SetObjective((Core*)ObjID);
    SelfCopy.SetObjItem((ItemScores*)ItemID);
    SelfCopy.OverrideActionList((ActionNodeP)ItemOwner);

    memcpy(SaveBuffer,&SelfCopy,sizeof(Strategy));
    SelfCopy.OverrideActionList(NULL); //precaution from deleting
                                        //actions list
    nAllocated += (unsigned short)sizeof(Strategy);

    for(ap = ActionList; ap != NULL; ap = ap->next)
    {
        nAllocated += ap->action.FillSaveBuffer(SaveBuffer + nAllocated);
    }

    return nAllocated;
}

//***************************************************************************
//Strategy::RestoreFromBuffer
//********** Description: **************
//overwrite the current instance with the info from the save buffer, restoring
//links from IDs
//********** Parameters:  **************
//BYTE* pbBuffer            - pointer to the save buffer
//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 Strategy::RestoreFromBuffer(BYTE *pbBuffer, Core **pbspPopulation,
        unsigned short nPopSize,
        ItemPtrArr *ipapAllItems,
        unsigned short *nInvSizes)
{
    Strategy *RawStr;
    ActionNodeP ap;
    int nCurrentOffset;
    if(pbBuffer == NULL)
        return;

    memcpy(this,pbBuffer,sizeof(Strategy));
    SetObjective(FindBSAddress((ID)Objective,pbspPopulation,nPopSize));
    SetObjItem(FindItemAddress((ID)Item,
            (ID)ActionList,     //which holds item owner
            nPopSize,ipapAllItems,nInvSizes));

    RawStr = CreateStrategy(NULL,StrategyID);
    if(RawStr == NULL)
    {
        ActionList = NULL;
        NextAction = NULL;
        LastAction = NULL;
        return;
    }
    ActionList = RawStr->GetActionList();
    delete RawStr; //we don't need it anymore

    nCurrentOffset = sizeof(Strategy);
    //strategy is restored. Now restore actions, one-by-one
    for(ap = ActionList; ap != NULL; ap = ap->next)
    {
        ap->action.RestoreFromBuffer(pbBuffer + nCurrentOffset,
                pbspPopulation,nPopSize,ipapAllItems,nInvSizes);
        nCurrentOffset += sizeof(Action);
        if(ap->action.GetReported1Ptr() != NULL)
            nCurrentOffset += sizeof(Action);
        if(ap->action.GetReported2Ptr() != NULL)
            nCurrentOffset += sizeof(Action);
    }

    NextAction = FindAction((unsigned char)NextAction);
    LastAction = FindAction((unsigned char)LastAction);
}

//***************************************************************************
//Strategy::OverrideActionList
//********** Description: **************
//set pointer to Action list, without freeing the latter
//********** Parameters:  **************
//ActionNodeP ActListArg - new Action list pointer
//-----------------------------------------------------------------------------
void BBECALL Strategy::OverrideActionList(ActionNodeP ActListArg)
{ ActionList = ActListArg; }

//***************************************************************************
//Strategy::ResetAllActionTargets
//********** Description: **************
//discard all the Action targets (but not strategy objectives!)
//********** Parameters:  **************
//none
//-----------------------------------------------------------------------
void BBECALL Strategy::ResetAllActionTargets()
{
	ActionNodeP ap;
    Action *Rep;

	for( ap = GetActionList(); ap != NULL ; ap = ap->next )
    {
            ap->action.ResetTargets();
            Rep = ap->action.GetReported1Ptr();
            if(Rep == NULL)
                continue;
            Rep->ResetTargets();

            Rep = ap->action.GetReported2Ptr();
            if(Rep == NULL)
                continue;
            Rep->ResetTargets();
    }
}

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