Use configured resolution for login/outgame/ingame
[ryzomcore.git] / ryzom / tools / make_anim_melee_impact / main.cpp
blobf5b0d0364ff8589e04f952b396b6f90e17a78bb8
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 // ***************************************************************************
22 This small tool was made for Graphist, to edit automatically *.animation_set sheets, so anims
23 get correct MeleeImpactDelay data
25 It uses an anim.txt file that was generated by inserting code (give at end of this file)
26 in the client.
28 This tool had to be "used one time only". hence its crappiest usage :)
32 #include "nel/misc/path.h"
33 #include "nel/misc/file.h"
34 #include "nel/misc/common.h"
35 #include "nel/misc/algo.h"
38 using namespace std;
39 using namespace NLMISC;
42 // ***************************************************************************
43 // Config
44 bool ReplaceExistingMeleeImpactDelay= true;
45 float MeleeImpactTimeFactor= 0.35f;
47 // Inited in main()
48 map<string, string> StateNameToStateCode;
51 // ***************************************************************************
52 class CAnimCombatState
54 public:
55 void build(const string & line);
57 // A1, A2 etc...
58 string StateCode;
59 // Mean Animation Time of all sub animations of this state
60 float MeanAnimTime;
62 public:
63 bool operator<(const CAnimCombatState &o) const {return StateCode<o.StateCode;}
66 class CAnimCombatSet
68 public:
69 // name of the anim set
70 string Name;
72 // set of CAnimCombatState
73 set<CAnimCombatState> States;
75 public:
76 bool operator<(const CAnimCombatSet &o) const {return Name<o.Name;}
80 // ***************************************************************************
81 void CAnimCombatState::build(const string &line)
83 StateCode= line.substr(4, 2);
84 string time= line.substr(10, 5);
85 NLMISC::fromString(time, MeanAnimTime);
89 // ***************************************************************************
90 void makeAnimMeleeImpact(const std::string &animSetFile, const set<CAnimCombatSet> &combatAnimSets)
92 // look if this animSetFile is in the combat list to patch
93 string shortName= NLMISC::toLowerAscii(CFile::getFilenameWithoutExtension(animSetFile));
94 CAnimCombatSet key;
95 key.Name= shortName;
96 set<CAnimCombatSet>::const_iterator it= combatAnimSets.find(key);
97 if(it == combatAnimSets.end())
98 return;
100 const CAnimCombatSet &currentCombatAnimSet= *it;
102 InfoLog->displayRawNL("patching %s", animSetFile.c_str());
105 // *** Read the animset file.
106 CIFile iFile;
107 iFile.open(animSetFile, true);
108 // Read all text
109 static vector<string> animSetText;
110 animSetText.clear();
111 while(!iFile.eof())
113 char tmp[50000];
114 iFile.getline(tmp, 50000);
115 animSetText.push_back(tmp);
117 iFile.close();
120 bool someChangeDone= false;
122 // *** Parse the animSet
124 // For each line of the animSet
125 sint structLevel= 0;
126 sint meleeImpactDelayLine= -1;
127 string currentStateName;
128 for(uint j=0;j<animSetText.size();j++)
130 string line= animSetText[j];
131 string lineLwr= toLowerAscii(line);
133 // Find <LOG> TAg? => stop
134 if(line.find("<LOG>")!=string::npos)
135 break;
137 // Find a STRUCT start?
138 if(line.find("<STRUCT")!=string::npos)
140 // inc struct
141 structLevel++;
143 // if start a new State block
144 if(structLevel==2)
146 // reset info for this state
147 currentStateName.clear();
148 meleeImpactDelayLine= -1;
150 // try to get the name
151 const string tagStart= "name=\"";
152 std::string::size_type start= lineLwr.find(tagStart);
153 if(start!=string::npos)
155 start+= tagStart.size();
156 std::string::size_type end= lineLwr.find("\"", start);
157 if(end!=string::npos)
158 currentStateName= lineLwr.substr(start, end-start);
163 // Find a STRUCT end?
164 if(line.find("</STRUCT>")!=string::npos)
166 // if end a state block, may add or replace MeleeDelayImpact
167 if(structLevel==2 && !currentStateName.empty())
169 // If the state is not in the combat state, no need to patch anything
170 static CAnimCombatState key;
171 // must translate for instance "attack1" to "A1"
172 key.StateCode= StateNameToStateCode[currentStateName];
173 set<CAnimCombatState>::const_iterator it= currentCombatAnimSet.States.find(key);
174 if(it!=currentCombatAnimSet.States.end())
176 // else take the mean anim time
177 string format= " <ATOM Name=\"MeleeImpactDelay\" Value=\"%.3f\"/>";
178 string newLine= toString(format.c_str(), it->MeanAnimTime * MeleeImpactTimeFactor);
180 // melee impact delay doesn't exist?
181 if(meleeImpactDelayLine==-1)
183 // add just before this line the Melee Impact Atom
184 animSetText.insert(animSetText.begin()+j, newLine);
185 j++;
186 someChangeDone= true;
188 // else exist and want to replace?
189 else if(ReplaceExistingMeleeImpactDelay)
191 animSetText[meleeImpactDelayLine]= newLine;
192 someChangeDone= true;
197 // dec struct level
198 structLevel--;
201 // if we are in level 2 structure, try to get the line to modify (if exist)
202 if(structLevel==2)
204 if( line.find("Name=\"MeleeImpactDelay\"")!=string::npos )
205 meleeImpactDelayLine= j;
210 // *** Write the animset file.
211 if(someChangeDone)
213 COFile oFile;
214 oFile.open(animSetFile, false, true);
215 // Write all text
216 for(uint i=0;i<animSetText.size();i++)
218 string str= animSetText[i];
219 str+= "\n";
220 oFile.serialBuffer((uint8*)str.c_str(), (uint)str.size());
226 // ***************************************************************************
227 int usage()
229 printf("Usage: make_anim_melee_impact animset_dir");
230 return -1;
234 // ***************************************************************************
235 int main(int argc, char *argv[])
237 NLMISC::createDebug();
239 // make_anim_melee_impact animset_dir
240 if(argc!=2)
241 return usage();
242 string animSetDir= argv[1];
244 // *** parse the anim.txt file
245 set<CAnimCombatSet> combatAnimSets;
246 CIFile animFile;
247 if(!animFile.open("anim.txt", true))
249 nlwarning("Can't open anim.txt file. abort");
250 return 0;
252 else
254 char tmp[5000];
255 CAnimCombatSet lastAnimSet;
256 // parse all lines
257 while(!animFile.eof())
259 animFile.getline(tmp, 5000);
260 string line= tmp;
261 if(line.empty())
262 continue;
264 // new anim set?
265 if(line[0]!=' ')
267 // insert the last anim state
268 if(!lastAnimSet.States.empty())
269 combatAnimSets.insert(lastAnimSet);
270 lastAnimSet.States.clear();
271 lastAnimSet.Name= line;
273 // new anim state?
274 else if(!lastAnimSet.Name.empty())
276 CAnimCombatState state;
277 state.build(line);
278 lastAnimSet.States.insert(state);
282 // append the last anim set if needed
283 if(!lastAnimSet.States.empty())
284 combatAnimSets.insert(lastAnimSet);
286 animFile.close();
289 // *** Get the list of .animset to make by race
290 vector<string> files;
291 files.clear();
292 CPath::getPathContent(animSetDir, true, false, true, files);
293 vector<string> animSetList;
294 InfoLog->displayRawNL("");
295 InfoLog->displayRawNL("*****************************");
296 InfoLog->displayRawNL("**** .animation_set list ****");
297 InfoLog->displayRawNL("*****************************");
298 for(uint i=0;i<files.size();i++)
300 if(testWildCard(files[i], "*.animation_set"))
302 animSetList.push_back(files[i]);
303 InfoLog->displayRawNL(animSetList.back().c_str());
307 // *** Init StateNameToStateCode
308 StateNameToStateCode["attack1"]= "A1";
309 StateNameToStateCode["attack2"]= "A2";
310 StateNameToStateCode["walk atk"]= "Wa";
311 StateNameToStateCode["run atk"]= "Ra";
312 StateNameToStateCode["backward atk"]= "Ba";
313 StateNameToStateCode["default atk low"]= "Dl";
314 StateNameToStateCode["default atk middle"]= "Dm";
315 StateNameToStateCode["default atk high"]= "Dh";
316 StateNameToStateCode["powerful atk low"]= "Pl";
317 StateNameToStateCode["powerful atk middle"]= "Pm";
318 StateNameToStateCode["powerful atk high"]= "Ph";
319 StateNameToStateCode["area atk low"]= "Al";
320 StateNameToStateCode["area atk middle"]= "Am";
321 StateNameToStateCode["area atk high"]= "Ah";
324 // *** For each animset, test if can replace some anim
325 InfoLog->displayRawNL("");
326 InfoLog->displayRawNL("**************************");
327 InfoLog->displayRawNL("**** Starting Process ****");
328 InfoLog->displayRawNL("**************************");
329 for(uint i=0;i<animSetList.size();i++)
331 makeAnimMeleeImpact(animSetList[i], combatAnimSets);
334 return 0;
340 // ***************************************************************************
341 // ***************************************************************************
342 // ***************************************************************************
343 // ***************************************************************************
346 To generate the anim.txt file, this code has to be inserted in the client, in
347 entity_animation_manager.cpp, CEntityAnimationManager::load(), after
350 if(_AnimationSet)
352 _AnimationSet->build();
353 ....
356 ** insert here ***
359 animSet Sheets and 3D anim data (fauna_animations.bnp and characters_animations.bnp) must be up to date
365 // *************************************
366 // CODE TO GENERATE MELEE IMPACT DELAY
367 // *************************************
368 CFileDisplayer animLog("anim.txt", true);
369 TAnimStateId walkAtk= CAnimationStateSheet::getAnimationStateId("walk atk");
370 TAnimStateId runAtk= CAnimationStateSheet::getAnimationStateId("run atk");
371 TAnimStateId backAtk= CAnimationStateSheet::getAnimationStateId("backward atk");
372 TAnimStateId stateCombat[]= {CAnimationStateSheet::Attack1, CAnimationStateSheet::Attack2,
373 walkAtk, runAtk, backAtk,
374 CAnimationStateSheet::DefaultAtkLow,CAnimationStateSheet::DefaultAtkHigh,
375 CAnimationStateSheet::DefaultAtkMiddle,CAnimationStateSheet::PowerfulAtkLow,
376 CAnimationStateSheet::PowerfulAtkHigh,CAnimationStateSheet::PowerfulAtkMiddle,
377 CAnimationStateSheet::AreaAtkLow,CAnimationStateSheet::AreaAtkHigh,
378 CAnimationStateSheet::AreaAtkMiddle};
379 string stateCombatCode[]= {"A1", "A2", "Wa", "Ra", "Ba",
380 "Dl", "Dh", "Dm", "Pl", "Ph", "Pm", "Al", "Ah", "Am"};
381 const uint32 nSC= sizeof(stateCombat) / sizeof(stateCombat[0]);
382 nlctassert(nSC==sizeof(stateCombatCode) / sizeof(stateCombatCode[0]));
384 float roughEval= 0.f;
385 uint nbRoughEval= 0;
386 TAnimSet::iterator it= _AnimSet.begin();
387 for(;it!=_AnimSet.end();it++)
389 CAnimationSet &animSet= it->second;
390 bool animSetDisplayed= false;
391 for(uint i=0;i<nSC;i++)
393 // per anim state
394 CAnimationState *state= const_cast<CAnimationState*>(animSet.getAnimationState(stateCombat[i]));
395 if(state)
397 // first compute mean and anim name
398 float mean= 0.f;
399 uint nbValid= 0;
400 string animName;
401 bool extended= false;
402 for(uint j=0;j<state->getNumAnimation();j++)
404 CAnimation *anim= state->getAnimationByIndex(j);
405 NL3D::UAnimation *anim3d= NULL;
406 if(anim)
407 anim3d= _AnimationSet->getAnimation(anim->id());
408 if(anim && anim3d)
410 // name
411 string name= NLMISC::toLowerAscii(_AnimationSet->getAnimationName(anim->id()));
412 if(animName.empty())
413 animName= name;
414 else if(!extended)
416 extended= true;
417 animName+= ", ...";
420 // meanLength and nb
421 float timeLen= anim3d->getEndTime()-anim3d->getBeginTime();
422 mean+= timeLen;
423 nbValid++;
426 if(nbValid)
427 mean/=nbValid;
429 // compute standard and max deviation
430 float stdDev=0.f, maxDev=0.f;
431 for(uint j=0;j<state->getNumAnimation();j++)
433 CAnimation *anim= state->getAnimationByIndex(j);
434 NL3D::UAnimation *anim3d= NULL;
435 if(anim)
436 anim3d= _AnimationSet->getAnimation(anim->id());
437 if(anim && anim3d)
439 float timeLen= anim3d->getEndTime()-anim3d->getBeginTime();
440 stdDev+= (float)fabs(timeLen - mean);
441 maxDev= max(maxDev, (float)fabs(timeLen - mean));
444 if(nbValid)
445 stdDev/= nbValid;
447 // valid?
448 if(nbValid)
450 // display first animSetName
451 if(!animSetDisplayed)
453 string msg= toString("%s\n", it->first.c_str() );
454 animLog.display(CLog::TDisplayInfo(), msg.c_str());
455 animSetDisplayed= true;
458 // then stats for this state
459 string msg= toString(" %s: mn%.03f, md%.03f, sd%.03f, ev%.03f (%s)\n", stateCombatCode[i].c_str(),
460 mean, maxDev, stdDev, mean*0.4f, animName.c_str());
461 animLog.display(CLog::TDisplayInfo(), msg.c_str());
463 roughEval+= mean;
464 nbRoughEval++;
469 if(nbRoughEval)
471 roughEval/= nbRoughEval;
472 nlinfo(" AnimDBG RoughEval: mn%.03f, ev%.03f",
473 roughEval, roughEval*0.4f);
476 // *************************************
477 // CODE TO GENERATE MELEE IMPACT DELAY
478 // *************************************