mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-11-30 15:45:59 +00:00
480 lines
No EOL
14 KiB
C++
480 lines
No EOL
14 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/>.
|
|
|
|
// ***************************************************************************
|
|
/*
|
|
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<string, string> 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<o.StateCode;}
|
|
};
|
|
|
|
class CAnimCombatSet
|
|
{
|
|
public:
|
|
// name of the anim set
|
|
string Name;
|
|
|
|
// set of CAnimCombatState
|
|
set<CAnimCombatState> States;
|
|
|
|
public:
|
|
bool operator<(const CAnimCombatSet &o) const {return Name<o.Name;}
|
|
};
|
|
|
|
|
|
// ***************************************************************************
|
|
void CAnimCombatState::build(const string &line)
|
|
{
|
|
StateCode= line.substr(4, 2);
|
|
string time= line.substr(10, 5);
|
|
MeanAnimTime= (float)atof(time.c_str());
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
void makeAnimMeleeImpact(const std::string &animSetFile, const set<CAnimCombatSet> &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<CAnimCombatSet>::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<string> 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<animSetText.size();j++)
|
|
{
|
|
string line= animSetText[j];
|
|
string lineLwr= toLower(line);
|
|
|
|
// Find <LOG> TAg? => stop
|
|
if(line.find("<LOG>")!=string::npos)
|
|
break;
|
|
|
|
// Find a STRUCT start?
|
|
if(line.find("<STRUCT")!=string::npos)
|
|
{
|
|
// inc struct
|
|
structLevel++;
|
|
|
|
// if start a new State block
|
|
if(structLevel==2)
|
|
{
|
|
// reset info for this state
|
|
currentStateName.clear();
|
|
meleeImpactDelayLine= -1;
|
|
|
|
// try to get the name
|
|
const string tagStart= "name=\"";
|
|
uint start= lineLwr.find(tagStart);
|
|
if(start!=string::npos)
|
|
{
|
|
start+= tagStart.size();
|
|
uint end= lineLwr.find("\"", start);
|
|
if(end!=string::npos)
|
|
currentStateName= lineLwr.substr(start, end-start);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find a STRUCT end?
|
|
if(line.find("</STRUCT>")!=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<CAnimCombatState>::iterator it= currentCombatAnimSet.States.find(key);
|
|
if(it!=currentCombatAnimSet.States.end())
|
|
{
|
|
// else take the mean anim time
|
|
string format= " <ATOM Name=\"MeleeImpactDelay\" Value=\"%.3f\"/>";
|
|
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<animSetText.size();i++)
|
|
{
|
|
string str= animSetText[i];
|
|
str+= "\n";
|
|
oFile.serialBuffer((uint8*)str.c_str(), str.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
int usage()
|
|
{
|
|
printf("Usage: make_anim_melee_impact animset_dir");
|
|
return -1;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
int main(int argc, char *argv[])
|
|
{
|
|
NLMISC::createDebug();
|
|
|
|
// make_anim_melee_impact animset_dir
|
|
if(argc!=2)
|
|
return usage();
|
|
string animSetDir= argv[1];
|
|
|
|
// *** parse the anim.txt file
|
|
set<CAnimCombatSet> 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<string> files;
|
|
files.clear();
|
|
CPath::getPathContent(animSetDir, true, false, true, files);
|
|
vector<string> animSetList;
|
|
InfoLog->displayRawNL("");
|
|
InfoLog->displayRawNL("*****************************");
|
|
InfoLog->displayRawNL("**** .animation_set list ****");
|
|
InfoLog->displayRawNL("*****************************");
|
|
for(uint i=0;i<files.size();i++)
|
|
{
|
|
if(testWildCard(files[i], "*.animation_set"))
|
|
{
|
|
animSetList.push_back(files[i]);
|
|
InfoLog->displayRawNL(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;i<animSetList.size();i++)
|
|
{
|
|
makeAnimMeleeImpact(animSetList[i], combatAnimSets);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
// ***************************************************************************
|
|
|
|
/*
|
|
To generate the anim.txt file, this code has to be inserted in the client, in
|
|
entity_animation_manager.cpp, CEntityAnimationManager::load(), after
|
|
|
|
|
|
if(_AnimationSet)
|
|
{
|
|
_AnimationSet->build();
|
|
....
|
|
}
|
|
|
|
** 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<nSC;i++)
|
|
{
|
|
// per anim state
|
|
CAnimationState *state= const_cast<CAnimationState*>(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;j<state->getNumAnimation();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;j<state->getNumAnimation();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
|
|
// *************************************
|
|
|
|
|
|
*/ |