13using namespace Tempest;
15static void setupTime(std::vector<uint64_t>& t0,
const std::vector<int32_t>& inp,
float fps){
16 t0.resize(inp.size());
17 for(
size_t i=0;i<inp.size();++i){
18 t0[i] = uint64_t(
float(inp[i])*1000.f/fps);
22static uint64_t
frameClamp(int32_t frame,uint32_t first,uint32_t numFrames,uint32_t last) {
25 if(frame>=
int(first+numFrames))
29 return uint64_t(frame)-first;
33 ref = std::move(p.aliases);
35 for(
auto& ani : p.animations) {
36 auto& data = loadMAN(ani, std::string(name) +
'-' + ani.name +
".MAN");
37 data.data->sfx = std::move(ani.sfx);
38 data.data->gfx = std::move(ani.sfx_ground);
39 data.data->events = std::move(ani.events);
40 data.data->mmStartAni = std::move(ani.morph);
42 data.data->pfx.resize(ani.pfx.size() + ani.pfx_stop.size());
43 for(
size_t i=0; i<ani.pfx.size(); ++i) {
44 static_cast<zenkit::MdsParticleEffect&
>(data.data->pfx[i]) = std::move(ani.pfx[i]);
46 for(
size_t i=0; i<ani.pfx_stop.size(); ++i) {
47 auto& p = data.data->pfx[i + ani.pfx.size()];
48 p.index = ani.pfx_stop[i].index;
49 p.frame = ani.pfx_stop[i].frame;
54 return l.frame < r.frame;
58 for(
auto& co : p.combinations) {
59 string_frm name(co.model, 1+(co.last_frame-1)/2);
62 for(
size_t r=0;r<sequences.size();++r) {
63 auto& i = sequences[sequences.size()-r-1];
66 sequences.emplace_back();
72 ani.
blendIn = uint64_t(1000*co.blend_in);
73 ani.
blendOut = uint64_t(1000*co.blend_out);
74 ani.
next = std::move(co.next);
76 ani.
comb.resize(
size_t(co.last_frame));
83 Log::d(
"comb not found: ", co.name,
" -> ", co.model,
"(", name,
")");
87 mesh = std::move(p.meshes);
88 meshDef = std::move(p.skeleton);
94 if(sqHot!=
nullptr && sqHot->
name==name)
96 auto it = std::lower_bound(sequences.begin(),sequences.end(),name,[](
const Sequence& s,std::string_view n){
100 if(it!=sequences.end() && it->name==name) {
108 for(
auto& i:sequences)
115 for(
auto& i:sequences)
120 if(!meshDef.name.empty() && !meshDef.disable_mesh)
125Animation::Sequence& Animation::loadMAN(
const zenkit::MdsAnimation& hdr, std::string_view name) {
126 sequences.emplace_back(hdr,name);
127 auto& ret = sequences.back();
128 if(ret.data==
nullptr) {
129 ret.data = std::make_shared<AnimData>();
130 Log::e(
"unable to load animation sequence: \"",name,
"\"");
135void Animation::setupIndex() {
136 for(
auto& sq:sequences)
137 sq.data->setupEvents(sq.data->fpsRate);
141 for(
auto& s:sequences)
142 if(s.askName==r.alias)
145 if(ani.data==
nullptr) {
146 Log::d(
"alias not found: ",r.name,
" -> ",r.alias);
153 ani.blendIn = uint64_t(1000 * r.blend_in);
154 ani.blendOut = uint64_t(1000 * r.blend_out);
155 ani.reverse = r.direction != zenkit::AnimationDirection::FORWARD;
157 sequences.emplace_back(std::move(ani));
161 for(
auto& sq:sequences) {
163 i = char(std::toupper(i));
165 i = char(std::toupper(i));
168 std::sort(sequences.begin(),sequences.end(),[](
const Sequence& a,
const Sequence& b){
169 return a.name<b.name;
172 for(
auto& s:sequences) {
175 for(
size_t i=0;i<s.comb.size();++i) {
181 for(
auto& i:sequences) {
182 if((i.next==i.askName && !i.next.empty()) || i.next==i.name)
184 if(!i.data->defWindow.empty()) {
189 if(i.name.find(
"S_")==0)
190 i.shortName = &i.name[2];
191 if(i.name==
"T_1HRUN_2_1H" || i.name==
"T_BOWRUN_2_BOW" ||
192 i.name==
"T_2HRUN_2_2H" || i.name==
"T_CBOWRUN_2_CBOW" ||
193 i.name==
"T_MAGRUN_2_MAG" || i.name==
"T_FISTRUN_2_FIST")
197 for(
auto& i:sequences) {
211 auto reader = entry->open_read();
212 zenkit::ModelAnimation p;
213 p.load(reader.get());
215 data = std::make_shared<AnimData>();
219 blendIn = uint64_t(1000*hdr.blend_in);
220 blendOut = uint64_t(1000*hdr.blend_out);
222 reverse = hdr.direction != zenkit::AnimationDirection::FORWARD;
224 data->firstFrame = uint32_t(hdr.first_frame);
225 data->lastFrame = uint32_t(hdr.last_frame);
230 data->fpsRate = p.fps;
231 data->numFrames = p.frame_count;
232 data->nodeIndex = p.node_indices;
233 data->samples = p.samples;
239 const uint64_t t = now-sTime;
240 if(comboLen<data->defHitEnd.size()) {
241 if(t>data->defHitEnd[comboLen])
244 return float(t)>totalTime();
248 return float(data->numFrames)*1000.f/data->fpsRate;
252 if(comboLen<data->defHitEnd.size()) {
253 uint64_t time = data->defHitEnd[comboLen];
260 const uint16_t cId = uint16_t(comboLen*2u);
262 if(
size_t(cId+1)<data->defWindow.size() && data->defWindow.size()>0 && !data->defHitEnd.empty()) {
263 return !((now-sTime)<=data->defWindow[cId+0]);
268 if(
size_t(cId+1)<data->defWindow.size() && isInComboWindow(now-sTime,comboLen)) {
275 uint16_t
id = uint16_t(comboLen*2u);
276 return (data->defWindow[
id+0]<t && t<=data->defWindow[
id+1]);
280 if(data->defParFrame.size()!=2)
282 return data->defParFrame[0]<=t && t<data->defParFrame[1];
286 for(
size_t i=0;i+1<data->defWindow.size();i+=2) {
287 if(data->defWindow[i+0]<=t && t<data->defWindow[i+1])
294 for(
auto& e:data->events)
295 if(e.type == zenkit::MdsEventType::OPTIMAL_FRAME)
302 auto numFrames = d.numFrames;
306 int64_t frame = int64_t(
float(now-sTime)*d.fpsRate/1000.f);
308 for(
auto& e:data->events)
309 if(e.type == zenkit::MdsEventType::OPTIMAL_FRAME)
310 for(
auto i : e.frames)
311 if(int64_t(i) > frame)
316bool Animation::Sequence::extractFrames(uint64_t& frameA,uint64_t& frameB,
bool& invert,uint64_t barrier, uint64_t sTime, uint64_t now)
const {
318 auto numFrames = d.numFrames;
322 float fpsRate = d.fpsRate;
323 frameA = uint64_t(
float(barrier-sTime)*fpsRate/1000.f);
324 frameB = uint64_t(
float(now -sTime)*fpsRate/1000.f);
333 frameA = std::min<uint64_t>(frameA,numFrames);
334 frameB = std::min<uint64_t>(frameB,numFrames);
338 frameA = numFrames-frameA;
339 frameB = numFrames-frameB;
340 std::swap(frameA,frameB);
343 invert = (frameB<frameA);
345 std::swap(frameA,frameB);
350 uint64_t frameA=0,frameB=0;
352 if(!extractFrames(frameA,frameB,invert,barrier,sTime,now))
357 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
358 if(((frameA<=fr && fr<frameB) ^ invert) || i.frame==int32_t(d.lastFrame)) {
365 if(npc!=
nullptr && !npc->
isInAir()) {
367 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
368 if((frameA<=fr && fr<frameB) ^ invert)
375 if(data->pfx.empty())
378 uint64_t frameA=0,frameB=0;
380 if(!extractFrames(frameA,frameB,invert,barrier,sTime,now))
388 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
390 processPfx(i, visual, world);
393 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
395 processPfx(i, visual, world);
399 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
400 if(frameA<=fr && fr<frameB)
401 processPfx(i, visual, world);
410 else if(!p.name.empty()) {
413 visual.
startEffect(world,std::move(e),p.index,
false);
418 uint64_t frameA=0,frameB=0;
420 if(!extractFrames(frameA,frameB,invert,barrier,sTime,now))
424 float fpsRate = d.fpsRate;
426 for(
auto& e:d.events) {
427 if(e.type == zenkit::MdsEventType::OPTIMAL_FRAME) {
428 for(
auto i:e.frames) {
429 uint64_t fr =
frameClamp(i,d.firstFrame,d.numFrames,d.lastFrame);
430 if((frameA<=fr && fr<frameB) ^ invert)
431 processEvent(e,ev,uint64_t(
float(fr)*1000.f/fpsRate)+sTime);
434 uint64_t fr =
frameClamp(e.frame,d.firstFrame,d.numFrames,d.lastFrame);
435 if((frameA<=fr && fr<frameB) ^ invert)
436 processEvent(e,ev,uint64_t(
float(fr)*1000.f/fpsRate)+sTime);
441 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
442 if((frameA<=fr && fr<frameB) ^ invert)
446 for(
auto& i:d.mmStartAni){
447 uint64_t fr =
frameClamp(i.frame,d.firstFrame,d.numFrames,d.lastFrame);
448 if((frameA<=fr && fr<frameB) ^ invert) {
450 e.
anim = i.animation;
452 ev.
morph.push_back(e);
457void Animation::Sequence::processEvent(
const zenkit::MdsEventTag& e,
Animation::EvCount &ev, uint64_t time) {
459 case zenkit::MdsEventType::OPTIMAL_FRAME:
462 case zenkit::MdsEventType::SET_FIGHT_MODE:
465 case zenkit::MdsEventType::ITEM_CREATE:
466 case zenkit::MdsEventType::ITEM_EXCHANGE:{
472 ev.
timed.push_back(ex);
475 case zenkit::MdsEventType::ITEM_INSERT:
476 case zenkit::MdsEventType::ITEM_REMOVE:
477 case zenkit::MdsEventType::ITEM_DESTROY:
478 case zenkit::MdsEventType::ITEM_PLACE: {
483 ev.
timed.push_back(ex);
486 case zenkit::MdsEventType::MUNITION_PLACE:
487 case zenkit::MdsEventType::MUNITION_REMOVE: {
492 ev.
timed.push_back(ex);
495 case zenkit::MdsEventType::MESH_SWAP: {
499 ex.slot[1] = e.slot2;
501 ev.
timed.push_back(ex);
504 case zenkit::MdsEventType::TORCH_DRAW:
505 case zenkit::MdsEventType::TORCH_INVENTORY:
506 case zenkit::MdsEventType::TORCH_DROP: {
511 ev.
timed.push_back(ex);
520 auto a = translateXZ(at), b=translateXZ(at+dt);
521 Tempest::Vec3 f = b-a;
530 uint32_t numFrames = d.numFrames;
531 if(numFrames==0 || d.tr.size()==0)
532 return Tempest::Vec3();
535 uint64_t all=uint64_t(totalTime());
540 float fr = float(data->fpsRate*
float(at))/1000.f;
541 float a = std::fmod(fr,1.f);
542 uint64_t frameA = uint64_t(fr);
543 uint64_t frameB = frameA+1;
545 auto mA = frameA/d.tr.size();
546 auto pA = d.tr[size_t(frameA%d.tr.size())];
548 auto mB = frameB/d.tr.size();
549 auto pB = d.tr[size_t(frameB%d.tr.size())];
551 pA += d.moveTr*float(mA);
552 pB += d.moveTr*float(mB);
554 Tempest::Vec3 p = pA + (pB-pA)*a;
560 for(
size_t i=0, r=0; i<name.size(); ++i) {
562 for(i++;i<name.size() && r<63;++i) {
574void Animation::Sequence::setupMoveTr() {
579 size_t sz = nodeIndex.size();
586 if(0<samples.size() && sz<=samples.size()) {
587 auto& a = samples[0].position;
588 auto& b = samples[samples.size()-sz].position;
593 tr.resize(samples.size()/sz - 1);
594 for(
size_t i=0, r=0; r<tr.size(); i+=sz,++r) {
596 auto& bi = samples[i].position;
601 static const float eps = 0.4f;
603 if(std::fabs(i.x)<eps && std::fabs(i.y)<eps && std::fabs(i.z)<eps)
610 if(0<samples.size()){
611 translate.x = samples[0].position.x;
612 translate.y = samples[0].position.y;
613 translate.z = samples[0].position.z;
621 int hasOptFrame = std::numeric_limits<int>::max();
622 for(
size_t i=0; i<events.size(); ++i)
623 if(events[i].type==zenkit::MdsEventType::OPTIMAL_FRAME) {
624 hasOptFrame = std::min(hasOptFrame, events[i].frames[0]);
627 for(
size_t i=0; i<events.size(); ++i)
628 if(events[i].type==zenkit::MdsEventType::OPTIMAL_FRAME && events[i].frames[0]!=hasOptFrame) {
629 events[i] = std::move(events.back());
633 for(
auto& r:events) {
634 if(r.type==zenkit::MdsEventType::HIT_END)
636 if(r.type==zenkit::MdsEventType::PARRY_FRAME)
638 if(r.type==zenkit::MdsEventType::COMBO_WINDOW)
static void setupTime(std::vector< uint64_t > &t0, const std::vector< int32_t > &inp, float fps)
static uint64_t frameClamp(int32_t frame, uint32_t first, uint32_t numFrames, uint32_t last)
Animation(zenkit::ModelScript &p, std::string_view name, bool ignoreErrChunks)
const Sequence * sequenceAsc(std::string_view name) const
const Sequence * sequence(std::string_view name) const
std::string_view defaultMesh() const
void emitSoundEffect(std::string_view sound, float range, bool freeSlot)
void startEffect(World &owner, Effect &&pfx, int32_t slot, bool noSlot)
void stopEffect(const VisualFx &vfx)
void emitSoundEffect(std::string_view sound, float range, bool freeSlot)
void emitSoundGround(std::string_view sound, float range, bool freeSlot)
static const zenkit::Vfs & vdfsIndex()
void setupEvents(float fpsRate)
zenkit::MdsFightMode weaponCh
std::vector< EvMorph > morph
std::vector< EvTimed > timed
float atkTotalTime(uint16_t comboLen) const
bool canInterrupt(uint64_t now, uint64_t sTime, uint16_t comboLen) const
bool isAttackAnim() const
void processPfx(uint64_t barrier, uint64_t sTime, uint64_t now, MdlVisual &visual, World &world) const
bool isDefParWindow(uint64_t t) const
Tempest::Vec3 speed(uint64_t at, uint64_t dt) const
bool isFinished(uint64_t now, uint64_t sTime, uint16_t comboLen) const
Tempest::Vec3 translateXZ(uint64_t at) const
bool isPrehit(uint64_t sTime, uint64_t now) const
void schemeName(char buf[64]) const
zenkit::AnimationFlags flags
void processEvents(uint64_t barrier, uint64_t sTime, uint64_t now, EvCount &ev) const
bool isInComboWindow(uint64_t t, uint16_t comboLen) const
void processSfx(uint64_t barrier, uint64_t sTime, uint64_t now, Npc *npc, Interactive *mob) const
std::vector< const Sequence * > comb
bool isDefWindow(uint64_t t) const
std::shared_ptr< AnimData > data