//---------------------------------------------------------------------------
#include "CharacterClass.h"
#include "CharacterUnit.h"
//---------------------------------------------------------------------------
#define VM_STATEMENT    1
#define VM_DEAL         2
#define VM_REQUEST      3
#define VM_TEASE        4
#define VM_THREAT       5
//------------------------
#define ATTACK           7
#define NONLETHAL_ATTACK 3

//---------------------------------------------------------------------------
extern CharacterNode CharListHead;
extern GroupNode     GroupListHead;
extern int step;
void lstPrint(char *argMsg);
extern char Msg1[120];

//---------------------------------------------------------------------------
InventoryItem::InventoryItem()
{ next = NULL; _Type = itMisc; BEStuff.Owner = 0; }

//---------------------------------------------------------------------------
InventoryItem::~InventoryItem()
{}

//---------------------------------------------------------------------------
void InventoryItem::SetBEScores(percentage argWick, percentage argAna, percentage
    	argMean, percentage argFor, percentage argWeal, percentage argBeauty,
        percentage argPers)

{
	BEStuff.Wickedness = argWick;
    BEStuff.Anarchy = argAna;
	BEStuff.Meanness = argMean;
    BEStuff.Foreignness = argFor;
	BEStuff.Wealth = argWeal;
    BEStuff.Beauty = argBeauty;
	BEStuff.Personality = argPers;
    switch(_Type){
        case itWeapon:	BEStuff.Influence = 20; //per cents
        	break;
        case itArmor:   BEStuff.Influence = 35;
        	break;
        case itReagent: BEStuff.Influence = 5;
        	break;
        case itGarment: BEStuff.Influence = 35;
        	break;
        default:		BEStuff.Influence = 15;
        	break;
    }
    BEStuff.CanBeDivided = FALSE;  //default setting
}

//---------------------------------------------------------------------------
//-------- Character class --------------------------------------------------
//---------------------------------------------------------------------------
Character::Character()
{
    InventoryHead = NULL;
    CharID = BEStuff.GetCoreAddress()->attId;
    IsActive = TRUE;
    IsPlayerCharacter = FALSE;
}
//++cAutoNumber; }

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

Character::Character(Core argBs)
{
	InventoryHead = NULL;
    //attId is taken from self-counting Core. No pre-Init required
    CharID = BEStuff.GetCoreAddress()->attId; //++cAutoNumber;
    IsActive = TRUE;
	Init(argBs);
}

//---------------------------------------------------------------------------
Character::~Character()
{}

//---------------------------------------------------------------------------
void Character::Init(Core argBs)
{
    SetBEScores(argBs);
}

//---------------------------------------------------------------------------
void Character::SetBEScores(Core argBs)
{
    //so-called Meanness is, in fact, combat overall. With this
    //simple example I'll demonstrate how you translate regular scores
    //to "Meanness". Of course, you are not obliged to use both
    //"seemed" and "true" scores, but, IMHO, it enhances role-playing
    //effect
	BEStuff.SetCore(argBs);

}

//---------------------------------------------------------------------------
InventoryItem *Character::GetItem(ID itID,InventoryItem **iiSav)
{
    return ::GetItem(InventoryHead,itID,iiSav);
}

//---------------------------------------------------------------------------
//AddItem
//merely attaches the item to the end
void Character::AddItem(InventoryItem *argItem)
{
	InventoryItem *iip;
    argItem->BEStuff.Owner = BEStuff.GetCoreAddress()->attId;
    if(InventoryHead == NULL)
    {
 	    argItem->next = NULL;
        InventoryHead = argItem;
        return;
    }
	for(iip = InventoryHead ; iip->next != NULL ; iip = iip->next);
    iip->next = argItem;
    argItem->next = NULL;
}

//---------------------------------------------------------------------------
void Character::ClearInventory()
{
	InventoryItem *iip, *savp;
	for(iip = InventoryHead ; iip != NULL ; )
    {
    	savp = iip->next;
        delete iip;
        iip = savp;
    }
    InventoryHead = NULL;
}

//---------------------------------------------------------------------------
InventoryItem *Character::GetItem(ItemScores *isArg,InventoryItem **iiSav)
{
	InventoryItem *iip;

    if(iiSav != NULL)   //save previous pointer
        (*iiSav) = InventoryHead;

	for(iip = InventoryHead ; iip != NULL ; iip = iip->next)
    {
        if(&(iip->BEStuff) == isArg)
            return iip;
        if(iiSav != NULL)   //save previous pointer
            (*iiSav) = iip;
    }

    return NULL;
}

//---------------------------------------------------------------------------
//warning: ItemArray must be freed after use
unsigned short Character::InvListToArray(ItemPtrArr *ItemArray)
{
	InventoryItem *iip;
    unsigned short i = 0, nItemQty;
    if(ItemArray != NULL)
    	delete [](*ItemArray);
    nItemQty = ItemsQty();
    if(nItemQty == 0)
    {   //Trust no one. Explicitly assign NULL
        *ItemArray = NULL;
        return NULL;
    }
    (*ItemArray) = new ItemPtr[nItemQty];
	for(iip = InventoryHead ; iip != NULL ; iip = iip->next )
    	(*ItemArray)[i++] = &(iip->BEStuff);
    return i;
}

//---------------------------------------------------------------------------
unsigned short Character::ItemsQty()
{
	InventoryItem *iip;
    unsigned short Qty = 0;
	for(iip = InventoryHead ; iip != NULL ; iip = iip->next )
		Qty++;
    return Qty;
}

//---------------------------------------------------------------------------
unsigned short Character::FillSaveBuffer(BYTE *SaveBuffer)
{
    unsigned short nAllocated = 0;
    InventoryItem *iip;

    if(SaveBuffer == NULL)
        return nAllocated;

    memcpy(SaveBuffer + nAllocated,&CharID,sizeof(ID));
    nAllocated += (unsigned short)sizeof(ID);
    memcpy(SaveBuffer + nAllocated,szName,MAX_NAME_LEN);
    nAllocated += (unsigned short)MAX_NAME_LEN;
    memcpy(SaveBuffer + nAllocated,&Cash,sizeof(double));
    nAllocated += (unsigned short)sizeof(double);
    memcpy(SaveBuffer + nAllocated,&IsActive,sizeof(BOOL));
    nAllocated += (unsigned short)sizeof(BOOL);
    memcpy(SaveBuffer + nAllocated,&IsPlayerCharacter,sizeof(BOOL));
    nAllocated += (unsigned short)sizeof(BOOL);

	for(iip = InventoryHead ; iip != NULL ; iip = iip->next)
    {
        memcpy(SaveBuffer + nAllocated,iip,sizeof(InventoryItem));
        nAllocated += (unsigned short)sizeof(InventoryItem);
    }

    memcpy(SaveBuffer + nAllocated,"INVEND",6);
    nAllocated += (unsigned short)6;
    nAllocated += (unsigned short)BEStuff.FillSaveBuffer(SaveBuffer + nAllocated);
    return nAllocated;
}

//---------------------------------------------------------------------------
void Character::RestoreFromBufferStep1(BYTE *SaveBuffer,
        unsigned short nLen)
{
    unsigned short nCopied = 0, nInvEnd;
    InventoryItem *LastAdded;

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

    memcpy(&CharID,SaveBuffer + nCopied,sizeof(ID));
    nCopied += (unsigned short)sizeof(ID);
    memcpy(szName,SaveBuffer + nCopied,MAX_NAME_LEN);
    nCopied += (unsigned short)MAX_NAME_LEN;
    memcpy(&Cash,SaveBuffer + nCopied,sizeof(double));
    nCopied += (unsigned short)sizeof(double);
    memcpy(&IsActive,SaveBuffer + nCopied,sizeof(BOOL));
    nCopied += (unsigned short)sizeof(BOOL);
    memcpy(&IsPlayerCharacter,SaveBuffer + nCopied,sizeof(BOOL));
    nCopied += (unsigned short)sizeof(BOOL);

    for(nInvEnd = nCopied; nInvEnd < (nLen-6); nInvEnd++)
        if(         SaveBuffer[nInvEnd]   == 'I'
                &&  SaveBuffer[nInvEnd+1] == 'N'
                &&  SaveBuffer[nInvEnd+2] == 'V'
                &&  SaveBuffer[nInvEnd+3] == 'E'
                &&  SaveBuffer[nInvEnd+4] == 'N'
                &&  SaveBuffer[nInvEnd+5] == 'D')
            break;

    InventoryHead = NULL;

    for(; (nCopied < nInvEnd) && (nCopied < nLen); nCopied += (unsigned short)sizeof(InventoryItem))
    {
        LastAdded = new InventoryItem;
        if(LastAdded == NULL)
            break;
        memcpy(LastAdded,SaveBuffer + nCopied,sizeof(InventoryItem));
        AddItem(LastAdded);
    }

    nCopied = (unsigned short)(nInvEnd + 6);
    BEStuff.RestoreFromBufferStep1(SaveBuffer + nCopied,
        (unsigned short)(nLen - nCopied));
}

