1142 lines
34 KiB
C++
1142 lines
34 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
#include "stdpch.h"
|
|
|
|
// Net
|
|
#include "nel/net/service.h"
|
|
// Georges
|
|
#include "nel/georges/u_form.h"
|
|
#include "nel/georges/u_form_elm.h"
|
|
#include "nel/georges/u_form_loader.h"
|
|
#include "nel/georges/load_form.h"
|
|
|
|
#include "sheets.h"
|
|
|
|
#include "nel/misc/o_xml.h"
|
|
|
|
using namespace MULTI_LINE_FORMATER;
|
|
|
|
///////////
|
|
// USING //
|
|
///////////
|
|
using namespace NLGEORGES;
|
|
using namespace NLMISC;
|
|
using namespace NLNET;
|
|
using namespace std;
|
|
using namespace AITYPES;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Constants //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef NL_DEBUG
|
|
CVariable<string> debugSheet("ai", "debugSheet", "The sheet to break onto", "", 0, true);
|
|
#endif
|
|
|
|
char const* AISPackedSheetsFilename="ais.packed_sheets";
|
|
char const* AISPackedFightConfigSheetsFilename="ais_fight_config.packed_sheets";
|
|
char const* AISPackedActionSheetsFilename="ais_action.packed_sheets";
|
|
char const* AISPackedRaceStatsSheetsFilename="ais_race_stats.packed_sheets";
|
|
|
|
static AISHEETS::CCreature EmptySheet;
|
|
|
|
sint32 AISHEETS::ICreature::InvalidFameForGuardAttack = 0x7FFFFFFF;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CAIAction //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
AISHEETS::CAIAction::CAIAction()
|
|
: _SelfAction(false)
|
|
{
|
|
}
|
|
|
|
void AISHEETS::CAIAction::readGeorges(NLMISC::CSmartPtr<NLGEORGES::UForm> const& form, NLMISC::CSheetId const& sheetId)
|
|
{
|
|
NLGEORGES::UFormElm const& item = form->getRootNode();
|
|
// the form was found so read the true values from George
|
|
_SheetId = sheetId;
|
|
item.getValueByName(_SelfAction, "SelfAction");
|
|
}
|
|
|
|
uint AISHEETS::CAIAction::getVersion()
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
void AISHEETS::CAIAction::serial(NLMISC::IStream& s)
|
|
{
|
|
s.serial(_SheetId);
|
|
s.serial(_SelfAction);
|
|
}
|
|
|
|
std::vector<std::string> AISHEETS::CAIAction::getMultiLineInfoString() const
|
|
{
|
|
std::vector<std::string> container;
|
|
|
|
pushTitle(container, "AISHEETS::CAIAction");
|
|
pushEntry(container, "ai_action sheet");
|
|
pushFooter(container);
|
|
|
|
return container;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CActionList //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void AISHEETS::CActionList::computeAbilities()
|
|
{
|
|
_HasNormalAction = false;
|
|
_HasSelfAction = false;
|
|
FOREACH(itAction, std::vector<IAIActionCPtr>, _Actions)
|
|
{
|
|
IAIActionCPtr const& action = *itAction;
|
|
if (!action.isNull() && action->SelfAction())
|
|
_HasSelfAction = true;
|
|
else
|
|
_HasNormalAction = true;
|
|
}
|
|
}
|
|
|
|
void AISHEETS::CActionList::readGeorges(NLMISC::CSmartPtr<NLGEORGES::UForm> const& form, NLMISC::CSheetId const& sheetId)
|
|
{
|
|
NLGEORGES::UFormElm const& item = form->getRootNode();
|
|
// the form was found so read the true values from George
|
|
_SheetId = sheetId;
|
|
|
|
{
|
|
NLGEORGES::UFormElm const* actionListNode = NULL;
|
|
item.getNodeByName(&actionListNode, "actions");
|
|
|
|
if (actionListNode)
|
|
{
|
|
uint arraySize = 0;
|
|
actionListNode->getArraySize(arraySize);
|
|
for (uint i=0; i<arraySize; ++i)
|
|
{
|
|
std::string action;
|
|
actionListNode->getArrayValue(action, i);
|
|
if (!action.empty())
|
|
{
|
|
addAction(NLMISC::CSheetId(action), action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
computeAbilities();
|
|
}
|
|
|
|
uint AISHEETS::CActionList::getVersion()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
void AISHEETS::CActionList::serial(NLMISC::IStream& s)
|
|
{
|
|
s.serial(_SheetId);
|
|
|
|
if (s.isReading())
|
|
{
|
|
uint32 nbSheet;
|
|
s.serial(nbSheet);
|
|
for (uint32 i=0; i<nbSheet; ++i)
|
|
{
|
|
NLMISC::CSheetId sheetId;
|
|
s.serial(sheetId);
|
|
addAction(sheetId, std::string());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32 nbSheet = (uint32)_Actions.size();
|
|
s.serial(nbSheet);
|
|
for (uint32 i=0; i<nbSheet; ++i)
|
|
{
|
|
NLMISC::CSheetId sheetId = _Actions[i]->SheetId();
|
|
s.serial(sheetId);
|
|
}
|
|
}
|
|
computeAbilities();
|
|
}
|
|
|
|
void AISHEETS::CActionList::addAction(NLMISC::CSheetId const& sheetId, std::string const& actionName)
|
|
{
|
|
IAIActionCPtr action = CSheets::getInstance()->lookupAction(sheetId);
|
|
|
|
if (!action.isNull())
|
|
{
|
|
_Actions.push_back(action);
|
|
}
|
|
else
|
|
{
|
|
if (!actionName.empty())
|
|
nlwarning("action %s doesnt exist", actionName.c_str());
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> AISHEETS::CActionList::getMultiLineInfoString() const
|
|
{
|
|
std::vector<std::string> container;
|
|
|
|
pushTitle(container, "AISHEETS::CActionList");
|
|
pushEntry(container, "action_list sheet");
|
|
pushFooter(container);
|
|
|
|
return container;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CGroupProperties //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
AISHEETS::CGroupProperties::CGroupProperties()
|
|
: _Assist(false)
|
|
, _Attack(false)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CCreature //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
AISHEETS::CCreature::CCreature()
|
|
: _Level(1)
|
|
, _Radius(0.5f), _Height(2.0f), _Width(1.0f), _Length(1.0f)
|
|
, _BoundingRadius(0.5)
|
|
, _BonusAggroHungry(0.0), _BonusAggroVeryHungry(0.0)
|
|
, _AssistDist(10)
|
|
, _MinFightDist(0)
|
|
, _FactionIndex(CStaticFames::INVALID_FACTION_INDEX)
|
|
, _FameForGuardAttack(ICreature::InvalidFameForGuardAttack)
|
|
, _GroupPropertiesIndex(0)
|
|
, _DynamicGroupCountMultiplier(1)
|
|
{
|
|
}
|
|
|
|
void AISHEETS::CCreature::calcFightAndVisualValues(std::string* left, std::string* right)
|
|
{
|
|
CVisualSlotManager* visualSlotManager = CVisualSlotManager::getInstance();
|
|
#ifdef NL_DEBUG
|
|
nlassert(visualSlotManager);
|
|
#endif
|
|
uint32 leftAsInt = visualSlotManager->leftItem2Index(LeftItem());
|
|
uint32 rightAsInt = visualSlotManager->rightItem2Index(RightItem());
|
|
|
|
if (LeftItem()!=NLMISC::CSheetId::Unknown && leftAsInt==0 && left!=NULL)
|
|
{
|
|
if (left->size() < 3 || left->at(3) != 'p')
|
|
nlwarning("Left item '%s' not allowed, if ammo, this is normal", left->c_str());
|
|
}
|
|
|
|
if (RightItem()!=NLMISC::CSheetId::Unknown && rightAsInt==0 && right!=NULL)
|
|
{
|
|
if (right->size() < 3 || right->at(3) != 'p')
|
|
nlwarning("Right item '%s' not allowed, if ammo, this is normal", right->c_str());
|
|
}
|
|
_MinFightDist = (!FightConfig(FIGHTCFG_RANGE).isNULL()||!FightConfig(FIGHTCFG_NUKE).isNULL())?20:0;
|
|
}
|
|
|
|
void AISHEETS::CCreature::parseFightConfig(NLGEORGES::UForm const* form, std::string const& fightConfigString, uint32 actionListIndex, NLMISC::CDbgPtr<CActionList>& fightConfig)
|
|
{
|
|
NLGEORGES::UFormElm const* actionListNode = NULL;
|
|
const_cast<NLGEORGES::UFormElm&>(form->getRootNode()).getNodeByName(&actionListNode, fightConfigString.c_str());
|
|
|
|
if (actionListNode)
|
|
{
|
|
uint arraySize = 0;
|
|
actionListNode->getArraySize(arraySize);
|
|
|
|
if (actionListIndex<arraySize)
|
|
{
|
|
std::string actionListFileName;
|
|
actionListNode->getArrayValue(actionListFileName,actionListIndex);
|
|
addActionConfig (actionListFileName, fightConfig);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AISHEETS::CCreature::readFightConfig(NLMISC::IStream& s, NLMISC::CDbgPtr<CActionList>& fightConfig)
|
|
{
|
|
NLMISC::CSheetId sheetId;
|
|
s.serial(sheetId);
|
|
if (sheetId!=NLMISC::CSheetId::Unknown)
|
|
addActionConfig(sheetId, fightConfig);
|
|
}
|
|
|
|
void AISHEETS::CCreature::saveFightConfig(NLMISC::IStream& s, NLMISC::CDbgPtr<CActionList>& fightConfig)
|
|
{
|
|
if (!fightConfig.isNULL())
|
|
{
|
|
s.serial(fightConfig->_SheetId);
|
|
}
|
|
else
|
|
{
|
|
NLMISC::CSheetId id;
|
|
s.serial(id);
|
|
}
|
|
}
|
|
|
|
bool AISHEETS::CCreature::mustAssist(CCreature const& creature) const
|
|
{
|
|
return getPropertiesCst(creature.GroupPropertiesIndex()).assist();
|
|
}
|
|
|
|
void AISHEETS::CCreature::setAssisGroupIndexs()
|
|
{
|
|
_GroupPropertiesIndex = CSheets::getInstance()->getGroupPropertiesIndex(GroupIndexStr());
|
|
if (_GroupPropertiesIndex==~0)
|
|
return;
|
|
|
|
std::vector<uint32> groupList;
|
|
getGroupStr(groupList, AssistGroupIndexStr());
|
|
|
|
FOREACH(it, std::vector<uint32>, groupList)
|
|
getProperties(*it).setAssist(true);
|
|
}
|
|
|
|
void AISHEETS::CCreature::setAttackGroupIndexs()
|
|
{
|
|
_GroupPropertiesIndex = CSheets::getInstance()->getGroupPropertiesIndex(GroupIndexStr());
|
|
if (_GroupPropertiesIndex==~0)
|
|
return;
|
|
|
|
std::vector<uint32> groupList;
|
|
getGroupStr(groupList, AttackGroupIndexStr());
|
|
|
|
FOREACH(it, std::vector<uint32>, groupList)
|
|
getProperties(*it).setAttack(true);
|
|
}
|
|
|
|
void AISHEETS::CCreature::addActionConfig(std::string const& sheetIdName, NLMISC::CDbgPtr<CActionList>& actionConfigList)
|
|
{
|
|
if (sheetIdName.empty())
|
|
{
|
|
nlwarning("sheetIdName is empty");
|
|
return;
|
|
}
|
|
|
|
if (!addActionConfig(NLMISC::CSheetId(sheetIdName), actionConfigList))
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlwarning("Error in actionConfig Reference for %s", sheetIdName.c_str());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool AISHEETS::CCreature::addActionConfig(NLMISC::CSheetId const& sheetId, NLMISC::CDbgPtr<CActionList>& actionConfigList)
|
|
{
|
|
CActionList const* actionConfig = CSheets::getInstance()->lookupActionList(sheetId);
|
|
if (actionConfig)
|
|
{
|
|
actionConfigList = actionConfig; // fightConfigList.push_back(actionConfig);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AISHEETS::CGroupProperties& AISHEETS::CCreature::getProperties(uint32 groupIndex)
|
|
{
|
|
#if !FINAL_VERSION
|
|
nlassert(groupIndex!=~0);
|
|
#endif
|
|
if (_GroupPropertiesTbl.size()<=groupIndex && groupIndex!=~0)
|
|
{
|
|
uint32 const resizeSize = std::max((uint32)CSheets::getInstance()->_NameToGroupIndex.size(), (uint32)(groupIndex+1));
|
|
_GroupPropertiesTbl.resize(resizeSize);
|
|
}
|
|
return _GroupPropertiesTbl[groupIndex];
|
|
}
|
|
|
|
AISHEETS::CGroupProperties const& AISHEETS::CCreature::getPropertiesCst(uint32 groupIndex) const
|
|
{
|
|
if (groupIndex<_GroupPropertiesTbl.size())
|
|
return _GroupPropertiesTbl[groupIndex];
|
|
else
|
|
return CSheets::getInstance()->_DefaultGroupProp;
|
|
}
|
|
|
|
std::vector<std::string> AISHEETS::CCreature::getMultiLineInfoString() const
|
|
{
|
|
std::vector<std::string> container;
|
|
|
|
pushTitle(container, "AISHEETS::CCreature");
|
|
pushEntry(container, "sheet=" + SheetId().toString());
|
|
pushEntry(container, "level=" + toString("%d", Level()));
|
|
container.back() += "radii=" + toString("%4.1f,%4.1f", Radius(), BoundingRadius());
|
|
container.back() += "height=" + toString("%4.1f", Height());
|
|
pushFooter(container);
|
|
|
|
return container;
|
|
}
|
|
|
|
void AISHEETS::CCreature::readGeorges(NLMISC::CSmartPtr<NLGEORGES::UForm> const& form, NLMISC::CSheetId const& sheetId)
|
|
{
|
|
NLGEORGES::UFormElm const& item = form->getRootNode();
|
|
|
|
// the form was found so read the true values from George
|
|
_SheetId = sheetId;
|
|
#ifdef NL_DEBUG
|
|
nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet));
|
|
#endif
|
|
|
|
item.getValueByName(_Level,"Basics.Level");
|
|
|
|
if (!item.getValueByName(_DynamicGroupCountMultiplier, "Basics.Characteristics.DynGroupCountMultiplier"))
|
|
_DynamicGroupCountMultiplier = 1;
|
|
|
|
item.getValueByName(_ColorHead,"Basics.Equipment.Head.Color");
|
|
item.getValueByName(_ColorArms,"Basics.Equipment.Arms.Color");
|
|
item.getValueByName(_ColorHands,"Basics.Equipment.Hands.Color");
|
|
item.getValueByName(_ColorBody,"Basics.Equipment.Body.Color");
|
|
item.getValueByName(_ColorLegs,"Basics.Equipment.Legs.Color");
|
|
item.getValueByName(_ColorFeets,"Basics.Equipment.Feet.Color");
|
|
|
|
item.getValueByName(_Radius,"Collision.CollisionRadius");
|
|
item.getValueByName(_Height,"Collision.Height");
|
|
item.getValueByName(_Width,"Collision.Width");
|
|
item.getValueByName(_Length,"Collision.Length");
|
|
item.getValueByName(_BoundingRadius,"Collision.BoundingRadius");
|
|
|
|
item.getValueByName(_NotTraversable, "Collision.NotTraversable");
|
|
|
|
item.getValueByName(_BonusAggroHungry,"Combat.BonusAggroHungry");
|
|
item.getValueByName(_BonusAggroVeryHungry,"Combat.BonusAggroVeryHungry");
|
|
|
|
item.getValueByName(_AggroRadiusNotHungry,"Combat.AggroRadiusNotHungry");
|
|
item.getValueByName(_AggroRadiusHungry,"Combat.AggroRadiusHungry");
|
|
item.getValueByName(_AggroRadiusHunting,"Combat.AggroRadiusHunting");
|
|
|
|
if (!item.getValueByName(_AggroReturnDistCheck,"Combat.AggroReturnDistCheck"))
|
|
_AggroReturnDistCheck = -1.f;
|
|
if (!item.getValueByName(_AggroRadiusD1,"Combat.AggroRadiusD1"))
|
|
_AggroRadiusD1 = -1.f;
|
|
if (!item.getValueByName(_AggroRadiusD2,"Combat.AggroRadiusD2"))
|
|
_AggroRadiusD2 = -1.f;
|
|
if (!item.getValueByName(_AggroPrimaryGroupDist,"Combat.AggroPrimaryGroupDist"))
|
|
_AggroPrimaryGroupDist = -1.f;
|
|
if (!item.getValueByName(_AggroPrimaryGroupCoef,"Combat.AggroPrimaryGroupCoef"))
|
|
_AggroPrimaryGroupCoef = -1.f;
|
|
if (!item.getValueByName(_AggroSecondaryGroupDist,"Combat.AggroSecondaryGroupDist"))
|
|
_AggroSecondaryGroupDist = -1.f;
|
|
if (!item.getValueByName(_AggroSecondaryGroupCoef,"Combat.AggroSecondaryGroupCoef"))
|
|
_AggroSecondaryGroupCoef = -1.f;
|
|
if (!item.getValueByName(_AggroPropagationRadius,"Combat.AggroPropagationRadius"))
|
|
_AggroPropagationRadius = -1.f;
|
|
|
|
item.getValueByName(_AssistDist,"Combat.AssistDist");
|
|
|
|
item.getValueByName(_Scale, "3d data.Scale");
|
|
{
|
|
std::string faunaTypeStr;
|
|
item.getValueByName(faunaTypeStr, "Basics.type");
|
|
_FaunaType = getType<TFaunaType>(faunaTypeStr.c_str());
|
|
}
|
|
|
|
item.getValueByName(_ForceDisplayCreatureName, "3d data.ForceDisplayCreatureName");
|
|
|
|
// Get the dist fromm Bip to Mid
|
|
float tmpBip01ToMid;
|
|
if (!item.getValueByName(tmpBip01ToMid, "Collision.Dist Bip01 to mid"))
|
|
tmpBip01ToMid = 0.f;
|
|
// Get the distance from the bip01 to the front.
|
|
if (!item.getValueByName(_DistToFront, "Collision.Dist Bip01 to front"))
|
|
_DistToFront = 1.f;
|
|
// Get the distance from the bip01 to the front.
|
|
if (!item.getValueByName(_DistToBack, "Collision.Dist Bip01 to back"))
|
|
_DistToBack = 1.f;
|
|
// Get the creature Width.
|
|
if (!item.getValueByName(_DistToSide, "Collision.Width"))
|
|
_DistToSide = 1.f;
|
|
|
|
_DistToFront = _DistToFront-tmpBip01ToMid;
|
|
_DistToBack = tmpBip01ToMid-_DistToBack;
|
|
_DistToSide = _DistToSide/2.f;
|
|
|
|
_DistToFront *= _Scale;
|
|
_DistToBack *= _Scale;
|
|
_DistToSide *= _Scale;
|
|
|
|
if (!item.getValueByName(_DistModulator, "Combat.DistModulator"))
|
|
_DistModulator = 0.5f; // (0) - (1) - (n).
|
|
|
|
if (!item.getValueByName(_TargetModulator, "Combat.TargetModulator"))
|
|
_TargetModulator = 1.f; // (0) - (1).
|
|
|
|
if (!item.getValueByName(_ScoreModulator, "Combat.ScoreModulator"))
|
|
_ScoreModulator = 0.01f; // (0) - (1).
|
|
|
|
if (!item.getValueByName(_FearModulator, "Combat.FearModulator"))
|
|
_FearModulator = 0.01f; // (0) - (1).
|
|
|
|
if (!item.getValueByName(_LifeLevelModulator, "Combat.LifeLevelModulator"))
|
|
_LifeLevelModulator = 0.5f; // (0) - (1).
|
|
|
|
if (!item.getValueByName(_CourageModulator, "Combat.CourageModulator"))
|
|
_CourageModulator = 2.f; // (-n) - (0) - (+n).
|
|
|
|
if (!item.getValueByName(_GroupCohesionModulator, "Combat.GroupCohesionModulator"))
|
|
_GroupCohesionModulator = 0.5f; // (0) - (1)
|
|
|
|
if (!item.getValueByName(_GroupDispersion, "Basics.MovementSpeeds.GroupDispersion"))
|
|
_GroupDispersion = 0.5f; // (0) - (1).
|
|
|
|
if (!item.getValueByName(_XPLevel, "Basics.XPLevel"))
|
|
_XPLevel = 1;
|
|
|
|
if (!item.getValueByName(_NbPlayers, "Basics.NbPlayers"))
|
|
_NbPlayers = 1;
|
|
|
|
nlassert(_DistModulator>=0);
|
|
nlassert(_TargetModulator>=0);
|
|
nlassert(_ScoreModulator>=0 && _ScoreModulator<=1);
|
|
nlassert(_FearModulator>=0 && _FearModulator<=1);
|
|
nlassert(_LifeLevelModulator>=0 && _LifeLevelModulator<=1);
|
|
nlassert(_GroupCohesionModulator>=0 && _GroupCohesionModulator<=1);
|
|
nlassert(_GroupDispersion>=0 && _GroupDispersion<=1);
|
|
|
|
_EnergyValue = uint32(0.01f * ENERGY_SCALE);
|
|
float v;
|
|
if (item.getValueByName(v, "Basics.Characteristics.DynamicEnergyValue") && v!=0)
|
|
_EnergyValue = uint32(v * ENERGY_SCALE);
|
|
|
|
if (!item.getValueByName(_CanTurn, "Properties.Turn"))
|
|
_CanTurn = true;
|
|
|
|
uint32 meleeConfigChoice = 0;
|
|
uint32 rangeConfigChoice = 0;
|
|
uint32 nukeConfigChoice = 0;
|
|
uint32 healConfigChoice = 0;
|
|
|
|
breakable
|
|
{
|
|
std::string actionConfigStr;
|
|
item.getValueByName(actionConfigStr, "action_cfg");
|
|
|
|
if (actionConfigStr.length()!=5) // 4numbers + "f".
|
|
break;
|
|
|
|
char a[2] = "0";
|
|
a[0] = actionConfigStr[0];
|
|
meleeConfigChoice = atol(a);
|
|
a[0] = actionConfigStr[1];
|
|
rangeConfigChoice = atol(a);
|
|
a[0] = actionConfigStr[2];
|
|
nukeConfigChoice = atol(a);
|
|
a[0] = actionConfigStr[3];
|
|
healConfigChoice = atol(a);
|
|
}
|
|
|
|
static std::string meleeFightConfigString("melee_cfg");
|
|
static std::string rangeFightConfigString("range_cfg");
|
|
static std::string nukeFightConfigString("nuke_cfg");
|
|
static std::string healFightConfigString("heal_cfg");
|
|
|
|
if (meleeConfigChoice>0)
|
|
parseFightConfig(form, meleeFightConfigString, meleeConfigChoice-1, _FightConfig[FIGHTCFG_MELEE]);
|
|
if (rangeConfigChoice>0)
|
|
parseFightConfig(form, rangeFightConfigString, rangeConfigChoice-1, _FightConfig[FIGHTCFG_RANGE]);
|
|
if (nukeConfigChoice>0)
|
|
parseFightConfig(form, nukeFightConfigString, nukeConfigChoice-1, _FightConfig[FIGHTCFG_NUKE]);
|
|
if (healConfigChoice>0)
|
|
parseFightConfig(form, healFightConfigString, healConfigChoice-1, _FightConfig[FIGHTCFG_HEAL]);
|
|
|
|
// reads left & right item.
|
|
{
|
|
std::string left;
|
|
item.getValueByName(left, "item_left");
|
|
if (!left.empty())
|
|
_LeftItem = NLMISC::CSheetId(left);
|
|
|
|
std::string right;
|
|
item.getValueByName(right, "item_right");
|
|
if (!right.empty())
|
|
_RightItem = NLMISC::CSheetId(right);
|
|
|
|
calcFightAndVisualValues(&left, &right);
|
|
}
|
|
|
|
std::string s;
|
|
if (item.getValueByName(s, "Basics.Fame"))
|
|
{
|
|
_FactionIndex = CStaticFames::getInstance().getFactionIndex(s);
|
|
}
|
|
if (item.getValueByName(s, "Basics.FameForGuardAttack") && !s.empty())
|
|
{
|
|
double tmp;
|
|
sscanf(s.c_str(), "%f", &tmp);
|
|
_FameForGuardAttack = (sint32)tmp;
|
|
}
|
|
else
|
|
_FameForGuardAttack = ICreature::InvalidFameForGuardAttack;
|
|
|
|
// Assist Group Indexs.
|
|
{
|
|
item.getValueByName(_GroupIndexStr,"group_id");
|
|
if (_GroupIndexStr.empty())
|
|
{
|
|
std::string cat;
|
|
std::string raceCode;
|
|
if (item.getValueByName(cat, "category") && item.getValueByName(raceCode, "race_code"))
|
|
_GroupIndexStr = cat + raceCode;
|
|
}
|
|
|
|
item.getValueByName(_AssistGroupIndexStr, "group_assist");
|
|
setAssisGroupIndexs();
|
|
item.getValueByName(_AttackGroupIndexStr, "group_attack");
|
|
setAttackGroupIndexs();
|
|
}
|
|
|
|
// Bot name
|
|
item.getValueByName(_BotName, "Basics.BotName");
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Reads Script Comps.
|
|
breakable
|
|
{
|
|
NLGEORGES::UFormElm const* scriptCompNode = NULL;
|
|
const_cast<NLGEORGES::UFormElm&>(form->getRootNode()).getNodeByName(&scriptCompNode, "special_comp");
|
|
|
|
if (!scriptCompNode)
|
|
break;
|
|
|
|
uint arraySize = 0;
|
|
scriptCompNode->getArraySize(arraySize);
|
|
|
|
for (uint arrayIndex=0; arrayIndex<arraySize; ++arrayIndex)
|
|
{
|
|
std::string scriptCompStr;
|
|
scriptCompNode->getArrayValue(scriptCompStr, arrayIndex);
|
|
|
|
CFightScriptComp* scriptComp;
|
|
try
|
|
{
|
|
scriptComp = CFightScriptCompReader::createScriptComp(scriptCompStr);
|
|
registerScriptComp(scriptComp);
|
|
}
|
|
catch (ReadFightActionException& ex)
|
|
{
|
|
nlwarning("script read error (ignored): %s", ex.what());
|
|
}
|
|
}
|
|
}
|
|
// Creature race
|
|
breakable
|
|
{
|
|
string raceStr;
|
|
if(item.getValueByName(raceStr, "Basics.Race") && !raceStr.empty())
|
|
_Race = EGSPD::CPeople::fromString(raceStr);
|
|
else
|
|
_Race = EGSPD::CPeople::Unknown;
|
|
}
|
|
}
|
|
|
|
void AISHEETS::CCreature::registerScriptComp(CFightScriptComp* scriptComp)
|
|
{
|
|
_ScriptCompList.push_back(scriptComp);
|
|
|
|
CFightSelectFilter* filter = dynamic_cast<CFightSelectFilter*>(scriptComp);
|
|
if (!filter)
|
|
return;
|
|
|
|
std::string const& param = filter->getParam();
|
|
if (param=="ON_UPDATE")
|
|
_UpdateScriptList.push_back(scriptComp);
|
|
if (param=="ON_DEATH")
|
|
_DeathScriptList.push_back(scriptComp);
|
|
if (param=="ON_BIRTH")
|
|
_BirthScriptList.push_back(scriptComp);
|
|
}
|
|
|
|
|
|
uint AISHEETS::CCreature::getVersion()
|
|
{
|
|
return 45;
|
|
}
|
|
|
|
void AISHEETS::CCreature::serial(NLMISC::IStream &s)
|
|
{
|
|
s.serial(_SheetId, _Level);
|
|
#ifdef NL_DEBUG
|
|
nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet));
|
|
#endif
|
|
|
|
s.serial(_DynamicGroupCountMultiplier);
|
|
s.serial(_ColorHead, _ColorArms, _ColorHands),
|
|
s.serial(_ColorBody, _ColorLegs, _ColorFeets);
|
|
|
|
s.serial(_Radius, _Height, _Width, _Length);
|
|
s.serial(_BoundingRadius);
|
|
s.serial(_BonusAggroHungry, _BonusAggroVeryHungry);
|
|
s.serial(_AggroRadiusNotHungry, _AggroRadiusHungry, _AggroRadiusHunting);
|
|
s.serial(_AggroReturnDistCheck);
|
|
s.serial(_AggroRadiusD1, _AggroRadiusD2);
|
|
s.serial(_AggroPrimaryGroupDist);
|
|
s.serial(_AggroPrimaryGroupCoef);
|
|
s.serial(_AggroSecondaryGroupDist);
|
|
s.serial(_AggroSecondaryGroupCoef);
|
|
s.serial(_AggroPropagationRadius);
|
|
s.serial(_Scale);
|
|
s.serial(_ForceDisplayCreatureName);
|
|
s.serialEnum(_FaunaType);
|
|
|
|
s.serial(_DistToFront);
|
|
s.serial(_DistToBack);
|
|
s.serial(_DistToSide);
|
|
|
|
s.serial(_DistModulator);
|
|
s.serial(_TargetModulator);
|
|
s.serial(_ScoreModulator);
|
|
s.serial(_FearModulator);
|
|
s.serial(_LifeLevelModulator);
|
|
s.serial(_CourageModulator);
|
|
s.serial(_GroupCohesionModulator);
|
|
|
|
s.serial(_GroupDispersion);
|
|
|
|
s.serial(_XPLevel);
|
|
s.serial(_NbPlayers);
|
|
|
|
s.serial(_EnergyValue);
|
|
|
|
s.serial(_CanTurn);
|
|
|
|
if (s.isReading())
|
|
{
|
|
readFightConfig(s, _FightConfig[FIGHTCFG_MELEE]);
|
|
readFightConfig(s, _FightConfig[FIGHTCFG_RANGE]);
|
|
readFightConfig(s, _FightConfig[FIGHTCFG_NUKE]);
|
|
readFightConfig(s, _FightConfig[FIGHTCFG_HEAL]);
|
|
}
|
|
else
|
|
{
|
|
saveFightConfig(s, _FightConfig[FIGHTCFG_MELEE]);
|
|
saveFightConfig(s, _FightConfig[FIGHTCFG_RANGE]);
|
|
saveFightConfig(s, _FightConfig[FIGHTCFG_NUKE]);
|
|
saveFightConfig(s, _FightConfig[FIGHTCFG_HEAL]);
|
|
}
|
|
|
|
s.serial(_AssistDist);
|
|
|
|
// serialize left & right item.
|
|
s.serial(_LeftItem, _RightItem);
|
|
s.serial(_FactionIndex);
|
|
s.serial(_FameForGuardAttack);
|
|
|
|
s.serial(_GroupIndexStr);
|
|
s.serial(_AssistGroupIndexStr);
|
|
s.serial(_AttackGroupIndexStr);
|
|
|
|
s.serial(_BotName);
|
|
s.serialEnum(_Race);
|
|
|
|
if (s.isReading())
|
|
{
|
|
setAssisGroupIndexs();
|
|
setAttackGroupIndexs();
|
|
}
|
|
|
|
if (s.isReading())
|
|
{
|
|
uint32 nbScript;
|
|
s.serial(nbScript);
|
|
for (uint32 index=0; index<nbScript; ++index)
|
|
{
|
|
string scriptCompStr;
|
|
s.serial(scriptCompStr);
|
|
|
|
CFightScriptComp* scriptComp;
|
|
try
|
|
{
|
|
scriptComp = CFightScriptCompReader::createScriptComp(scriptCompStr);
|
|
registerScriptComp(scriptComp);
|
|
}
|
|
catch (ReadFightActionException& ex)
|
|
{
|
|
nlwarning("script read error (ignored): %s", ex.what());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32 nbScript = (uint32)_ScriptCompList.size();
|
|
s.serial(nbScript);
|
|
for (uint32 index=0; index<nbScript; ++index)
|
|
{
|
|
string str = _ScriptCompList[index]->toString();
|
|
s.serial(str);
|
|
}
|
|
}
|
|
|
|
calcFightAndVisualValues();
|
|
}
|
|
|
|
void AISHEETS::CCreature::getGroupStr(std::vector<uint32>& groupIndexStrList, std::string const& groupIndexStr)
|
|
{
|
|
if (groupIndexStr.empty())
|
|
return;
|
|
|
|
size_t firstIndex = 0;
|
|
size_t lastIndex = firstIndex - 1;
|
|
|
|
do
|
|
{
|
|
firstIndex = lastIndex + 1;
|
|
lastIndex = groupIndexStr.find_first_of(',',firstIndex);
|
|
|
|
std::string str;
|
|
if (lastIndex==std::string::npos)
|
|
str = groupIndexStr.substr(firstIndex, groupIndexStr.size()-firstIndex);
|
|
else
|
|
str = groupIndexStr.substr(firstIndex, lastIndex-firstIndex);
|
|
|
|
uint32 const otherGroupIndex = CSheets::getInstance()->getGroupPropertiesIndex(str);
|
|
if (otherGroupIndex!=~0)
|
|
groupIndexStrList.push_back(otherGroupIndex);
|
|
} while (lastIndex!=std::string::npos);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CRaceStats //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void AISHEETS::CRaceStats::readGeorges(NLMISC::CSmartPtr<NLGEORGES::UForm> const& form, NLMISC::CSheetId const& sheetId)
|
|
{
|
|
NLGEORGES::UFormElm const& item = form->getRootNode();
|
|
|
|
// the form was found so read the true values from George
|
|
_SheetId = sheetId;
|
|
#ifdef NL_DEBUG
|
|
nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet));
|
|
#endif
|
|
|
|
item.getValueByName(_Race, "Race");
|
|
}
|
|
|
|
uint AISHEETS::CRaceStats::getVersion()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
void AISHEETS::CRaceStats::serial(NLMISC::IStream &s)
|
|
{
|
|
s.serial(_SheetId);
|
|
#ifdef NL_DEBUG
|
|
nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet));
|
|
#endif
|
|
|
|
s.serial(_SheetId);
|
|
s.serial(_Race);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CSheets //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
AISHEETS::CSheets* AISHEETS::CSheets::_Instance = NULL;
|
|
|
|
AISHEETS::CSheets* AISHEETS::CSheets::getInstance()
|
|
{
|
|
if (!_Instance)
|
|
_Instance = new AISHEETS::CSheets;
|
|
return _Instance;
|
|
}
|
|
|
|
void AISHEETS::CSheets::destroyInstance()
|
|
{
|
|
delete _Instance;
|
|
_Instance = NULL;
|
|
}
|
|
|
|
AISHEETS::CSheets::CSheets()
|
|
: _Initialised(false)
|
|
{
|
|
}
|
|
|
|
void AISHEETS::CSheets::init()
|
|
{
|
|
if (_Initialised)
|
|
return;
|
|
|
|
_PlayerGroupIndex=getGroupPropertiesIndex("zp");
|
|
#if !FINAL_VERSION
|
|
nlassert(_PlayerGroupIndex!=~0);
|
|
#endif
|
|
|
|
CConfigFile::CVar *varPtr=IService::getInstance()->ConfigFile.getVarPtr(std::string("GeorgePaths"));
|
|
const std::string writeFilesDirectoryName=IService::getInstance()->WriteFilesDirectory.toString();
|
|
|
|
// if config file variable 'GeorgePaths' exists then only do a minimal loadForms otherwise do the full works
|
|
if (varPtr!=NULL)
|
|
{
|
|
|
|
bool addSearchPath=false;
|
|
|
|
loadForm2("aiaction", writeFilesDirectoryName+AISPackedActionSheetsFilename, _ActionSheets, false, false);
|
|
if (_ActionSheets.empty())
|
|
{
|
|
if (!addSearchPath)
|
|
{
|
|
addSearchPath=true;
|
|
for (uint32 i=0;i<varPtr->size();++i)
|
|
CPath::addSearchPath(varPtr->asString(i).c_str(), true, false);
|
|
}
|
|
loadForm2("aiaction", writeFilesDirectoryName+AISPackedActionSheetsFilename, _ActionSheets, true);
|
|
}
|
|
|
|
loadForm("actionlist", writeFilesDirectoryName+AISPackedFightConfigSheetsFilename, _ActionListSheets, false, false);
|
|
if (_ActionListSheets.empty())
|
|
{
|
|
if (!addSearchPath)
|
|
{
|
|
addSearchPath=true;
|
|
for (uint32 i=0;i<varPtr->size();++i)
|
|
CPath::addSearchPath(varPtr->asString(i).c_str(), true, false);
|
|
}
|
|
loadForm("actionlist", writeFilesDirectoryName+AISPackedFightConfigSheetsFilename, _ActionListSheets, true);
|
|
}
|
|
|
|
|
|
loadForm2("creature", writeFilesDirectoryName+AISPackedSheetsFilename, _Sheets, false, false);
|
|
if (_Sheets.empty())
|
|
{
|
|
if (!addSearchPath)
|
|
{
|
|
addSearchPath=true;
|
|
for (uint32 i=0;i<varPtr->size();++i)
|
|
CPath::addSearchPath(varPtr->asString(i).c_str(), true, false);
|
|
}
|
|
loadForm2("creature", writeFilesDirectoryName+AISPackedSheetsFilename, _Sheets, true);
|
|
}
|
|
|
|
loadForm2("race_stats", writeFilesDirectoryName+AISPackedRaceStatsSheetsFilename, _RaceStatsSheets, false, false);
|
|
if (_RaceStatsSheets.empty())
|
|
{
|
|
if (!addSearchPath)
|
|
{
|
|
addSearchPath=true;
|
|
for (uint32 i=0;i<varPtr->size();++i)
|
|
CPath::addSearchPath(varPtr->asString(i).c_str(), true, false);
|
|
}
|
|
loadForm2("race_stats", writeFilesDirectoryName+AISPackedRaceStatsSheetsFilename, _RaceStatsSheets, true);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
loadForm2("aiaction", writeFilesDirectoryName+AISPackedActionSheetsFilename, _ActionSheets, true);
|
|
loadForm("actionlist", writeFilesDirectoryName+AISPackedFightConfigSheetsFilename, _ActionListSheets, true);
|
|
loadForm2("creature", writeFilesDirectoryName+AISPackedSheetsFilename, _Sheets, true);
|
|
loadForm2("race_stats", writeFilesDirectoryName+AISPackedRaceStatsSheetsFilename, _RaceStatsSheets, true);
|
|
}
|
|
|
|
_Initialised=true;
|
|
}
|
|
|
|
void AISHEETS::CSheets::release()
|
|
{
|
|
_Sheets.clear();
|
|
_ActionListSheets.clear();
|
|
_ActionSheets.clear();
|
|
_RaceStatsSheets.clear();
|
|
}
|
|
|
|
uint32 AISHEETS::CSheets::getGroupPropertiesIndex(std::string groupIndexName)
|
|
{
|
|
if (groupIndexName.empty())
|
|
return ~0;
|
|
|
|
NLMISC::strupr(groupIndexName);
|
|
std::map<string, uint32>::iterator it = _NameToGroupIndex.find(groupIndexName);
|
|
if (it==_NameToGroupIndex.end())
|
|
{
|
|
uint32 groupIndex = (uint32)_NameToGroupIndex.size();
|
|
_NameToGroupIndex.insert(make_pair(groupIndexName, groupIndex));
|
|
#if !FINAL_VERSION
|
|
nldebug("GroupIndex Entry: %s %d", groupIndexName.c_str(), groupIndex);
|
|
#endif
|
|
|
|
it = _NameToGroupIndex.find(groupIndexName);
|
|
#ifdef NL_DEBUG
|
|
nlassert(it!=_NameToGroupIndex.end());
|
|
#endif
|
|
// Resize other group table. Better imp should be done with listeners.
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
void AISHEETS::CSheets::display(CSmartPtr<CStringWriter> stringWriter, uint infoSelect)
|
|
{
|
|
nlassert(_Initialised);
|
|
|
|
std::map<CSheetId, AISHEETS::CCreaturePtr>::iterator it;
|
|
for(it=_Sheets.begin(); it!=_Sheets.end(); ++it)
|
|
{
|
|
std::vector<std::string> strings;
|
|
strings = it->second->getMultiLineInfoString();
|
|
FOREACHC(itString, std::vector<std::string>, strings)
|
|
stringWriter->append(toString("%04x", it->second->SheetId().asInt()) + " " + *itString);
|
|
}
|
|
}
|
|
|
|
AISHEETS::ICreatureCPtr AISHEETS::CSheets::lookup(CSheetId const& id)
|
|
{
|
|
// setup an iterator and lookup the sheet id in the map
|
|
std::map<CSheetId, AISHEETS::CCreaturePtr>::iterator it=_Sheets.find(id);
|
|
|
|
// if we found a valid entry return a pointer to the creature record otherwise 0
|
|
if (it!=_Sheets.end())
|
|
return (AISHEETS::CCreature*)it->second;
|
|
else
|
|
{
|
|
nlwarning("Unknow creature sheet '%s'", id.toString().c_str());
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
AISHEETS::IAIActionCPtr AISHEETS::CSheets::lookupAction(CSheetId const& id)
|
|
{
|
|
// setup an iterator and lookup the sheet id in the map
|
|
std::map<CSheetId, CAIActionPtr>::iterator it = _ActionSheets.find(id);
|
|
|
|
// if we found a valid entry return a pointer to the creature record otherwise 0
|
|
if (it!=_ActionSheets.end())
|
|
return (AISHEETS::CAIAction*)it->second;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
AISHEETS::CActionList const* AISHEETS::CSheets::lookupActionList(CSheetId const& id)
|
|
{
|
|
// setup an iterator and lookup the sheet id in the map
|
|
std::map<CSheetId, AISHEETS::CActionList>::iterator it=_ActionListSheets.find(id);
|
|
|
|
// if we found a valid entry return a pointer to the creature record otherwise 0
|
|
if (it!=_ActionListSheets.end())
|
|
return &((*it).second);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
AISHEETS::IRaceStatsCPtr AISHEETS::CSheets::lookupRaceStats(CSheetId const& id)
|
|
{
|
|
// setup an iterator and lookup the sheet id in the map
|
|
std::map<CSheetId, AISHEETS::CRaceStatsPtr>::iterator it=_RaceStatsSheets.find(id);
|
|
|
|
if (it!=_RaceStatsSheets.end())
|
|
return (AISHEETS::CRaceStats*)it->second;
|
|
else
|
|
{
|
|
nlwarning("Unknow race_stats sheet '%s'", id.toString().c_str());
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Console commands //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
NLMISC_COMMAND(displaySheetNames,"display sheet data for all sheets","")
|
|
{
|
|
if(args.size() !=0) return false;
|
|
|
|
AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log),0);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_COMMAND(displaySheetBasics,"display sheet data for all sheets","")
|
|
{
|
|
if(args.size() !=0) return false;
|
|
|
|
AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log),1);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_COMMAND(displaySheetCombat,"display sheet data for all sheets","")
|
|
{
|
|
if(args.size() !=0) return false;
|
|
|
|
AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log),2);
|
|
|
|
return true;
|
|
}
|
|
|
|
NLMISC_COMMAND(displaySheetByName,"display sheet data for given sheets","<sheet> [<sheet>...]")
|
|
{
|
|
if (args.size() <1)
|
|
return false;
|
|
|
|
for (uint i=0;i<args.size();++i)
|
|
{
|
|
// lookup the sheet id
|
|
AISHEETS::ICreatureCPtr sheet = AISHEETS::CSheets::getInstance()->lookup(NLMISC::CSheetId(args[i]));
|
|
if (!sheet)
|
|
{
|
|
log.displayNL("Failed to find sheet: %s",args[0].c_str());
|
|
continue;
|
|
}
|
|
std::vector<std::string> strings = sheet->getMultiLineInfoString();
|
|
FOREACHC(it, std::vector<std::string>, strings)
|
|
log.displayNL("%s", it->c_str());
|
|
}
|
|
return true;
|
|
}
|
|
/*
|
|
NLMISC_COMMAND(setSheetProperty,"change a value read from a sheet","<sheet> level|walk|run|radius|bounding|height|aggro|attack|danger|flight|survive|initSurv|crit <value>")
|
|
{
|
|
if (args.size() !=3)
|
|
return false;
|
|
|
|
// lookup the sheet id
|
|
AISHEETS::CCreature* sheet = const_cast<AISHEETS::CCreature*>(dynamic_cast<AISHEETS::CCreature const*>(AISHEETS::CSheets::getInstance()->lookup(NLMISC::CSheetId(args[0]))));
|
|
if (!sheet)
|
|
{
|
|
log.displayNL("Failed to find sheet: %s",args[0].c_str());
|
|
return false;
|
|
}
|
|
|
|
// get the value
|
|
float val = (float)atof(args[2].c_str());
|
|
if (val==0 && args[2]!="0" && args[2]!="0.0")
|
|
{
|
|
log.displayNL("'%s' is not a valid value",args[2].c_str());
|
|
return false;
|
|
}
|
|
|
|
breakable
|
|
{
|
|
if (nlstricmp(args[1].c_str(),"level")==0) { sheet->_Level=(uint32)val; break; }
|
|
if (nlstricmp(args[1].c_str(),"radius")==0) { sheet->_Radius=val; break; }
|
|
if (nlstricmp(args[1].c_str(),"bounding")==0) { sheet->_BoundingRadius=val; break; }
|
|
if (nlstricmp(args[1].c_str(),"height")==0) { sheet->_Height=val; break; }
|
|
|
|
log.displayNL("variable name not recognised: %s", args[1].c_str());
|
|
return false;
|
|
}
|
|
|
|
std::vector<std::string> strings = sheet->getMultiLineInfoString();
|
|
FOREACHC(it, std::vector<std::string>, strings)
|
|
log.displayNL("%s", it->c_str());
|
|
return true;
|
|
}
|
|
*/
|