// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
#include "script_compiler.h"
#include "ai_grp_npc.h"
#include "group_profile.h"
#include "ai_generic_fight.h"
#include "server_share/msg_brick_service.h"
#include "continent_inline.h"
#include "dyn_grp_inline.h"
#include "ai_script_data_manager.h"
using std::string;
using std::vector;
using namespace NLMISC;
using namespace AIVM;
using namespace AICOMP;
using namespace AITYPES;
using namespace RYAI_MAP_CRUNCH;
AITYPES::CPropertySet readSet(std::string strings, std::string separator = "|")
{
AITYPES::CPropertySet properties;
CStringSeparator const sep(strings, separator);
while (sep.hasNext())
properties.addProperty(AITYPES::CPropertyId::create(sep.get()));
return properties;
}
// CStateInstance
IScriptContext* spawnNewGroup(CStateInstance* entity, CScriptStack& stack, CAIInstance* aiInstance, CAIVector const& spawnPosition, sint32 baseLevel, double dispersionRadius)
{
string stateMachineName = stack.top();
stack.pop();
string dynGroupName = stack.top();
stack.pop();
if (!entity)
{
nlwarning("spawnNewGroup failed because entity==NULL");
return NULL; // return a normal stack.
}
CGroupDesc const* groupDesc = NULL;
// Find the group template.
// :TODO: Replace it with a faster map access.
FOREACH (itCont, CCont, aiInstance->continents())
{
FOREACH (itRegion, CCont, itCont->regions())
{
FOREACH (itFamily, CCont, itRegion->groupFamilies())
{
FOREACH (itGroupDesc, CCont >, itFamily->groupDescs())
{
if (itGroupDesc->getFullName()==dynGroupName || itGroupDesc->getName()==dynGroupName)
{
groupDesc = *itGroupDesc;
goto groupFound;
}
}
}
}
}
groupFound:
if (groupDesc==NULL)
{
nlwarning("spawnNewGroup failed: No Group Template Found: '%s'",dynGroupName.c_str());
return NULL;
}
// Find the state machine as a manager
CManager* manager=NULL;
FOREACH(itCont, CCont, aiInstance->managers())
{
if (itCont->getFullName()==stateMachineName || itCont->getName()==stateMachineName)
{
manager = *itCont;
break;
}
}
if (!manager)
{
nlwarning("spawnNpcGroup failed : Unknown stateMachine: '%s'", stateMachineName.c_str());
return NULL;
}
// Find the state machine as a npc manager
CMgrNpc* npcManager = dynamic_cast(manager);
if (!npcManager)
{
nlwarning("spawnNpcGroup failed : Not a npc state machine !: '%s'", stateMachineName.c_str());
return NULL;
}
// Get the state machine
CStateMachine const* stateMachine = manager->getStateMachine();
if (stateMachine->cstStates().size()==0)
stateMachine = NULL;
// Save the creator state
bool const savePlayerAttackable = groupDesc->getGDPlayerAttackable();
bool const saveBotAttackable = groupDesc->getGDBotAttackable();
// Set it to a correct value (:TODO: see why)
groupDesc->setGDPlayerAttackable(true);
groupDesc->setGDBotAttackable(true);
// Create the group
CGroupNpc* const grp = groupDesc->createNpcGroup(npcManager, spawnPosition, dispersionRadius, baseLevel);
// Restore the creator state
groupDesc->setGDPlayerAttackable(savePlayerAttackable);
groupDesc->setGDBotAttackable(saveBotAttackable);
// Verify that the group was created
if (!grp)
{
nlwarning("spawnNpcGroup failed : group cannot spawn !: %s", stateMachineName.c_str());
return NULL;
}
// Set the new group parameters
grp->autoDestroy(true);
grp->getPersistentStateInstance()->setParentStateInstance(entity->getPersistentStateInstance());
grp->initDynGrp(groupDesc, NULL);
// Verify that we have a state in the state machine
#if !FINAL_VERSION
if (!stateMachine || stateMachine->cstStates().size()==0)
nlwarning("no state defined for StateMachine in Manager %s", manager->getFullName().c_str());
#endif
// Set the group in that state
if(stateMachine)
grp->setStartState(stateMachine->cstStates()[0]); // sets the first state (must exist!).
grp->updateStateInstance(); // directly call his first state (to retrieve associated params).
return grp;
}
void getZoneWithFlags_helper(CStateInstance* entity, CScriptStack& stack, CAIInstance* const aiInstance, CZoneScorer const& scorer)
{
// :FIXME: Copy n past from getZoneWithFlags2 begin
// Get all the cell-zones
vector cellZones;
FOREACH(itCont, CCont, aiInstance->continents())
{
FOREACH(itRegion, CCont, itCont->regions())
{
FOREACH(itCellZone, CCont, itRegion->cellZones())
{
cellZones.push_back(*itCellZone);
}
}
}
// Shuffle 'em
std::random_shuffle(cellZones.begin(), cellZones.end());
// While no zone found
FOREACH(itCellZone, std::vector, cellZones)
{
// Get all cells
vector cells;
FOREACH(it, CCont, (*itCellZone)->cells())
cells.push_back(*it);
// Shuffle 'em
std::random_shuffle(cells.begin(), cells.end());
// Get a zone with a good score
CNpcZone const* spawnZone = CCellZone::lookupNpcZoneScorer(cells, scorer);
if (spawnZone)
{
stack.push(spawnZone->getAliasTreeOwner().getAliasFullName());
return;
}
}
nlwarning("getZoneWithFlags/getNearestZoneWithFlags No Zone Found");
stack.push(string());
return;
}