//---------------------------------------------------------------------------
void Character::RestoreFromBufferStep2(BYTE *SaveBuffer,unsigned short nLen,
        Agent **pbspPopulation, unsigned short nPopSize,
        ItemPtrArr* ipapAllItems, unsigned short *npSizes)
{
    BEStuff.RestoreFromBufferStep2(SaveBuffer,nLen,
        pbspPopulation, nPopSize,ipapAllItems, npSizes);
}

//---------------------------------------------------------------------------
//EnumInventory
//except re-IDing inventory, prints verbose info
void Character::EnumInventory()
{
	InventoryItem *iip,*iipSav = InventoryHead,*ipMoney = NULL;
    char szText[150],szTypeDesc[20];
    unsigned short Qty = 0;

    lstPrint("**");
    sprintf(szText,"** %s's inventory",szName);
    lstPrint(szText);

	for(iip = InventoryHead ; iip != NULL ; )
    {
        if(strcmpi(iip->Descr,"money") == 0)
        {
            //consolidate money bits
            if(ipMoney == NULL)
            {
                ipMoney = iip;
                Cash = WealthPts2Cash(ipMoney->BEStuff.Wealth);
            }
            else
            {
                ipMoney->BEStuff.Wealth += iip->BEStuff.Wealth;
                Cash = WealthPts2Cash(ipMoney->BEStuff.Wealth);
                iipSav->next = iip->next;
                delete iip;
                iip = iipSav->next;
                continue;
            }
        }

	    iip->ItemID = ++Qty;
        iip->BEStuff.ItemID = Qty;
        iip->BEStuff.Owner = BEStuff.GetCore().attId;
        switch(iip->_Type)
        {
            case 0:
                strcpy(szTypeDesc,"misc. item");
                break;
            case 1:
                strcpy(szTypeDesc,"weapon");
                break;
            case 2:
                strcpy(szTypeDesc,"armor");
                break;
            case 3:
                strcpy(szTypeDesc,"garment");
                break;
            case 4:
                strcpy(szTypeDesc,"reagent");
                break;
        }
        sprintf(szText,"** -%d- %s (%s)",iip->BEStuff.ItemID,iip->Descr,szTypeDesc);
        lstPrint(szText);
        iipSav = iip;
        iip = iip->next;
    }

    if(InventoryHead == NULL)
    {
        lstPrint("** <empty>");
    }
}

//---------------------------------------------------------------------------
Character *GetCharacter(ID argCharID)
{
	CharacterNode cp;
	for(cp = CharListHead ; cp != NULL ; cp = cp->next)
    {
    	if(cp->ch.BEStuff.GetCore().attId == argCharID)
        {
        	return &(cp->ch);
        }
    }

	return NULL;//if nothing found
}

//---------------------------------------------------------------------------
Character *GetCharacter(int nIndex)
{
	CharacterNode cp;
    int i = 0;
	for(cp = CharListHead ; cp != NULL ; cp = cp->next)
    {
    	if(i == nIndex)
        	return &(cp->ch);
        i++;
    }

	return NULL;//if nothing found
}
//---------------------------------------------------------------------------
//SetCharacter
//doesn't fully overwrites the character - only his/her basic scores
//and name
Character *SetCharacter(Character &argChar)
{
	Character *cp;
    if((cp = GetCharacter((ID)argChar.BEStuff.GetCore().attId)) == NULL)
    {
    	return NULL;
    }
//    memcpy(cp,&argChar, sizeof(Character));
    cp->SetBEScores(argChar.BEStuff.GetCore() );
    cp->BEStuff.SetOriginalCore(argChar.BEStuff.GetCore());
    cp->IsActive = argChar.IsActive;
    cp->IsPlayerCharacter = argChar.IsPlayerCharacter;
    cp->InventoryHead = argChar.InventoryHead;

    strncpy(cp->szName,argChar.szName,MAX_NAME_LEN);
    cp->Cash = argChar.Cash;

    return cp;
}

//---------------------------------------------------------------------------
Character *AddCharacter(Character &argChar)
{
 	CharacterNode new_cn = new struct _CharacterNode, cn;
    Attitude *apNewRoot;
    if(new_cn == NULL)
    	return NULL;
    memcpy(&(new_cn->ch),&argChar, sizeof(Character));
//	new_cn->ch = argChar;
    apNewRoot = argChar.BEStuff.GetCoreAddress()->attPersonal;
    new_cn->ch.BEStuff.SetAttitudesRoot(CopyAttitudeTree(apNewRoot));
    if(CharListHead == NULL)
        CharListHead = new_cn;
    else
    {
        for(cn = CharListHead; cn->next != NULL; cn = cn->next)
            /*just get to the end*/;
        cn->next = new_cn;
    }
    new_cn->next = NULL;
    return &(new_cn->ch);
}

//---------------------------------------------------------------------------
BOOL DelCharacter(ID argCharID)
{
	CharacterNode cp, sav_cp;
    Core *ObjBS;
    BOOL WasDel = FALSE;

    sav_cp = CharListHead;
	for(cp = CharListHead ; cp != NULL ; cp = cp->next)
    {
    	if(cp->ch.CharID == argCharID)
        {
            if(cp == CharListHead)
                CharListHead = cp->next;
            else
                sav_cp->next = cp->next;
            delete cp;
            WasDel = TRUE;
            break;
        }
        sav_cp = cp;
    }

    //now scan for references to the dead
	for(cp = CharListHead ; cp != NULL ; cp = cp->next)
    {
        ObjBS = cp->ch.BEStuff.CurrentStrategy.GetObjective();
        if(ObjBS == NULL)
            continue;
    	if(ObjBS->attId == argCharID)
        {
        //"Tralala, that's all, folks!" - when objective is gone,
        //there is no strategy, at least, in this demo.
            cp->ch.BEStuff.CurrentStrategy.End();
        }
    }

	return WasDel;
}

//---------------------------------------------------------------------------
void FreeCharList()
{
	CharacterNode cp, savp;
    GroupNode gp,savgp;
//    Stop("Freeing characters list...");

    ClearHistory();
    for(gp = GroupListHead;gp != NULL; )
    {
    	savgp = gp->next;
        delete gp;
        gp = savgp;
    }
    GroupListHead = NULL;

    for(cp = CharListHead;cp != NULL; )
    {
    	savp = cp->next;
        if(cp->ch.InventoryHead != NULL)
            cp->ch.ClearInventory();
        cp->ch.BEStuff.CurrentStrategy.End();
        delete cp;
        cp = savp;
    }
    CharListHead = NULL;

    step = 0;
//    Stop("Freed.");
}

//---------------------------------------------------------------------------
Character *GetActionTarget(Action *argAct)
{

	CharacterNode cp;
    Core *bsObj;
    bsObj = argAct->GetObjective();
    if(bsObj == NULL)
        return NULL;
    for(cp = CharListHead;cp != NULL; cp = cp->next)
    {
        if(cp->ch.BEStuff.GetID() == bsObj->attId)
            return &(cp->ch);
    }

    return NULL;
}

//---------------------------------------------------------------------------
// warning: must be freed after use
unsigned short MakePopulationArray(Agent ***PopArr)
{
	CharacterNode cp;
    unsigned short nPopulation = 0, i = 0;
    for(cp = CharListHead;cp != NULL; cp = cp->next)
        nPopulation++;

    (*PopArr) = new Agent*[nPopulation];

    for(cp = CharListHead;cp != NULL; cp = cp->next)
    {
        (*PopArr)[i++] = &(cp->ch.BEStuff);
    }
    return nPopulation;
}
//---------------------------------------------------------------------------
percentage Price2WealthPts(double nPrice)
{
/*    if(nPrice*AVG_SCORE/AVG_PRICE > 100)
        return 100;
    else*/
        return (percentage)(nPrice*AVG_SCORE/AVG_PRICE);
}
//---------------------------------------------------------------------------
double WealthPts2Cash(percentage nPts)
{
    return (double)nPts*AVG_PRICE/AVG_SCORE;
}

