// 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 .
// ***************************************************************************
/*
This small tool was made for Graphist, to edit automatically *.animation_set sheets, so anims
get correct MeleeImpactDelay data
It uses an anim.txt file that was generated by inserting code (give at end of this file)
in the client.
This tool had to be "used one time only". hence its crappiest usage :)
*/
#include "nel/misc/path.h"
#include "nel/misc/file.h"
#include "nel/misc/common.h"
#include "nel/misc/algo.h"
using namespace std;
using namespace NLMISC;
// ***************************************************************************
// Config
bool ReplaceExistingMeleeImpactDelay= true;
float MeleeImpactTimeFactor= 0.35f;
// Inited in main()
map StateNameToStateCode;
// ***************************************************************************
class CAnimCombatState
{
public:
void build(const string & line);
// A1, A2 etc...
string StateCode;
// Mean Animation Time of all sub animations of this state
float MeanAnimTime;
public:
bool operator<(const CAnimCombatState &o) const {return StateCode States;
public:
bool operator<(const CAnimCombatSet &o) const {return Name &combatAnimSets)
{
// look if this animSetFile is in the combat list to patch
string shortName= CFile::getFilenameWithoutExtension(animSetFile);
strlwr(shortName);
CAnimCombatSet key;
key.Name= shortName;
set::iterator it= combatAnimSets.find(key);
if(it == combatAnimSets.end())
return;
const CAnimCombatSet ¤tCombatAnimSet= *it;
InfoLog->displayRawNL("patching %s", animSetFile.c_str());
// *** Read the animset file.
CIFile iFile;
iFile.open(animSetFile, true);
// Read all text
static vector animSetText;
animSetText.clear();
while(!iFile.eof())
{
char tmp[50000];
iFile.getline(tmp, 50000);
animSetText.push_back(tmp);
}
iFile.close();
bool someChangeDone= false;
// *** Parse the animSet
{
// For each line of the animSet
sint structLevel= 0;
sint meleeImpactDelayLine= -1;
string currentStateName;
for(uint j=0;j TAg? => stop
if(line.find("")!=string::npos)
break;
// Find a STRUCT start?
if(line.find("")!=string::npos)
{
// if end a state block, may add or replace MeleeDelayImpact
if(structLevel==2 && !currentStateName.empty())
{
// If the state is not in the combat state, no need to patch anything
static CAnimCombatState key;
// must translate for instance "attack1" to "A1"
key.StateCode= StateNameToStateCode[currentStateName];
set::const_iterator it= currentCombatAnimSet.States.find(key);
if(it!=currentCombatAnimSet.States.end())
{
// else take the mean anim time
string format= " ";
string newLine= toString(format.c_str(), it->MeanAnimTime * MeleeImpactTimeFactor);
// melee impact delay doesn't exist?
if(meleeImpactDelayLine==-1)
{
// add just before this line the Melee Impact Atom
animSetText.insert(animSetText.begin()+j, newLine);
j++;
someChangeDone= true;
}
// else exist and want to replace?
else if(ReplaceExistingMeleeImpactDelay)
{
animSetText[meleeImpactDelayLine]= newLine;
someChangeDone= true;
}
}
}
// dec struct level
structLevel--;
}
// if we are in level 2 structure, try to get the line to modify (if exist)
if(structLevel==2)
{
if( line.find("Name=\"MeleeImpactDelay\"")!=string::npos )
meleeImpactDelayLine= j;
}
}
}
// *** Write the animset file.
if(someChangeDone)
{
COFile oFile;
oFile.open(animSetFile, false, true);
// Write all text
for(uint i=0;i combatAnimSets;
CIFile animFile;
if(!animFile.open("anim.txt", true))
{
nlwarning("Can't open anim.txt file. abort");
return 0;
}
else
{
char tmp[5000];
CAnimCombatSet lastAnimSet;
// parse all lines
while(!animFile.eof())
{
animFile.getline(tmp, 5000);
string line= tmp;
if(line.empty())
continue;
// new anim set?
if(line[0]!=' ')
{
// insert the last anim state
if(!lastAnimSet.States.empty())
combatAnimSets.insert(lastAnimSet);
lastAnimSet.States.clear();
lastAnimSet.Name= line;
}
// new anim state?
else if(!lastAnimSet.Name.empty())
{
CAnimCombatState state;
state.build(line);
lastAnimSet.States.insert(state);
}
}
// append the last anim set if needed
if(!lastAnimSet.States.empty())
combatAnimSets.insert(lastAnimSet);
animFile.close();
}
// *** Get the list of .animset to make by race
vector files;
files.clear();
CPath::getPathContent(animSetDir, true, false, true, files);
vector animSetList;
InfoLog->displayRawNL("");
InfoLog->displayRawNL("*****************************");
InfoLog->displayRawNL("**** .animation_set list ****");
InfoLog->displayRawNL("*****************************");
for(uint i=0;idisplayRawNL(animSetList.back().c_str());
}
}
// *** Init StateNameToStateCode
StateNameToStateCode["attack1"]= "A1";
StateNameToStateCode["attack2"]= "A2";
StateNameToStateCode["walk atk"]= "Wa";
StateNameToStateCode["run atk"]= "Ra";
StateNameToStateCode["backward atk"]= "Ba";
StateNameToStateCode["default atk low"]= "Dl";
StateNameToStateCode["default atk middle"]= "Dm";
StateNameToStateCode["default atk high"]= "Dh";
StateNameToStateCode["powerful atk low"]= "Pl";
StateNameToStateCode["powerful atk middle"]= "Pm";
StateNameToStateCode["powerful atk high"]= "Ph";
StateNameToStateCode["area atk low"]= "Al";
StateNameToStateCode["area atk middle"]= "Am";
StateNameToStateCode["area atk high"]= "Ah";
// *** For each animset, test if can replace some anim
InfoLog->displayRawNL("");
InfoLog->displayRawNL("**************************");
InfoLog->displayRawNL("**** Starting Process ****");
InfoLog->displayRawNL("**************************");
for(uint i=0;ibuild();
....
}
** insert here ***
animSet Sheets and 3D anim data (fauna_animations.bnp and characters_animations.bnp) must be up to date
*/
/*
// *************************************
// CODE TO GENERATE MELEE IMPACT DELAY
// *************************************
CFileDisplayer animLog("anim.txt", true);
TAnimStateId walkAtk= CAnimationStateSheet::getAnimationStateId("walk atk");
TAnimStateId runAtk= CAnimationStateSheet::getAnimationStateId("run atk");
TAnimStateId backAtk= CAnimationStateSheet::getAnimationStateId("backward atk");
TAnimStateId stateCombat[]= {CAnimationStateSheet::Attack1, CAnimationStateSheet::Attack2,
walkAtk, runAtk, backAtk,
CAnimationStateSheet::DefaultAtkLow,CAnimationStateSheet::DefaultAtkHigh,
CAnimationStateSheet::DefaultAtkMiddle,CAnimationStateSheet::PowerfulAtkLow,
CAnimationStateSheet::PowerfulAtkHigh,CAnimationStateSheet::PowerfulAtkMiddle,
CAnimationStateSheet::AreaAtkLow,CAnimationStateSheet::AreaAtkHigh,
CAnimationStateSheet::AreaAtkMiddle};
string stateCombatCode[]= {"A1", "A2", "Wa", "Ra", "Ba",
"Dl", "Dh", "Dm", "Pl", "Ph", "Pm", "Al", "Ah", "Am"};
const uint32 nSC= sizeof(stateCombat) / sizeof(stateCombat[0]);
nlctassert(nSC==sizeof(stateCombatCode) / sizeof(stateCombatCode[0]));
{
float roughEval= 0.f;
uint nbRoughEval= 0;
TAnimSet::iterator it= _AnimSet.begin();
for(;it!=_AnimSet.end();it++)
{
CAnimationSet &animSet= it->second;
bool animSetDisplayed= false;
for(uint i=0;i(animSet.getAnimationState(stateCombat[i]));
if(state)
{
// first compute mean and anim name
float mean= 0.f;
uint nbValid= 0;
string animName;
bool extended= false;
for(uint j=0;jgetNumAnimation();j++)
{
CAnimation *anim= state->getAnimationByIndex(j);
NL3D::UAnimation *anim3d= NULL;
if(anim)
anim3d= _AnimationSet->getAnimation(anim->id());
if(anim && anim3d)
{
// name
string name= _AnimationSet->getAnimationName(anim->id());
strlwr(name);
if(animName.empty())
animName= name;
else if(!extended)
{
extended= true;
animName+= ", ...";
}
// meanLength and nb
float timeLen= anim3d->getEndTime()-anim3d->getBeginTime();
mean+= timeLen;
nbValid++;
}
}
if(nbValid)
mean/=nbValid;
// compute standard and max deviation
float stdDev=0.f, maxDev=0.f;
for(uint j=0;jgetNumAnimation();j++)
{
CAnimation *anim= state->getAnimationByIndex(j);
NL3D::UAnimation *anim3d= NULL;
if(anim)
anim3d= _AnimationSet->getAnimation(anim->id());
if(anim && anim3d)
{
float timeLen= anim3d->getEndTime()-anim3d->getBeginTime();
stdDev+= (float)fabs(timeLen - mean);
maxDev= max(maxDev, (float)fabs(timeLen - mean));
}
}
if(nbValid)
stdDev/= nbValid;
// valid?
if(nbValid)
{
// display first animSetName
if(!animSetDisplayed)
{
string msg= toString("%s\n", it->first.c_str() );
animLog.display(CLog::TDisplayInfo(), msg.c_str());
animSetDisplayed= true;
}
// then stats for this state
string msg= toString(" %s: mn%.03f, md%.03f, sd%.03f, ev%.03f (%s)\n", stateCombatCode[i].c_str(),
mean, maxDev, stdDev, mean*0.4f, animName.c_str());
animLog.display(CLog::TDisplayInfo(), msg.c_str());
roughEval+= mean;
nbRoughEval++;
}
}
}
}
if(nbRoughEval)
{
roughEval/= nbRoughEval;
nlinfo(" AnimDBG RoughEval: mn%.03f, ev%.03f",
roughEval, roughEval*0.4f);
}
}
// *************************************
// CODE TO GENERATE MELEE IMPACT DELAY
// *************************************
*/