//---------------------------------------------------------------------------
char *RandomName()
{
    unsigned char byIndex = (unsigned char)Random(1,38);
    switch(byIndex)
    {
        case 0:return "Mathusak";   case 1:return "Joshua";
        case 2:return "Pierre";     case 3:return "Ivan";
        case 4:return "Wilma";      case 5:return "Cain";
        case 6:return "Sherlock";   case 7:return "Socrates";
        case 8:return "Leonardo";   case 9:return "Abel";
        case 10:return "Viscount";  case 11:return "Vadim";
        case 13:return "Konstantin";case 14:return "Michael";
        case 15:return "Cerberus";  case 16:return "James";
        case 17:return "Gizmo";     case 18:return "Rotschild";
        case 19:return "Lion";      case 20:return "Seth";
        case 21:return "Don";       case 22:return "Luis";
        case 23:return "Diane";     case 24:return "Helen";
        case 25:return "Saul";      case 26:return "Mathew";
        case 27:return "John";      case 28:return "Jack";
        case 29:return "Diego";     case 30:return "Carlos";
        case 31:return "Hans";      case 32:return "Sarkhan";
        case 33:return "Peter";     case 34:return "George";
        case 35:return "Joel";      case 36:return "Ernest";
        default:return "Agasther";
    }
}

//---------------------------------------------------------------------------
BOOL ChoiceYN(char* szCaption,char* szTitle)
{
    if(MessageBox(NULL,szCaption,szTitle,MB_YESNO + MB_ICONQUESTION
            + MB_TASKMODAL )
        == IDYES)
        return true;
    else
        return false;
}

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//HandleExecution
//great name. But all this function must accomplish
// is print some info after action and activate response
void HandleExecution(Character *ch)
{
    Character *action_target;
    Action *pNextA;
    ActionDefNodeP pActDef;
    char szOutBuf[150],szActionName[25];

    if(ch == NULL)
        return;

    pNextA = ch->BEStuff.CurrentStrategy.GetNextAction();
    pActDef = GetActionNode(pNextA->GetID());
    if(pActDef != NULL)
        strncpy(szActionName,pActDef->Verb,MAX_ACT_NAME_LEN);
    else
        strcpy(szActionName,"");

    if(pNextA)
        action_target = GetActionTarget(pNextA);
    else
        action_target = NULL;

    if(action_target)
    {
        action_target->BEStuff.CalcResponse(*pNextA);
        if(action_target->BEStuff.IsDead())
            return;
        if(!action_target->IsActive)
            sprintf(szOutBuf,"Unconscious, %s thinks about %s:",
                action_target->szName,ch->szName);
        else
            sprintf(szOutBuf,"After %s's action, %s thinks about %s:",
                ch->szName,
                action_target->szName,ch->szName);
        lstPrint(szOutBuf);
        sprintf(szOutBuf,"\"%s\"",GetLastThought());
        lstPrint(szOutBuf);
    }
}

//---------------------------------------------------------------------------
int Leave(InteractionParam *act)
{
    char szText[150];

    if(act->Initiator == NULL)
        return 0;

    act->Initiator->BEStuff.Die();
    sprintf(szText,"%s quickly departs, leaving all the inventory behind",
        act->Initiator->szName);
    lstPrint(szText);
    return 1;
}

//---------------------------------------------------------------------------
int Attack(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;//,*Initiator;
    char szQuery[150];

    if(!CheckInteractionParam(act))
    {
        return 0;
    }
    else
        if(act->Squad != NULL)
        {
            //lstPrint("Invalid parameter(s)!");
            return 1;//and execute nothing more
        }

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        sprintf(szQuery,"Attack: objective #%d at %p wasn't found!",
            ObjBS->attId,ObjBS);
        lstPrint(szQuery);
        return 0;
    }
/*
    Initiator = GetCharacter(InitiBS->attId);
    if(Initiator == NULL)
        return 0;
*/
    if(ObjBS->Dead)
    {
        sprintf(szQuery,"%s attacks %s, but for some obscure reason the latter doesn't react.",
            act->Initiator->szName,
            Objective->szName);
        lstPrint(szQuery);
        return 1;
    }
    sprintf(szQuery,"%s attacks %s...",act->Initiator->szName,
        Objective->szName);
    lstPrint(szQuery);
    sprintf(szQuery,"%s attacks %s. Yes = %s wins, No = %s wins.",
        act->Initiator->szName,
        Objective->szName,
        act->Initiator->szName,
        Objective->szName);

    if(act->Initiator == Objective)
    {
        act->Initiator->BEStuff.Die();
        sprintf(szQuery,"%s commits suicide. Goodbye, cruel world!",
                Objective->szName);
        lstPrint(szQuery);
        return 1;
    }
    if(ChoiceYN(szQuery,"Lethal Attack"))
    {
        Objective->BEStuff.Die();
        sprintf(szQuery,"%s dies...",
                Objective->szName);
        lstPrint(szQuery);
        return 1;
    }
    else
    {
        act->Initiator->BEStuff.Die();
        sprintf(szQuery,"%s dies...",
                act->Initiator->szName);
        lstPrint(szQuery);
        //nothing to handle here. The objective is dead
        return 0;
    }

}

//---------------------------------------------------------------------------
int GroupAttack(InteractionParam *act)
{
    Core *ObjBS;
    Character *Objective;
    Agent *mem;
    char szText[150];
    unsigned short i,nMembQty;

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
        return 0;

    if(Objective->BEStuff.IsDead())
    {
        sprintf(szText,"%s planned to attack %s... but",
            act->Squad->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("now there's no point");
        return 1;
    }

    sprintf(szText,"%s attempt to kill %s...",
        act->Squad->szName,Objective->szName);
    lstPrint(szText);

    sprintf(szText,"%s attacks %s. Yes = %s wins, No = %s wins.",
        act->Squad->szName,
        Objective->szName,
        act->Initiator->szName,
        Objective->szName);
    if(ChoiceYN(szText,"Attack"))
    {
        Objective->BEStuff.Die();
        sprintf(szText,"%s dies",Objective->szName);
        lstPrint(szText);
    }
    else
    {
        act->Initiator->BEStuff.Die();
        sprintf(szText,"%s is killed. Everybody!!!",
                act->Squad->szName);
        lstPrint(szText);

        nMembQty = act->Squad->g.GetMembersQty();
        for(i = 0; i < nMembQty; i++)
        {
            mem = act->Squad->g.GetMember(i);
            if(mem != NULL)
                mem->Die();
        }
        act->Squad->g.Dismantle();
    }

    return 1;
}

//---------------------------------------------------------------------------
int NonLethalAttack(InteractionParam *act)
{
    Core *ObjBS;
    Character *Objective;
    char szText[150];

    if(!CheckInteractionParam(act))
        return 0;
    else
        if(act->Squad != NULL)
            return 1;//and execute nothing more

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        return 0;
    }
    if(!Objective->IsActive || Objective->BEStuff.IsDead())
    {
        sprintf(szText,"%s approaches %s with determination to kick ass... but",
            act->Initiator->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("there's no point now.");
        return 1;
    }

    sprintf(szText,"%s attempts to knock %s down...",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    sprintf(szText,"%s attempts to knock %s down. Yes = %s wins, No = %s wins.",
        act->Initiator->szName,Objective->szName,act->Initiator->szName,
        Objective->szName);
    if(ChoiceYN(szText,"Non-Lethal Attack"))
    {
        sprintf(szText,"%s falls unconscious",Objective->szName);
        lstPrint(szText);
        Objective->IsActive = FALSE;
    }
    else
    {
        sprintf(szText,"%s falls unconscious",act->Initiator->szName);
        lstPrint(szText);
        act->Initiator->IsActive = FALSE;
    }
    HandleExecution(act->Initiator);

    return Objective->IsActive;
}

//---------------------------------------------------------------------------
int GroupNonLethalAttack(InteractionParam *act)
{
    Core *ObjBS;
    Character *Objective;
    char szText[150];
    unsigned short i,nMembQty;

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
        return 0;

    if(!Objective->IsActive || Objective->BEStuff.IsDead())
    {
        sprintf(szText,"%s's party approaches %s with determination to kick ass... but",
            act->Initiator->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("there's no point now");
        return 1;
    }

    sprintf(szText,"%s attempts to knock %s down...",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    sprintf(szText,"%s's party attempt to knock %s down. Yes = %s wins, No = %s wins.",
        act->Initiator->szName,Objective->szName,act->Initiator->szName,
        Objective->szName);
    if(ChoiceYN(szText,"Non-Lethal Attack"))
    {
        sprintf(szText,"%s falls unconscious",Objective->szName);
        lstPrint(szText);
        Objective->IsActive = FALSE;
    }
    else
    {
        sprintf(szText,"Unbelievable! The kicking and roaming mass is all knocked down!");
        lstPrint(szText);
        act->Initiator->IsActive = FALSE;
        nMembQty = act->Squad->g.GetMembersQty();
        for(i = 0; i < nMembQty; i++)
        {
            Objective = GetCharacter((ID)act->Squad->g.GetMemberCoreAddress(i));
            if(Objective != NULL)
                Objective->IsActive = FALSE;
        }
    }

    return 1;
}

//---------------------------------------------------------------------------
int Statement(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    ActionError aeRetCode;
    Character *Objective;
    char szText[150];

    if(!CheckInteractionParam(act))
    {
        return 0;
    }

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        return 0;
    }

    if(!Objective->IsActive)
    {
        sprintf(szText,"%s tells %s a story... and after talking for an hour,",
            act->Initiator->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("notices that the latter is unconscious");
        return 0;
    }

    sprintf(szText,"%s tells %s the following:",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    szText[0] = '\0';
    GetWording(act->ToBeExecuted,szText,VM_STATEMENT);
    if(szText[0] == '\0')
        return 1;
    lstPrint(szText);
    if(act->ToBeExecuted->GetReported1Ptr() != NULL)
        if(act->ToBeExecuted->GetReported1Ptr()->GetObjective()
                == Objective->BEStuff.GetCoreAddress() ||
                act->ToBeExecuted->GetReported2Ptr()->GetObjective()
                == Objective->BEStuff.GetCoreAddress())
        {
            sprintf(szText,"Giving %s a strange look, %s slowly backs off...",
                act->Initiator->szName,Objective->szName);
        }
    aeRetCode = AcceptStatement(act->ToBeExecuted->GetReported1Ptr(),ObjBS);
    if(aeRetCode == ACTION_SUCCESS)
    //problem to solve: 3rd party attitude to committed action
        sprintf(szText,"%s believes the told story...",Objective->szName);
    else
        sprintf(szText,"%s doesn\'t believe the told story...",
            Objective->szName);
    lstPrint(szText);

    HandleExecution(act->Initiator);

    return 1;
}

//---------------------------------------------------------------------------
int Request(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    Action *apRequest;
    int nRetVal = 0;
    unsigned char RequestedActionImportance;
    char szText[150];

    if(!CheckInteractionParam(act))
    {
        return 0;
    }

    ObjBS = act->ToBeExecuted->GetObjective();
    apRequest = act->ToBeExecuted->GetReported1Ptr();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL || apRequest == NULL)
    {
        return 0;
    }

    if(!Objective->IsActive)
    {
        sprintf(szText,"%s asks %s for help... and after talking for an hour,",
            act->Initiator->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("notices that the latter is unconscious");
        return 0;
    }
    sprintf(szText,"%s asks %s the following:",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    GetWording(act->ToBeExecuted,szText,VM_REQUEST);
    lstPrint(szText);

    if(Objective->IsPlayerCharacter)
    {
        strcat(szText,"\nDo you fulfill the request?");
        if(!ChoiceYN(szText,"Request"))
        {
            sprintf(szText,"%s refuses...",
                Objective->szName);
            return 0;
        }
        if(AccomplishPromise(apRequest, Objective,
                UNSURPASSED_IMPORTANCE))
        {
            sprintf(szText,"...%s adopts a new strategy.",Objective->szName);
            lstPrint(szText);
        }
        return 1;
    }


    RequestedActionImportance = (percentage)AcceptRequest(apRequest,
            act->Initiator->BEStuff.GetCoreAddress());
    if(RequestedActionImportance > 0)
    {
    //problem to solve: 3rd party attitude to committed action
        if(RequestedActionImportance == 1)
            sprintf(szText,"%s agrees...",Objective->szName);
        else
            sprintf(szText,"%s willingly agrees...",Objective->szName);
        lstPrint(szText);
        if(AccomplishPromise(apRequest, Objective,
            RequestedActionImportance))
        {
            sprintf(szText,"...%s adopts a new strategy.",Objective->szName);
            nRetVal = 1;
        }
        else
        {
            sprintf(szText,"...Suddenly, %s recalls something more important must be fulfilled and refuses.",
                Objective->szName);
        }
    }
    else
        sprintf(szText,"%s refuses...",
            Objective->szName);

    lstPrint(szText);

    HandleExecution(act->Initiator);

    return nRetVal;
}

//---------------------------------------------------------------------------
int Deal(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    Action *Rep1, *Rep2;
    ItemScores *isTarget;
    InventoryItem *iiTarget,*TmpMoneyItem = NULL;
    unsigned char DealImportance;
    char szText[150];
    InteractionParam ItemExchange;
    percentage nNeededVal, nChange;
    BOOL bExchange1Scheduled = FALSE,bExchange2Scheduled, bUserAgreed = FALSE;
    double OldCash = 0;

    if(!CheckInteractionParam(act))
    {
        return 0;
    }

    ItemExchange.Squad = NULL;
    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        return 0;
    }

    Rep1 = act->ToBeExecuted->GetReported1Ptr();
    Rep2 = act->ToBeExecuted->GetReported2Ptr();
    if(Rep1 == NULL)
        return 0;

    //the same deal shouldn't be done twice
    GetHistoryAddress()->Register(
        act->Initiator->BEStuff.GetCore().attId,
        act->Initiator->BEStuff.CurrentStrategy.GetID(),
        act->Initiator->BEStuff.CurrentStrategy.GetObjective()->attId,
        STRATEGY_ITEMID(act->Initiator->BEStuff.CurrentStrategy),
        STRATEGY_FAILURE);
    if(!Objective->IsActive)
    {
        sprintf(szText,"%s offers %s a deal... and after talking for an hour,",
            act->Initiator->szName,Objective->szName);
        lstPrint(szText);
        lstPrint("notices that the latter is unconscious");
        return 0;
    }

    sprintf(szText,"%s offers %s a deal:",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    //format output string
    GetWording(act->ToBeExecuted,szText,VM_DEAL);
    lstPrint(szText);
    if(Objective->IsPlayerCharacter)
    {
        strcat(szText,"\nDo you accept the deal?");
        if(!ChoiceYN(szText,"Request"))
        {
            sprintf(szText,"%s refuses...",
                Objective->szName);
            lstPrint(szText);
            return 0;
        }
        bUserAgreed = TRUE;
    }

    //check if the exchange item is money
    isTarget = Rep1->GetObjItem();
    if(isTarget != NULL)
    {
    //check if the amount matches the "purchase". If not, create "change"
        iiTarget = act->Initiator->GetItem(isTarget);
        if(iiTarget)
            if(strcmpi(iiTarget->Descr,"money") == 0)
            {
                nNeededVal = (percentage)-PersonalEvaluate(*Rep2,
                    Objective->BEStuff.GetCore());
                nChange = (percentage)(isTarget->Wealth - nNeededVal);
                //We don't have to pass all the money,
                //so the change should be held somewhere else
                if(nChange > 0)
                {
                    isTarget->Wealth = nNeededVal;
                    OldCash = act->Initiator->Cash;//just in case
                    //1st, calc cash
                    act->Initiator->Cash = WealthPts2Cash(nChange);
                    //2nd, add new money item
                    TmpMoneyItem = SetMoneyInInv(&(act->Initiator->InventoryHead),
                        act->Initiator->Cash,TRUE);
                }
            }

    }

    DealImportance = (percentage)AcceptDealProposal(act->ToBeExecuted);
    if(DealImportance > 0 || bUserAgreed)
    {
    //problem to solve: 3rd party attitude to committed action
        if(DealImportance == 1)
            sprintf(szText,"%s agrees...",Objective->szName);
        else
            sprintf(szText,"%s agrees and seems very satisfied by the deal...",Objective->szName);
        lstPrint(szText);
        if(bUserAgreed)
            DealImportance = UNSURPASSED_IMPORTANCE;

        if(Rep1->GetObjItem())
        {
            //giving item to objective
            bExchange1Scheduled = TRUE;
        }
        else
            if(act->Initiator->BEStuff.AdoptStrategyFromOneAction(Rep1,
                    DealImportance))
            {
                sprintf(szText,"%s adopts a new strategy.",act->Initiator->szName);
                lstPrint(szText);
            }
            else
            {
                sprintf(szText,"...After thinking it over, %s cancels the deal.",
                    act->Initiator->szName);
                lstPrint(szText);
//                bExchange2Scheduled = FALSE;
                HandleExecution(act->Initiator);
                return 1;
            }

        if(Rep2->GetObjItem())
        {
            //taking item from objective
            bExchange2Scheduled = TRUE;
        }
        else
        {
            if(Objective->BEStuff.AdoptStrategyFromOneAction(Rep2,
                DealImportance))
                sprintf(szText,"...%s adopts a new strategy.",Objective->szName);
            else
            {
                sprintf(szText,"...After thinking it over, %s refuses.",
                    Objective->szName);
                bExchange1Scheduled = FALSE;
            }
            bExchange2Scheduled = FALSE;
            lstPrint(szText);
        }

        if(bExchange1Scheduled)
        {
            //giving item to objective
            ItemExchange.ToBeExecuted = Rep1;
            ItemExchange.Initiator = act->Initiator;
            GiveItem(&ItemExchange);
        }
        if(bExchange2Scheduled)
        {
            //giving item to objective
            ItemExchange.ToBeExecuted = Rep2;
            ItemExchange.Initiator = Objective;
            GiveItem(&ItemExchange);
        }
    }
    else
    {
        sprintf(szText,"%s refuses...",
            Objective->szName);
        lstPrint(szText);

        if(TmpMoneyItem != NULL)
        {
            act->Initiator->Cash = OldCash;//restore old balance
            if(act->Initiator->GetItem(&(TmpMoneyItem->BEStuff),&iiTarget)) //now remove the change
            {
                if(iiTarget == act->Initiator->InventoryHead)
                    act->Initiator->InventoryHead = TmpMoneyItem->next;
                else
                    iiTarget->next = TmpMoneyItem->next;
            }
        }
    }

    HandleExecution(act->Initiator);

    return 1;
}

//---------------------------------------------------------------------------
int Tease(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    char szText[150];

    if(!CheckInteractionParam(act))
    {
        return 0;
    }

    ObjBS = act->ToBeExecuted->GetObjective();
    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        return 0;
    }
    sprintf(szText,"%s yells to %s:",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    GetWording(act->ToBeExecuted,szText,VM_TEASE);
    lstPrint(szText);
    HandleExecution(act->Initiator);

    return 1;
}

//---------------------------------------------------------------------------
void GetWording(Action *pVerbalA,char *szBuffer,unsigned char byMode)
{
    Action *Rep1,*Rep2;
    ActionDefNodeP  pActDef;
    Character *chIniti,*chObj;
    Core *bsIniti, *bsObj;
    ItemScores *isObj;
    InventoryItem *iiObj;
    BOOL ItemRelatedThreat = FALSE;
    char szAux[40],szAux2[40],szAux3[40];
    if(pVerbalA == NULL)
        return;
    Rep1 = pVerbalA->GetReported1Ptr();
    Rep2 = pVerbalA->GetReported2Ptr();

    switch(byMode){
        case VM_STATEMENT:
            if(Rep1 == NULL || Rep2 == NULL)
                return;
            chIniti = GetCharacter(Rep1->GetInitiator()->attId);
            chObj = GetCharacter(Rep1->GetObjective()->attId);
            pActDef = GetActionNode(Rep1->GetID());
            if(pActDef != NULL)
                strncpy(szAux,pActDef->Verb,MAX_ACT_NAME_LEN);
            else
                strcpy(szAux,"");

            sprintf(szBuffer,"\"...Once upon a time %s executed %s on %s...\"",
                chIniti->szName,szAux,chObj->szName);
            break;
        case VM_DEAL:
            if(Rep2 == NULL)
                return;
            isObj = Rep2->GetObjItem();
            if(isObj == NULL)
            {
                bsObj = Rep2->GetObjective();
                if(bsObj)
                    chObj = GetCharacter(bsObj->attId);
                pActDef = GetActionNode(Rep2->GetID());
                if(pActDef != NULL)
                    strncpy(szAux,pActDef->Verb,MAX_ACT_NAME_LEN);
                else
                    strcpy(szAux,"");
                if(chObj)
                {
                    sprintf(szBuffer,"\"...I want you to execute %s on %s...\"",
                        szAux,chObj->szName);
                    lstPrint(szBuffer);//the output here is multi-line
                }

            }
            else
            {
                bsObj = Rep2->GetObjective();
                if(bsObj)
                    chObj = GetCharacter(bsObj->attId);
                iiObj = chObj->GetItem(isObj->ItemID);
                if(iiObj)
                {
                    sprintf(szBuffer,"\"...I want your %s...\"",
                        iiObj->Descr);
                    lstPrint(szBuffer);//the output here is multi-line
                }
            }

            if(Rep1 == NULL)
                return;
            bsIniti = pVerbalA->GetInitiator();
            if(bsIniti)
                chIniti = GetCharacter(bsIniti->attId);
            isObj = Rep1->GetObjItem();
            if(isObj == NULL)
            {
                bsObj = Rep1->GetObjective();
                if(bsObj)
                    chObj = GetCharacter(bsObj->attId);
                pActDef = GetActionNode(Rep1->GetID());
                if(pActDef != NULL)
                    strncpy(szAux,pActDef->Verb,MAX_ACT_NAME_LEN);
                else
                    strcpy(szAux,"");
                if(chObj)
                {
                    sprintf(szBuffer,"\"...In exchange, I'll execute %s on %s...\"",
                        szAux,chObj->szName);
                }
            }
            else
            {
                iiObj = chIniti->GetItem(isObj->ItemID);
                sprintf(szBuffer,"\"...In exchange, you can have my %s...\"",
                    iiObj->Descr);
            }
            break;
        case VM_REQUEST:
            if(Rep1 == NULL)
                return;
            isObj = Rep1->GetObjItem();
            if(isObj == NULL)
            {
                chObj = GetCharacter(Rep1->GetObjective()->attId);
                pActDef = GetActionNode(Rep1->GetID());
                if(pActDef != NULL)
                    strncpy(szAux,pActDef->Verb,MAX_ACT_NAME_LEN);
                else
                    strcpy(szAux,"");
                sprintf(szBuffer,"\"...I want you to execute %s on %s...\"",
                    szAux,chObj->szName);
            }
            else
            {
                chObj = GetCharacter(pVerbalA->GetObjective()->attId);
                iiObj = chObj->GetItem(isObj->ItemID);
                sprintf(szBuffer,"\"...I want your %s...\"",
                    iiObj->Descr);
            }
            break;
        case VM_TEASE:
            bsObj = pVerbalA->GetObjective();
            bsIniti = pVerbalA->GetInitiator();
            //...here the glorious AbuseGenerator will generate the most
            // suitable abuse
            strncpy(szAux,AbuseGenerator(*bsIniti,
                *bsObj,BOTH),40);

            switch(Random(1,5))
            {
                case 1:
                    sprintf(szBuffer,"\"Hey you, %s!\"",szAux);
                    break;
                case 2:
                    sprintf(szBuffer,"\"Hey, %s!\"",szAux);
                    break;
                case 3:
                    sprintf(szBuffer,"\"Yo, %s!\"",szAux);
                    break;
                case 4:
                    sprintf(szBuffer,"\"You %s! How dare you to show your face here?!\"",
                        szAux);
                    break;
                default:
                    sprintf(szBuffer,"\"Hey you, %s!\"",szAux);
            }

            break;
        case VM_THREAT:
            Rep1 = pVerbalA->GetReported1Ptr();
            Rep2 = pVerbalA->GetReported2Ptr();
            if(Rep1 == NULL || Rep2 == NULL)
                return;

            bsIniti = Rep1->GetObjective();
            if(bsIniti)
                chIniti = GetCharacter(bsIniti->attId);
            else
                return;
            bsObj = Rep2->GetObjective();
            if(bsObj)
                chObj = GetCharacter(bsObj->attId);
            else
                return;
            if(chIniti == NULL || chObj == NULL)
                return;

            pActDef = GetActionNode(Rep1->GetID());
            if(pActDef != NULL)
                strncpy(szAux2,pActDef->Verb,MAX_ACT_NAME_LEN);
            else
                strcpy(szAux2,"");

            pActDef = GetActionNode(Rep2->GetID());
            if(pActDef != NULL)
                strncpy(szAux3,pActDef->Verb,MAX_ACT_NAME_LEN);
            else
                strcpy(szAux3,"");

            //...here the glorious AbuseGenerator will generate the most
            // suitable abuse
            strncpy(szAux,AbuseGenerator(*(pVerbalA->GetInitiator()),//*bsIniti,
                *(pVerbalA->GetObjective()),//*bsObj,
                BOTH),40);
            isObj = Rep2->GetObjItem();
            if(isObj)   //maybe the threat is inventory related
            {
                iiObj = chIniti->GetItem(isObj->ItemID);
                if(iiObj)
                {
                    ItemRelatedThreat = TRUE;
                    strcpy(szAux3,iiObj->Descr);
                }
            }
            if(bsIniti == pVerbalA->GetObjective())
            {
                if(ItemRelatedThreat)
                    sprintf(szBuffer,"\"Hey, %s! Give me your %s or I'll %s you!\"",
                        szAux,szAux3,szAux2);
                else
                    sprintf(szBuffer,"\"Hey, %s! You must %s %s or I'll %s you\"",
                        szAux,szAux3,chObj->szName,szAux2);
            }
            else
            {
                if(ItemRelatedThreat)
                    sprintf(szBuffer,"\"Hey, %s! Give me your %s or I'll %s %s\"",
                        szAux,szAux3,szAux2,chIniti->szName);
                else
                    sprintf(szBuffer,"\"Hey, %s! Execute %s on %s or I'll %s %s\"",
                        szAux,szAux3,chObj->szName,szAux2,chIniti->szName);
            }

            break;
    }
}

//---------------------------------------------------------------------------
void DiscardCorpses()
{
	CharacterNode cp, sav_cp;

	for(cp = CharListHead ; cp != NULL ; cp = sav_cp)
    {
        sav_cp = cp->next;
    	if(cp->ch.BEStuff.IsDead())
        {
            //Rest In Peace... but do not take up memory
            DelCharacter(cp->ch.CharID);
        }
    }
}

//---------------------------------------------------------------------------
//CheckInteractionParam()
// performs routine checks for NULL pointers
// to prevent crashes
//---------------------------------------------------------------------------
BOOL CheckInteractionParam(InteractionParam *ToCheck)
{
    Core *ObjBS,*InitiBS;
    Character *ch;
    ID ActID;
    ActionDefNodeP ap;
    char szText[50];

    if(ToCheck == NULL)
    {
        lstPrint("Can't execute action: parameter(s) missing!");
        return FALSE;
    }

    if(ToCheck->ToBeExecuted == NULL)
    {
        lstPrint("Action is NULL!");
        return FALSE;
    }

    if(ToCheck->Squad != NULL)
    {
        ActID = ToCheck->ToBeExecuted->GetID();
        switch(ActID)
        {
            case ATTACK:
                        GroupAttack(ToCheck);
                        break;
            case NONLETHAL_ATTACK:
                        GroupNonLethalAttack(ToCheck);
                        break;
            default:
                ap = GetActionNode(ActID);
                if(ap != NULL)
                {
                    strcpy(szText,ap->Verb);
                    strcat(szText," cannot be executed by a party");
                    lstPrint(szText);
                }
                return FALSE;

        }
//        InitiBS = ToCheck->Squad->GetLeaderCoreAddress();
        ObjBS = ToCheck->ToBeExecuted->GetObjective();
        if(ObjBS != NULL)
        {
            if(ObjBS->Dead)
                return FALSE;
            ch = GetCharacter((ID)ObjBS->attId);
            if(ch != NULL && ch->IsActive)
                ch->BEStuff.CalcResponse(*(ToCheck->ToBeExecuted));
        }
        return TRUE;   //nothing else should be executed
    }
        //it is impossible to pass the objective with other parameters,
        //because it might not be defined yet...
    if(ToCheck->Initiator == NULL)
    {
        lstPrint("Can't execute action: initiator is missing!");
        return FALSE;
    }

    ObjBS = ToCheck->ToBeExecuted->GetObjective();
    InitiBS = ToCheck->Initiator->BEStuff.GetCoreAddress();
        //...But we may obtain it now!
    if(ObjBS == NULL || InitiBS == NULL)
    {
        lstPrint("Can't execute action: could not obtain participants' address(es)");
        return FALSE;
    }

//    if(ToCheck->ToBeExecuted->GetType() != VERBAL)
        return TRUE;
/*
    if(ToCheck->ToBeExecuted->GetReported1Ptr() == NULL)
        return FALSE;

    return TRUE;
*/
}

//---------------------------------------------------------------------------
int CheckMember(InteractionParam *act)
{
    Core *ObjBS;
    Character *Candidate;
    char szText[100],szLeaderName[31];
    ObjBS = act->ToBeExecuted->GetObjective();
    if(ObjBS == NULL)
        return 0;
    Candidate = GetCharacter(ObjBS->attId);
    if(Candidate == NULL)
        return 0;
    if(act->Initiator)
        strcpy(szLeaderName,act->Initiator->szName);
    else
        strcpy(szLeaderName,"someone");
    sprintf(szText,"Enlist %s to %s's group?",
        Candidate->szName,szLeaderName);
    return ChoiceYN(szText,"Candidate to party");

}

//---------------------------------------------------------------------------
int TakeItem(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    ItemScores *isTarget;
    InventoryItem *iiTarget,*iiSav = NULL;
    char szText[150];

    if(!CheckInteractionParam(act))
        return 0;

    ObjBS = act->ToBeExecuted->GetObjective();
    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        lstPrint("Couldn't find objective");
        return 0;
    }

    isTarget = act->ToBeExecuted->GetObjItem();
    if(isTarget == NULL)
        return 0;
    iiTarget = Objective->GetItem(isTarget->ItemID,&iiSav);
    if(iiTarget == NULL)
        return 0;
    if(Objective == act->Initiator)
    {
        sprintf(szText,"Suddenly, %s's hand reaches for %s... and releases it.",
            Objective->szName,iiTarget->Descr);
        lstPrint(szText);
        return 1;
    }

    if(!Objective->BEStuff.IsDead() && Objective->IsActive)
    {
        sprintf(szText,"As %s's hand reaches for %s's %s, %s stops it:",
            act->Initiator->szName,Objective->szName,iiTarget->Descr,
            Objective->szName);
        lstPrint(szText);
        lstPrint("\"I believe it's still mine...\"");
        HandleExecution(act->Initiator);
        return 0;
    }

    if(iiTarget == Objective->InventoryHead)
        Objective->InventoryHead = iiTarget->next;
    else
        if(iiSav != NULL)
            iiSav->next = iiTarget->next;

    act->Initiator->AddItem(iiTarget);
    sprintf(szText,"%s takes the %s from %s...",
        act->Initiator->szName,iiTarget->Descr,Objective->szName);
    lstPrint(szText);

    return 1;
}

//---------------------------------------------------------------------------
int Steal(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    ItemScores *isTarget;
    InventoryItem *iiTarget,*iiSav = NULL;
    char szText[150];

    if(!CheckInteractionParam(act))
        return 0;

    ObjBS = act->ToBeExecuted->GetObjective();
    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        lstPrint("Couldn't find objective");
        return 0;
    }

    isTarget = act->ToBeExecuted->GetObjItem();
    if(isTarget == NULL)
        return 0;
    iiTarget = Objective->GetItem(isTarget->ItemID,&iiSav);
    if(iiTarget == NULL)
        return 0;
    if(Objective == act->Initiator)
    {
        sprintf(szText,"Suddenly, %s's hand reaches for %s... and releases it.",
            Objective->szName,iiTarget->Descr);
        lstPrint(szText);
        return 1;
    }

    if(Objective->BEStuff.IsDead() || !Objective->IsActive)
        return TakeItem(act);

    if(iiTarget == Objective->InventoryHead)
        Objective->InventoryHead = iiTarget->next;
    else
        if(iiSav != NULL)
            iiSav->next = iiTarget->next;

    sprintf(szText,"%s makes an attempt to steal the %s from %s...",
        act->Initiator->szName,iiTarget->Descr,Objective->szName);
    lstPrint(szText);
    if(Objective->BEStuff.GetCore().Meanness[0]
            > (act->Initiator->BEStuff.GetCore().Meanness[0] + Random(0,4)))
    {
        lstPrint("...and succeeds!");
        act->Initiator->AddItem(iiTarget);
        return 1;
    }
    else
    {
        lstPrint("...and fails, facing the consequences...");
        HandleExecution(act->Initiator);
        return 0;
    }
}

//---------------------------------------------------------------------------
int GiveItem(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    ItemScores *isTarget;
    InventoryItem *iiTarget,*iiSav = NULL;
    char szText[150];
    if(!CheckInteractionParam(act))
        return 0;

    ObjBS = act->ToBeExecuted->GetObjective();
    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        lstPrint("Couldn't find objective");
        return 0;
    }

    isTarget = act->ToBeExecuted->GetObjItem();
    if(isTarget == NULL)
    {
        lstPrint("Couldn't find item to give");
        return 0;
    }

    iiTarget = act->Initiator->GetItem(isTarget,&iiSav);
    if(iiTarget == NULL)
    {
        lstPrint("Couldn't find item to give");
        return 0;
    }

    if(iiTarget == act->Initiator->InventoryHead)
        act->Initiator->InventoryHead = iiTarget->next;
    else
        if(iiSav != NULL)
            iiSav->next = iiTarget->next;

    Objective->AddItem(iiTarget);
    sprintf(szText,"%s passes %s the %s...",
        act->Initiator->szName,Objective->szName,iiTarget->Descr);
    lstPrint(szText);
    Objective->EnumInventory();
    act->Initiator->EnumInventory();

    return 1;
}

//---------------------------------------------------------------------------
InventoryItem *SetMoneyInInv(InventoryItem **InvHead, double nCash, BOOL bForceInsertMode)
{
    InventoryItem *iip;
    BOOL found = FALSE;

    if(!bForceInsertMode)
        for(iip = *InvHead; (iip != NULL) && !found; iip = iip->next)
            if(strcmpi("money",iip->Descr) == 0)
            {
                found = TRUE;
                break;
            }

    if(bForceInsertMode || !found)
    {
        iip = new InventoryItem;
        if(iip == NULL)
        	return NULL;
        strcpy(iip->Descr,"money");
    	iip->_Type = (ItemType)(0);
        iip->next = *InvHead;
        *InvHead = iip;
    }

    iip->SetBEScores(0, 0, 0,
        	0, (percentage)(Price2WealthPts(nCash)),0,0);
    iip->BEStuff.CanBeDivided = TRUE;
    return iip;
}

//---------------------------------------------------------------------------
int Threat(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    ActionError ThreatAcceptance;
    Action *aThreat, *aDemand;
    ID idAntiGoal;
    int iRetCode;
    char szText[150];

    if(!CheckInteractionParam(act))
    {
        return 0;
    }

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
    {
        return 0;
    }
    sprintf(szText,"%s threatens %s:",
        act->Initiator->szName,Objective->szName);
    lstPrint(szText);

    aThreat = act->ToBeExecuted->GetReported1Ptr();
    aDemand = act->ToBeExecuted->GetReported2Ptr();
    if(aThreat == NULL || aDemand == NULL)
        return 0;
    switch(aThreat->GetID())
    {
        case 7://lethal attack
            idAntiGoal = 14;//SELF-DEFENSE: kill the potential attacker
            break;
    }

    GetWording(act->ToBeExecuted,szText,VM_THREAT);
    lstPrint(szText);
    ThreatAcceptance = (percentage)AcceptThreat(act->ToBeExecuted,
        idAntiGoal,&(Objective->BEStuff));
    //now for the outcome...
    switch(ThreatAcceptance)
    {
        case ACTION_NOT_PRIMED: return 0;
        case WOULD_NOT_BELIEVE:
            sprintf(szText,"%s considers the threat hollow and ignores it...",Objective->szName);
            lstPrint(szText);
            iRetCode = 0;
            break;
        case ACTION_SUCCESS:
            sprintf(szText,"%s accepts the conditions...",Objective->szName);
            lstPrint(szText);
            //swap initiator and target. Ugly, but this is the only way...
            aDemand->InitTargets(ObjBS,act->Initiator->BEStuff.GetCoreAddress());
            if(AccomplishPromise(aDemand, Objective, 6))
                iRetCode = 1;
            else
                iRetCode = 0;

            break;
        default:
            sprintf(szText,"%s does not accept the conditions...",Objective->szName);
            lstPrint(szText);
            iRetCode = 0;
            break;
    }

    HandleExecution(act->Initiator);

    return iRetCode;
}

//---------------------------------------------------------------------------
#define GROUP_ATTACK    11  //that's "sheer numbers" strategy number
int GatherGang(InteractionParam *act)
{
    Core *ObjBS,*InitiBS;
    Character *Objective;
    GroupNode NewGroup = new struct _GroupNode,pGr;
    char szText[150];

    if(!CheckInteractionParam(act))
        return 0;

    ObjBS = act->ToBeExecuted->GetObjective();

    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
        return 0;
    if(GroupListHead != NULL)
    {
        for(pGr = GroupListHead; pGr != NULL; pGr = pGr->next)
            if(pGr->g.GetLeaderCoreAddress()
                    == act->Initiator->BEStuff.GetCoreAddress())
            {
                return 0;
            }
    }
    NewGroup->g = *CreateGroup(&(act->Initiator->BEStuff),
        GROUP_ATTACK,
        ObjBS);
    sprintf(NewGroup->szName,"%s's gang",act->Initiator->szName);

    InitiBS = act->Initiator->BEStuff.GetCoreAddress();

    //this is done in order to prevent from creating the same group twice
    GetHistoryAddress()->Register(InitiBS->attId,
        act->Initiator->BEStuff.CurrentStrategy.GetID(),
        act->Initiator->BEStuff.CurrentStrategy.GetObjective()->attId,
        0,
        STRATEGY_FAILURE);

    if(NewGroup == NULL)
    {
        sprintf(szText,"%s cannot gather a group",act->Initiator->szName);
        lstPrint(szText);
        return 0;
    }

    NewGroup->next = GroupListHead;
    GroupListHead = NewGroup;
    sprintf(szText,"%s looks for the potential group members...",
        act->Initiator->szName);
    lstPrint(szText);

    return 1;
}

//---------------------------------------------------------------------------
int AccomplishPromise(Action *aPromise, Character *Initiator,
    percentage Importance)
{
    InteractionParam ItemExchange;
    if(aPromise == NULL || Initiator == NULL)
    {
//        Stop("Promise is NULL?! What the hell are you talking about?!");
        lstPrint("Error - cannot find promise action");
        return 0;
    }
    ItemExchange.Squad = NULL;

    if(aPromise->GetObjItem())
    {
        //giving item to objective
        ItemExchange.ToBeExecuted = aPromise;
        ItemExchange.Initiator = Initiator;
        GiveItem(&ItemExchange);
    }
    else
        if(Initiator->BEStuff.AdoptStrategyFromOneAction(aPromise,
                    Importance))
        {
//            Stop("Promise: new strategy adopted");
        }
        else
        {
            HandleExecution(Initiator);
            return 0;
        }

    return 1;
}

//---------------------------------------------------------------------------
void SaveAllCharacters(char *szFilename)
{
    HANDLE hFile;
    CharacterNode cp;
    int nCharQty = 0, nAllocated = 0, nUsedByLast;
    unsigned long nBytesWritten = 0;
    BYTE *bypBuffer;
//    BYTE bypBuffer[30000];

    hFile = CreateFile(szFilename,GENERIC_WRITE,0,
        NULL,CREATE_ALWAYS,FILE_FLAG_SEQUENTIAL_SCAN,NULL);

    if(hFile == NULL)
    {
        return;
    }

    //count characters
    for(cp = CharListHead; cp != NULL; cp = cp->next)
        nCharQty++;

//    bypBuffer = (BYTE*)GlobalAlloc(GPTR,nCharQty * 1000);
    bypBuffer = new BYTE[nCharQty * 1000];
    if(bypBuffer == NULL)   return;

    //first write down the quantity
    memcpy(bypBuffer,&nCharQty,sizeof(long));
    nAllocated += sizeof(long);

    //the scheme is:
    //  <how much is used>  <------ the character itself -------> <"CHEND">

    for(cp = CharListHead; cp != NULL; cp = cp->next)
    {
        nAllocated += sizeof(long);
        nUsedByLast = cp->ch.FillSaveBuffer(bypBuffer + nAllocated);
        memcpy(bypBuffer + nAllocated - sizeof(long),&nUsedByLast,sizeof(long));
        nAllocated += nUsedByLast;
    }
    if(!WriteFile(hFile,bypBuffer,nAllocated,&nBytesWritten,NULL))
        APIError();

    CloseHandle(hFile);
    delete []bypBuffer;//    GlobalFree((HGLOBAL)bypBuffer);
}

//---------------------------------------------------------------------------
void RestoreAllCharacters(char *szFilename)
{
    HANDLE hFile;
    Character *NewCh, *ChInList;
    CharacterNode cpi;
    unsigned long nBytesRead = 0,nCharQty = 0, nCopied, nCharRecLen, i;
    unsigned short *ItemArraysLen;
    BYTE *bypBuffer;

    Agent **People = NULL;
    ID PeopleNo;
    ItemPtrArr *Inventories;

    hFile = CreateFile(szFilename,GENERIC_READ,0,
        NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL);
    if(hFile == NULL)
    {
        return;
    }

    nCopied = GetFileSize(hFile,NULL);
    if(!nCopied)
    {
        Stop("Corrupted file!");
        CloseHandle(hFile);
        return;
    }

    bypBuffer = (BYTE*)GlobalAlloc(GPTR,nCopied);
    if(bypBuffer == NULL)   return;
    if(!ReadFile(hFile,bypBuffer,nCopied,&nBytesRead,NULL))
    {
        APIError();
        CloseHandle(hFile);
        return;
    }
    CloseHandle(hFile); //"I don't need you anymore!"

    FreeCharList(); //"Just to prevent mess"

    memcpy(&nCharQty,bypBuffer,sizeof(long));
    nCopied = (unsigned short)sizeof(long);
    while(nCopied < nBytesRead)
    {
        memcpy(&nCharRecLen,bypBuffer + nCopied,sizeof(long));
        nCopied += (unsigned short)sizeof(long);
        NewCh = new Character;
        if(NewCh == NULL)
            return;
        ChInList = AddCharacter(*NewCh);
        delete NewCh;
//        NewCh = NULL;
        if(ChInList != NULL)
            ChInList->RestoreFromBufferStep1(bypBuffer + nCopied,
                    (unsigned short)nCharRecLen);
        nCopied += nCharRecLen;
    }

    //fill all the mega-arrays...
    PeopleNo = MakePopulationArray(&People);
    Inventories = new ItemPtrArr[PeopleNo];
    ItemArraysLen = new unsigned short[PeopleNo];
    i = 0;
    for(cpi = CharListHead; cpi != NULL ; cpi = cpi->next )
    {
        Inventories[i] = NULL;
        ItemArraysLen[i] = cpi->ch.InvListToArray(&(Inventories[i]));
        i++;
    }

    memcpy(&nCharQty,bypBuffer,sizeof(long));
    nCopied = (unsigned short)sizeof(long);
    for(cpi = CharListHead; cpi != NULL ; cpi = cpi->next )
    {
        memcpy(&nCharRecLen,bypBuffer + nCopied,sizeof(long));
        nCopied += (unsigned short)sizeof(long);
        cpi->ch.RestoreFromBufferStep2(bypBuffer + nCopied,
            (unsigned short)nCharRecLen,
            People,PeopleNo,Inventories,ItemArraysLen);
        CharacterForm->PrintCharacterInfo(&(cpi->ch));
        nCopied += nCharRecLen;
    }

    //free all the wasted space... Hey, what about the time?..
    for(i = 0; i < (unsigned long)PeopleNo ; i++ )
        delete [](Inventories[i]);

    delete []Inventories;
    delete []ItemArraysLen;
    delete []People;
    GlobalFree((HGLOBAL)bypBuffer);
}

//---------------------------------------------------------------------------
int DefaultFn(InteractionParam *act)
{
    Core *ObjBS;//,*InitiBS;
    Character *Objective;
    char szText[150],szActionName[30];
    ActionDefNodeP pActDef;

    if(!CheckInteractionParam(act))
        return 0;

    ObjBS = act->ToBeExecuted->GetObjective();
    Objective = GetCharacter(ObjBS->attId);
    if(Objective == NULL)
        return 0;

    pActDef = GetActionNode(act->ToBeExecuted->GetID());
    if(pActDef != NULL)
        strncpy(szActionName,pActDef->Verb,MAX_ACT_NAME_LEN);
    else
        strcpy(szActionName,"");
    sprintf(szText,"%s executes %s on %s...",act->Initiator->szName,szActionName,
        Objective->szName);
    lstPrint(szText);
    sprintf(szText,"//Unfortunately, function handling this action is not linked to Brainiac Behavior Engine.");
    lstPrint(szText);
    sprintf(szText,"//<Now executing default function>");
    lstPrint(szText);
    sprintf(szText,"//To link your action handler, follow these steps:");
    lstPrint(szText);
    sprintf(szText,"//(1) Write the function. It can receive any parameter (but only one), and return integer.");
    lstPrint(szText);
    sprintf(szText,"//(If you don't want to use this prototype, write a \"wrapper\" function.)");
    lstPrint(szText);
    sprintf(szText,"//(2) Obtain export pattern of your function (string specified in your .DEF file)");
    lstPrint(szText);
    sprintf(szText,"//(3) Specify this export pattern in Brainiac Data Manager - your action's form, where it says \"Function label\"");
    lstPrint(szText);
    sprintf(szText,"//(4) In the same window specify the calling module ID. If your module is not listed, add it to the list");
    lstPrint(szText);
    sprintf(szText,"//through the table of \"Calling modules\"");
    lstPrint(szText);
    sprintf(szText,"// NOTE: Alternately, you can write a \"hub\" function");
    lstPrint(szText);
    sprintf(szText,"// redirecting to other functions according to global vars");
    lstPrint(szText);
    sprintf(szText,"// or your custom object properties, and link it when ");
    lstPrint(szText);
    sprintf(szText,"// initializing Brainiac BE library (InitActionLib()).");
    lstPrint(szText);
    HandleExecution(act->Initiator);
    return 1;
}

//---------------------------------------------------------------------------
InventoryItem *GetItem(InventoryItem *InventoryHead,ID itID,InventoryItem **iiSav)
{
	InventoryItem *iip;

    if(iiSav != NULL)   //save previous pointer
        (*iiSav) = InventoryHead;

	for(iip = InventoryHead ; iip != NULL ; iip = iip->next)
    {
    	if(iip->BEStuff.ItemID == itID)
        	return iip;
        if(iiSav != NULL)   //save previous pointer
            (*iiSav) = iip;
    }
	return NULL;//if nothing found
}

//---------------------------------------------------------------------------
InventoryItem *GetItemN(InventoryItem *InventoryHead,unsigned short itCnt,InventoryItem **iiSav)
{
	InventoryItem *iip;
    unsigned short i = 0;

    if(iiSav != NULL)   //save previous pointer
        (*iiSav) = NULL;

	for(iip = InventoryHead ; iip != NULL ; iip = iip->next)
    {
    	if(i == itCnt)
        {
        	return iip;
        }
        if(iiSav != NULL)   //save previous pointer
            (*iiSav) = iip;
        i++;
    }
	return NULL;//if nothing found
}

//---------------------------------------------------------------------------
void DiscardInactiveGroups()
{
	GroupNode gpi, sav_gp;

    if(GroupListHead == NULL)
        return;
	for(gpi = GroupListHead ; gpi != NULL ; )
    {
        if(gpi->g.ActivityMode() == FALSE)
        {
            sav_gp = gpi->next;
            if(gpi == GroupListHead)
                GroupListHead = NULL;
            delete gpi;
            gpi = sav_gp;
            if(GroupListHead == NULL)
                GroupListHead = gpi;
        }
        else
            gpi = gpi->next;
    }
}

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

