3#include <Tempest/Matrix4x4>
21using namespace Tempest;
25std::string_view Npc::Routine::wayPointName()
const {
26 return point!=
nullptr ? point->name : fallbackName;
30void Npc::GoTo::save(
Serialize& fout)
const {
31 fout.
write(npc, uint8_t(flag), wp, pos);
35 fin.
read(npc,
reinterpret_cast<uint8_t&
>(flag), wp, pos);
38 if(flag==GoToHint::GT_EnemyG || flag==GoToHint::GT_EnemyA)
43Vec3 Npc::GoTo::target()
const {
45 return npc->position() + Vec3(0, npc->translateY(), 0);
47 return wp->position();
51bool Npc::GoTo::isClose(
const Npc& self,
float dist)
const {
59bool Npc::GoTo::empty()
const {
63void Npc::GoTo::clear() {
75void Npc::GoTo::set(
const WayPoint* to, GoToHint hnt) {
81void Npc::GoTo::set(
const Item* to) {
86void Npc::GoTo::set(
const Vec3& to) {
91void Npc::GoTo::setFlee() {
98 hnpc = std::make_shared<zenkit::INpc>(*self.hnpc);
99 invent = std::move(self.invent);
105 body = std::move(self.body);
106 head = std::move(self.head);
116 hnpc = std::make_shared<zenkit::INpc>();
117 hnpc->user_ptr =
this;
129 int32_t aivar[zenkit::INpc::aivar_count]={};
131 auto exp = self.hnpc->exp;
132 auto exp_next = self.hnpc->exp_next;
133 auto lp = self.hnpc->lp;
134 auto level = self.hnpc->level;
135 std::memcpy(aivar,self.hnpc->aivar,
sizeof(aivar));
138 self.hnpc->exp = exp;
139 self.hnpc->exp_next = exp_next;
141 self.hnpc->level =
level;
142 std::memcpy(self.hnpc->aivar,aivar,
sizeof(aivar));
144 self.invent = std::move(
invent);
148 self.body = std::move(
body);
149 self.head = std::move(
head);
164 std::shared_ptr<zenkit::INpc>
hnpc={};
178 :owner(owner),aiPolicy(aiPolicy),mvAlgo(*this) {
181 hnpc = std::make_shared<zenkit::INpc>();
182 hnpc->user_ptr =
this;
183 hnpc->id = int32_t(
instance & 0x7FFFFFFF);
184 hnpc->wp = std::string(waypoint);
194 if(hnpc->damage_type==0)
195 hnpc->damage_type = 2;
202 currentInteract->
detach(*
this,
true);
208 fout.
write(body,head,vHead,vTeeth,bdColor,vColor,bdFatness);
209 fout.
write(x,y,z,angle,sz);
210 fout.
write(wlkMode,trGuild,talentsSk,talentsVl,refuseTalkMilis);
211 fout.
write(permAttitude,tmpAttitude);
212 fout.
write(perceptionTime,perceptionNextTime);
213 for(
auto& i:perception)
217 fout.
write(lastHitType,lastHitSpell);
218 if(currentSpellCast<uint32_t(-1))
219 fout.
write(uint32_t(currentSpellCast));
else
220 fout.
write(uint32_t(-1));
221 fout.
write(uint8_t(castLevel),castNextTime,manaInvested,aiExpectedInvest);
222 fout.
write(spellInfo);
227 fout.
write(currentInteract,currentOther,currentVictim);
228 fout.
write(currentLookAt,currentLookAtNpc,currentTarget,nearestEnemy);
231 fout.
write(currentFp,currentFpLock);
236 fout.
write(lastEventTime,angleY,runAng);
237 fout.
write(invTorch);
244 visual.
save(fout,*
this);
247 if(!invent.
isEmpty() ||
id==
size_t(-1))
254 hnpc = std::make_shared<zenkit::INpc>();
255 hnpc->user_ptr =
this;
257 fin.
read(body,head,vHead,vTeeth,bdColor,vColor,bdFatness);
261 sym->set_instance(hnpc);
263 fin.
read(x,y,z,angle,sz);
264 fin.
read(wlkMode,trGuild,talentsSk,talentsVl,refuseTalkMilis);
265 durtyTranform = TR_Pos|TR_Rot|TR_Scale;
267 fin.
read(permAttitude,tmpAttitude);
268 fin.
read(perceptionTime,perceptionNextTime);
269 for(
auto& i:perception)
273 fin.
read(lastHitType,lastHitSpell);
275 uint32_t currentSpellCastU32 = uint32_t(-1);
276 fin.
read(currentSpellCastU32);
277 currentSpellCast = (currentSpellCastU32==uint32_t(-1) ? size_t(-1) : currentSpellCastU32);
279 fin.
read(
reinterpret_cast<uint8_t&
>(castLevel),castNextTime);
281 fin.
read(manaInvested,aiExpectedInvest);
286 fin.
read(currentInteract,currentOther,currentVictim);
288 fin.
read(currentLookAt);
289 fin.
read(currentLookAtNpc,currentTarget,nearestEnemy);
292 fin.
read(currentFp,currentFpLock);
297 fin.
read(lastEventTime,angleY,runAng);
309 visual.
load(fin,*
this);
315 invent.
load(fin,*
this);
326 if(currentInteract!=
nullptr && !currentInteract->
isAttached(*
this))
327 currentInteract =
nullptr;
330void Npc::saveAiState(
Serialize& fout)
const {
331 fout.
write(aniWaitTime,waitTime,faiWaitTime,outWaitTime);
332 fout.
write(uint8_t(aiPolicy));
333 fout.
write(aiState.funcIni,aiState.funcLoop,aiState.funcEnd,aiState.sTime,aiState.eTime,aiState.started,aiState.loopNextTime);
334 fout.
write(aiPrevState);
337 aiQueueOverlay.
save(fout);
339 fout.
write(uint32_t(routines.size()));
340 for(
auto& i:routines) {
341 fout.
write(i.start,i.end,i.callback,i.point,i.fallbackName);
346 fin.
read(aniWaitTime);
347 fin.
read(waitTime,faiWaitTime);
348 fin.
read(outWaitTime);
349 fin.
read(
reinterpret_cast<uint8_t&
>(aiPolicy));
350 fin.
read(aiState.funcIni,aiState.funcLoop,aiState.funcEnd,aiState.sTime,aiState.eTime,aiState.started,aiState.loopNextTime);
351 fin.
read(aiPrevState);
355 aiState.hint = s->name().c_str();
360 aiQueueOverlay.
load(fin);
364 routines.resize(size);
365 for(
auto& i:routines) {
366 fin.
read(i.start,i.end,i.callback,i.point);
368 fin.
read(i.fallbackName);
372void Npc::saveTrState(
Serialize& fout)
const {
373 if(transformSpl!=
nullptr) {
375 transformSpl->save(fout);
385 transformSpl.reset(
new TransformBack(*
this, owner.
script().
getVm(), fin));
389 if(x==ix && y==iy && z==iz)
394 durtyTranform |= TR_Pos;
403void Npc::setViewPosition(
const Tempest::Vec3& pos) {
407 durtyTranform |= TR_Pos;
410int Npc::aiOutputOrderId()
const {
417 const int order = act.
target->aiOutputOrderId();
437 float a = angleDir(pos.x, pos.z);
443 durtyTranform |= TR_Rot;
455 durtyTranform |= TR_Rot;
459 durtyTranform |= TR_Rot;
463float Npc::angleDir(
float x,
float z) {
466 a = 90+180.f*std::atan2(z,x)/float(M_PI);
471 const bool g2 = owner.
version().game==2;
478 invent.
clearSlot(*
this,
"",currentInteract!=
nullptr);
501 if(at->isLocked() && !
isDead) {
549bool Npc::checkHealth(
bool onChange,
bool allowUnconscious) {
559 if(currentOther==
nullptr || !allowUnconscious || !
isHuman() ||
575void Npc::onNoHealth(
bool death, HitSound sndMask) {
581 updateWeaponSkeleton();
587 const char* svm = death ?
"SVM_%d_DEAD" :
"SVM_%d_AARGH";
588 const char* state = death ?
"ZS_Dead" :
"ZS_Unconscious";
609 setAnim(lastHitType==
'A' ? Anim::DeadA :
Anim::DeadB);
else
610 setAnim(lastHitType==
'A' ? Anim::UnconsciousA :
Anim::UnconsciousB);
613bool Npc::hasAutoroll()
const {
618void Npc::stopWalkAnimation() {
637 const size_t head = visual.
pose().
findNode(
"BIP01 HEAD");
640 if(isFirstPerson && head!=
size_t(-1)) {
651 const size_t head = visual.
pose().
findNode(
"BIP01 HEAD");
652 if(isFirstPerson && head!=
size_t(-1)) {
663 return angle*float(M_PI)/180.f;
671 return angleY*float(M_PI)/180.f;
691 return currentLookAtNpc;
704 return dp.quadLength();
727uint8_t Npc::calcAniComb()
const {
728 if(currentTarget==
nullptr)
735 return hnpc->name[0];
806void Npc::dropTorch(
bool burnout) {
823 size_t leftHand = sk->findNode(
"ZS_LEFTHAND");
824 if(torchId!=
size_t(-1) && leftHand!=
size_t(-1)) {
830 owner.
addItemDyn(torchId,mat,hnpc->symbol_index());
843void Npc::setVisualBody(int32_t headTexNr, int32_t teethTexNr, int32_t bodyTexNr, int32_t bodyTexColor,
844 std::string_view ibody, std::string_view ihead) {
850 bdColor = bodyTexColor;
854 visual.
setVisualBody(*
this,std::move(vhead),std::move(vbody),bdColor);
857 durtyTranform|=TR_Pos;
866 visual.
setBody(*
this,std::move(vbody),bdColor);
868 auto& itData = ar->handle();
869 auto flag =
ItmFlags(itData.main_flag);
871 auto& asc = itData.visual_change;
872 auto vbody = asc.empty() ?
MeshObjects::Mesh() : w.addView(asc,vColor,0,bdColor);
873 visual.
setArmor(*
this,std::move(vbody));
880 updateWeaponSkeleton();
885 updateWeaponSkeleton();
890 updateWeaponSkeleton();
896 updateWeaponSkeleton();
915void Npc::updateWeaponSkeleton() {
920 physic = std::move(item);
934 durtyTranform |= TR_Scale;
973 implAniWait(uint64_t(sq->totalTime()));
1061 const auto lvl = talentsSk[t];
1134 return talentsSk[t];
1145 return talentsVl[t];
1150 if(t<=zenkit::INpc::hitchance_count)
1151 return hnpc->hitchance[t];
1156 return refuseTalkMilis>=owner.
tickCount();
1168 refuseTalkMilis = owner.
tickCount()+milis;
1173 return hnpc->attribute[a];
1190 hnpc->attribute[a]+=val;
1191 if(hnpc->attribute[a]<0)
1192 hnpc->attribute[a]=0;
1196 hnpc->attribute[a] = hnpc->attribute[
ATR_MANAMAX];
1202 checkHealth(
true,allowUnconscious);
1204 aiState.started =
true;
1210 return hnpc->protection[p];
1216 hnpc->protection[p]=val;
1220 return uint32_t(hnpc->symbol_index());
1224 return std::min(uint32_t(hnpc->guild), uint32_t(
GIL_MAX-1));
1228 const bool g2 = owner.
version().game==2;
1231 return SEPERATOR_HUM<
guild() &&
guild()<SEPERATOR_ORC;
1235 const bool g2 = owner.
version().game==2;
1237 return guild() < SEPERATOR_HUM;
1263 return hnpc->exp_next;
1279 bool g2 = owner.
version().game==2;
1280 return ( g2 && hnpc->type==zenkit::NpcType::G2_FRIEND) ||
1281 (!g2 && hnpc->type==zenkit::NpcType::G1_FRIEND);
1288bool Npc::implPointAt(
const Tempest::Vec3& to) {
1295bool Npc::implLookAtWp(uint64_t dt) {
1296 if(currentLookAt==
nullptr)
1298 auto dvec = currentLookAt->
position();
1299 return implLookAt(dvec.x,dvec.y,dvec.z,dt);
1302bool Npc::implLookAtNpc(uint64_t dt) {
1303 if(currentLookAtNpc==
nullptr)
1306 auto otherHead = currentLookAtNpc->visual.
mapHeadBone();
1307 auto dvec = otherHead - selfHead;
1308 return implLookAt(dvec.x,dvec.y,dvec.z,dt);
1311bool Npc::implLookAt(
float dx,
float dy,
float dz, uint64_t dt) {
1312 static const float rotSpeed = 200;
1313 static const float maxRot = 80;
1322 dst.y = std::atan2(dy,std::sqrt(dx*dx+dz*dz));
1323 dst.y = dst.y*180.f/float(M_PI);
1325 if(dst.x<-maxRot || dst.x>maxRot) {
1336 auto drot = dst-rot;
1338 drot.x = std::min(std::abs(drot.x),rotSpeed*
float(dt)/1000.f);
1339 drot.y = std::min(std::abs(drot.y),rotSpeed*
float(dt)/1000.f);
1351bool Npc::implTurnAway(
const Npc &oth, uint64_t dt) {
1363bool Npc::implTurnTo(
const Npc &oth, uint64_t dt) {
1376 return implTurnTo(dx,dz,anim,dt);
1382 return implTurnTo(wp->
dir.x,wp->
dir.z,anim,dt);
1388 return rotateTo(dx,dz,step,anim,dt);
1391bool Npc::implWhirlTo(
const Npc &oth, uint64_t dt) {
1395bool Npc::implGoTo(uint64_t dt) {
1405 if(go2.wp!=
nullptr && go2.wp->useCounter()>1)
1408 return implGoTo(dt,dist);
1411bool Npc::implGoTo(uint64_t dt,
float destDist) {
1420 auto target = go2.target();
1426 else if(go2.isClose(*
this, destDist)) {
1427 bool finished =
true;
1429 go2.wp = go2.wp->hasLadderConn(wayPath.
first()) ? wayPath.
first() : wayPath.
pop();
1430 if(go2.wp!=
nullptr) {
1432 if(setGoToLadder()) {
1446 if(setGoToLadder()) {
1464bool Npc::implAttack(uint64_t dt) {
1468 if(currentTarget->
isDown()){
1475 if(aiQueue.
size()>0) {
1495 adjustAttackRotation(dt);
1540 setAnim(Npc::Anim::Idle);
else
1541 adjustAttackRotation(dt);
1546 static const Anim ani[4] = {Anim::Attack, Anim::AttackL, Anim::AttackR};
1554 bool obsticle =
false;
1555 if(currentTarget!=
nullptr) {
1557 if(hit.hasCol && hit.npcHit!=currentTarget) {
1561 if(hit.npcHit!=
nullptr && hit.npcHit!=currentTarget && owner.
script().
isFriendlyFire(*
this,*hit.npcHit))
1566 if(spl->isSpell() && !spl->isSpellShoot())
1570 auto anim = (owner.
script().
rand(2)==0 ? Npc::Anim::MoveL : Npc::Anim::MoveR);
1607 implFaiWait(aniTime);
1609 implAniWait(aniTime);
1612 adjustAttackRotation(dt);
1623 if(!implTurnTo(*currentTarget,dt))
1629 if(
setAnim(Npc::Anim::MoveL)) {
1638 if(
setAnim(Npc::Anim::MoveR)) {
1666 aiState.loopNextTime = owner.
tickCount();
1669 if(
setAnim(Npc::Anim::MoveBack)) {
1678 const bool prGRange = fghAlgo.
isInGRange(*
this, *currentTarget, owner.
script());
1687 const bool isGRange = fghAlgo.
isInGRange(*
this, *currentTarget, owner.
script());
1688 const bool isWRange = fghAlgo.
isInWRange(*
this, *currentTarget, owner.
script());
1689 const bool isFocus = fghAlgo.
isInFocusAngle(*
this, *currentTarget);
1691 if((isWRange || (isGRange!=prGRange)) && isFocus) {
1694 aiState.loopNextTime = owner.
tickCount();
1706 stopWalkAnimation();
1713 stopWalkAnimation();
1719 stopWalkAnimation();
1726void Npc::adjustAttackRotation(uint64_t dt) {
1727 if(currentTarget!=
nullptr && !currentTarget->
isDown()) {
1733 implTurnTo(*currentTarget,anim,dt);
1738bool Npc::implAiTick(uint64_t dt) {
1740 if(aiQueue.
size()==0) {
1742 if(aiQueue.
size()>0)
1743 nextAiAction(aiQueue,dt);
1746 nextAiAction(aiQueue,dt);
1750void Npc::implAiWait(uint64_t dt) {
1756void Npc::implAniWait(uint64_t dt) {
1762void Npc::implFaiWait(uint64_t dt) {
1764 aiState.loopNextTime = faiWaitTime;
1780 else if(ev.
weaponCh==zenkit::MdsFightMode::SINGLE_HANDED || ev.
weaponCh==zenkit::MdsFightMode::DUAL_HANDED) {
1788 else if(ev.
weaponCh==zenkit::MdsFightMode::BOW || ev.
weaponCh==zenkit::MdsFightMode::CROSSBOW) {
1794 updateWeaponSkeleton();
1797bool Npc::implAiFlee(uint64_t dt) {
1798 if(currentTarget==
nullptr)
1804 auto& oth = *currentTarget;
1807 const float maxDist = 5*100;
1829 if(implTurnTo(-dx,-dz,anim,dt))
1832 auto dx = wp->
pos.x-x;
1833 auto dz = wp->
pos.z-z;
1834 if(implTurnTo(dx,dz,anim,dt))
1843bool Npc::setGoToLadder() {
1844 if(go2.wp==
nullptr || go2.wp!=wayPath.
first())
1846 auto inter = go2.wp->ladder;
1847 auto pos = inter->nearestPoint(*
this);
1849 if(!inter->isAvailable())
1858void Npc::commitDamage() {
1859 if(currentTarget==
nullptr)
1872 assert(b==
nullptr || !b->
isSpell());
1873 const auto& pose = visual.
pose();
1875 const bool isBlock = (!other.
isMonster() || other.
inventory().activeWeapon()!=
nullptr) &&
1884 if(!(isBlock || isJumpb) || b!=
nullptr) {
1896 lastHitSpell = splId;
1908 float a = angleDir(other.x-x,other.z-z);
1910 if(std::cos(da*M_PI/180.0)<0)
1911 lastHitType=
'A';
else
1917 const bool dontKill = ((b==
nullptr && splId==0) || (bMask &
COLL_DONTKILL)) && (!
isSwim());
1923 damageType = spl.damage_type;
1924 for(
size_t i=0; i<zenkit::DamageType::NUM; ++i)
1925 if((damageType&(1<<i))!=0)
1926 dmg[i] = spl.damage_per_level;
1946 const bool noInter = (hnpc->bodystate_interruptable_override!=0);
1953 if(damageType & (1<<zenkit::DamageType::FLY))
1959 if(hitResult.
value>0) {
1960 currentOther = &other;
1979 if((damageType & (1<<zenkit::DamageType::FLY)) && !
isLie()) {
1984void Npc::takeFallDamage(
const Vec3& fallSpeed) {
1990 const float a = angleDir(-fallSpeed.x,-fallSpeed.z);
1991 const float da = a-angle;
1992 if(std::cos(da*M_PI/180.0)<0 || Vec2(fallSpeed.x,fallSpeed.z).length()<0.1f)
1993 lastHitType=
'A';
else
1995 setAnim(lastHitType==
'A' ? Anim::FallenA :
Anim::FallenB);
2009void Npc::takeDrownDamage() {
2013Npc *Npc::updateNearestEnemy() {
2018 float dist = std::numeric_limits<float>::max();
2019 if(nearestEnemy!=
nullptr &&
2036 return nearestEnemy;
2039Npc* Npc::updateNearestBody() {
2044 float dist = std::numeric_limits<float>::max();
2060 if(ev.
timed.empty())
2064 return a.time<b.time;
2068 for(
auto& i:ev.timed) {
2070 case zenkit::MdsEventType::ITEM_CREATE: {
2072 invent.
putToSlot(*
this,it->clsId(),i.slot[0]);
2076 case zenkit::MdsEventType::ITEM_INSERT: {
2080 case zenkit::MdsEventType::ITEM_REMOVE:
2081 case zenkit::MdsEventType::ITEM_DESTROY: {
2082 invent.
clearSlot(*
this,
"", i.def != zenkit::MdsEventType::ITEM_REMOVE);
2085 case zenkit::MdsEventType::ITEM_PLACE: {
2086 if(currentInteract!=
nullptr)
2090 case zenkit::MdsEventType::ITEM_EXCHANGE: {
2091 if(!invent.
clearSlot(*
this,i.slot[0],
true)) {
2097 invent.
putToSlot(*
this,it->clsId(),i.slot[0]);
2101 case zenkit::MdsEventType::SET_FIGHT_MODE:
2103 case zenkit::MdsEventType::MUNITION_PLACE: {
2105 if(active!=
nullptr) {
2106 const int32_t munition = active->
handle().munition;
2111 case zenkit::MdsEventType::MUNITION_REMOVE: {
2115 case zenkit::MdsEventType::TORCH_DRAW:
2118 case zenkit::MdsEventType::TORCH_INVENTORY:
2121 case zenkit::MdsEventType::TORCH_DROP:
2124 case zenkit::MdsEventType::SOUND_DRAW:
2126 case zenkit::MdsEventType::SOUND_UNDRAW:
2128 case zenkit::MdsEventType::MESH_SWAP:
2130 case zenkit::MdsEventType::HIT_LIMB:
2132 case zenkit::MdsEventType::HIT_DIRECTION:
2134 case zenkit::MdsEventType::DAMAGE_MULTIPLIER:
2136 case zenkit::MdsEventType::PARRY_FRAME:
2138 case zenkit::MdsEventType::OPTIMAL_FRAME:
2140 case zenkit::MdsEventType::HIT_END:
2142 case zenkit::MdsEventType::COMBO_WINDOW:
2144 case zenkit::MdsEventType::UNKNOWN:
2150void Npc::tickRegen(int32_t& v,
const int32_t max,
const int32_t chg,
const uint64_t dt) {
2152 if(
tick<dt || chg==0)
2154 int32_t time0 = int32_t(
tick%1000);
2155 int32_t time1 = time0+int32_t(dt);
2157 int32_t val0 = (time0*chg)/1000;
2158 int32_t val1 = (time1*chg)/1000;
2160 int32_t nextV = std::max(0,std::min(v+val1-val0,max));
2164 checkHealth(
true,
false);
2170 const bool hasEvents = visual.
processEvents(owner,lastEventTime,ev);
2172 visual.
setNpcEffect(owner,*
this,hnpc->effect,hnpc->flags);
2176 for(
auto& i:ev.
morph)
2182 implSetFightMode(ev);
2187 static bool dbg =
false;
2188 static int kId = -1;
2189 if(dbg && !
isPlayer() && hnpc->id!=kId)
2200 uint32_t gl =
guild();
2201 int32_t v =
world().script().guildVal().dive_time[gl]*1000;
2203 if(v>=0 && t>v+
int(dt)) {
2204 int tickSz =
world().script().npcDamDiveTime();
2207 int dmg = t/tickSz - (t-int(dt))/tickSz;
2214 nextAiAction(aiQueueOverlay,dt);
2228 adjustAttackRotation(dt);
2251bool Npc::prepareTurn() {
2265void Npc::nextAiAction(
AiQueue& queue, uint64_t dt) {
2270 auto act = queue.
pop();
2274 currentLookAt=
nullptr;
2275 currentLookAtNpc=act.
target;
2279 currentLookAtNpc=
nullptr;
2280 currentLookAt=act.
point;
2284 if(!prepareTurn()) {
2288 if(act.
target!=
nullptr && implTurnAway(*act.
target,dt)) {
2295 if(!prepareTurn()) {
2299 if(act.
target!=
nullptr && implTurnTo(*act.
target,dt)) {
2310 if(!prepareTurn()) {
2314 if(act.
target!=
nullptr && implWhirlTo(*act.
target,dt)) {
2325 currentFp =
nullptr;
2326 currentFpLock =
FpLock();
2338 currentFpLock =
FpLock(*fp);
2351 auto wpoint = wayPath.
pop();
2353 if(wpoint!=
nullptr) {
2364 currentLookAtNpc=
nullptr;
2365 currentLookAt=
nullptr;
2371 stopWalkAnimation();
2390 implAniWait(uint64_t(sq->totalTime()));
2400 implAniWait(uint64_t(sq->totalTime()));
2415 implAiWait(uint64_t(act.
i0));
2465 if(inter==
nullptr) {
2475 if(currentInteract!=
nullptr && inter!=currentInteract) {
2481 if(inter!=
nullptr) {
2482 auto pos = inter->nearestPoint(*
this);
2496 if(currentInteract==
nullptr || currentInteract->
stateId()!=act.
i0) {
2516 uint32_t itm = uint32_t(act.
i0);
2520 if(!invent.
putState(*
this,state>=0 ? itm : 0,state))
2521 queue.pushFront(std::move(act));
2554 const int32_t spell = act.
i0;
2556 aiExpectedInvest = act.
i1;
else
2562 if(currentTarget!=
nullptr) {
2574 implAniWait(uint64_t(sq->totalTime()));
2588 if(performOutput(act)) {
2590 uint64_t msgTime = 0;
2609 const int PERC_DIST_DIALOG = 2000;
2624 if(act.
target->
qDistTo(*
this)>PERC_DIST_DIALOG*PERC_DIST_DIALOG) {
2629 act.
target->stopWalkAnimation();
2631 stopWalkAnimation();
2637 act.
target->outputPipe = p;
2644 if(outputPipe->
close()) {
2646 if(currentOther!=
nullptr)
2657 if(
auto fp = currentFp){
2658 if(fp->dir.x!=0.f || fp->dir.z!=0.f){
2666 const int32_t r = act.
i0*act.
i0;
2672 if(qDistTo(other)>float(r))
2674 other.aiPush(AiQueue::aiStartState(act.func,1,other.currentOther,other.currentVictim,other.hnpc->wp));
2696 else if(!implTurnTo(*act.
target,dt)) {
2707 if(act.
item==
nullptr)
2718 if(act.
point==
nullptr)
2739 int timesec = act.
i2;
2742 bool complete =
false;
2743 if(aiOutputBarrier<=owner.
tickCount()) {
2744 if(outputPipe->
printScr(*
this,timesec,msg,posx,posy,font))
2763 if(aiState.funcIni==
id) {
2766 aiState.started =
false;
2794 if(
isPlayer() && !isPlayerEnabledState(st))
2797 aiState.started =
false;
2798 aiState.funcIni = st.funcIni;
2799 aiState.funcLoop = st.funcLoop;
2800 aiState.funcEnd = st.funcEnd;
2802 aiState.eTime = endTime;
2803 aiState.loopNextTime = owner.
tickCount();
2804 aiState.hint = st.name();
2809 if(aiState.funcIni.isValid() && aiState.started) {
2812 aiPrevState = aiState.funcIni;
2819bool Npc::isPlayerEnabledState(const ::AiState& st)
const {
2820 static const std::array playerEnabledStatesG1 = {
2821 "ZS_DEAD",
"ZS_UNCONSCIOUS",
"ZS_MAGICFREEZE",
2822 "ZS_PYRO",
"ZS_ASSESSMAGIC",
"ZS_ASSESSSTOPMAGIC",
2823 "ZS_ZAPPED",
"ZS_SHORTZAPPED",
"ZS_MAGICSLEEP",
2826 static const std::array playerEnabledStatesG2 = {
2827 "ZS_DEAD",
"ZS_UNCONSCIOUS",
"ZS_MAGICFREEZE",
2828 "ZS_PYRO",
"ZS_ASSESSMAGIC",
"ZS_ASSESSSTOPMAGIC",
2829 "ZS_ZAPPED",
"ZS_SHORTZAPPED",
"ZS_MAGICSLEEP",
2837 const auto& playerEnabledStates = (owner.
version().game==2 ? playerEnabledStatesG2 : playerEnabledStatesG1);
2838 for(
auto* pState:playerEnabledStates)
2839 if(sym->name()==pState)
2844void Npc::tickRoutine() {
2845 if(!aiState.funcIni.isValid() && !
isPlayer()) {
2846 auto r = currentRoutine();
2847 if(r.callback.isValid()) {
2848 auto t = endTime(r);
2849 startState(r.callback, r.wayPointName(), t,
false);
2851 else if(hnpc->start_aistate!=0) {
2852 auto endTime = owner.
time();
2854 startState(uint32_t(hnpc->start_aistate),
"", endTime,
false);
2858 if(!aiState.funcIni.isValid())
2861 auto& sc = owner.
script();
2862 if(!aiState.started) {
2863 aiState.started =
true;
2864 aiState.loopNextTime = owner.
tickCount();
2867 sc.invokeState(
this,currentOther,currentVictim,aiState.funcIni);
2872 if(aiState.loopNextTime<=owner.
tickCount()) {
2873 aiState.loopNextTime = owner.
tickCount() + 1000;
2875 if(aiState.funcLoop.isValid()) {
2876 static const float MAX_DIST = 300;
2877 if(fastPath && currentFp!=
nullptr &&
qDistTo(currentFp) < MAX_DIST*MAX_DIST) {
2880 else if(fastPath && currentFp!=
nullptr) {
2882 loop = sc.invokeState(
this,currentOther,currentVictim,aiState.funcLoop);
2885 loop = sc.invokeState(
this,currentOther,currentVictim,aiState.funcLoop);
2890 loop = owner.
version().hasZSStateLoop() ? 1 : 0;
2893 if(aiState.eTime<=owner.
time()) {
2895 if(currentTarget==
nullptr && outputPipe->
isFinished())
2901 currentOther =
nullptr;
2902 currentVictim =
nullptr;
2908 if(currentTarget==t)
2917 return currentTarget;
2921 nearestEnemy =
nullptr;
2926 Log::e(
"unxepected perc acton");
2937 return aiOutputOrderId()!=std::numeric_limits<int>::max();
2943 outWaitTime = aiOutputBarrier;
2962 std::snprintf(frm,
sizeof(frm),
"%.*s",
int(svm.size()),svm.data());
2965 std::snprintf(name,
sizeof(name),frm,
int(hnpc->voice));
2990 const bool g2 = owner.
version().game==2;
3011 auto active = invent.
getItem(currentSpellCast);
3012 if(active==
nullptr || !active->isSpellOrRune())
3015 const int32_t splId = active->
spellId();
3021 if(active->isSpellShoot()) {
3022 const int lvl = (castLevel-
CS_Emit_0)+1;
3024 for(
size_t i=0; i<zenkit::DamageType::NUM; ++i)
3025 if((spl.damage_type&(1<<i))!=0) {
3026 dmg[i] = spl.damage_per_level*lvl;
3029 auto& b = owner.
shootSpell(*active, *
this, currentTarget);
3040 e.setTarget((currentTarget==
nullptr) ?
this : currentTarget);
3041 e.setSpellId(splId,owner);
3046 if(currentTarget!=
nullptr) {
3047 currentTarget->lastHitSpell = splId;
3052 if(active->isSpell()) {
3053 size_t cnt = active->count();
3054 invent.
delItem(active->clsId(),1,*
this);
3056 Item* spl =
nullptr;
3057 for(uint8_t i=0;i<8;++i) {
3072 if(spellInfo!=0 && transformSpl==
nullptr) {
3079 hnpc->level = transformSpl->hnpc->level;
3083const Npc::Routine& Npc::currentRoutine(
bool assertWp)
const {
3087 for(
auto& i:routines) {
3088 if(assertWp && i.point==
nullptr)
3090 if(i.end<i.start && (time<i.end || i.start<=time))
3092 if(i.start<=time && time<i.end)
3097 const Routine* rtn =
nullptr;
3098 int64_t delta = std::numeric_limits<int64_t>::max();
3099 for(
auto& i:routines) {
3100 if(assertWp && i.point==
nullptr)
3102 int64_t d = time.toInt() - i.end.toInt();
3119 if(routines.empty())
3121 return currentRoutine(
true).point;
3124gtime Npc::endTime(
const Npc::Routine &r)
const {
3125 auto wtime = owner.
time();
3131 return gtime(wtime.day(),r.end.hour(),r.end.minute());
3132 return gtime(wtime.day()+1,r.end.hour(),r.end.minute());
3135 if(r.end.hour()==0 || r.end<time)
3136 return gtime(wtime.day()+1,r.end.hour(),r.end.minute());
else
3137 return gtime(wtime.day(),r.end.hour(),r.end.minute());
3139 if(r.start==r.end && r.end.toInt()==0) {
3141 return gtime(wtime.day()+1,r.end.hour(),r.end.minute());
3170 return s==i->stateMask();
3186 invent.
equip(item,*
this,
true);
3190 if(w==
nullptr || w->clsId()!=item)
3201 updateWeaponSkeleton();
3214 updateWeaponSkeleton();
3220 aiQueueOverlay.
pushBack(std::move(a));
else
3226 auto& r = currentRoutine();
3227 if(r.callback.isValid()) {
3228 auto t = endTime(r);
3229 startState(r.callback,r.wayPointName(),t,
false);
3234 return invent.
addItem(item,count,owner);
3238 return invent.
addItem(std::move(i));
3259 std::unique_ptr<Item> ptr = owner.
takeItem(item);
3260 if(ptr!=
nullptr && ptr->isTorchBurn()) {
3264 if(torchId!=
size_t(-1))
3269 auto it = ptr.get();
3310 int32_t price = from.invent.
priceOf(
id);
3311 if(price>0 &&
size_t(price)*count>invent.
goldCount()) {
3312 count = invent.
goldCount()/size_t(price);
3338 size_t rightHand = sk->
findNode(
"ZS_RIGHTHAND");
3339 if(rightHand==
size_t(-1))
3347 mat = visual.
pose().
bone(rightHand);
3349 auto it = owner.
addItemDyn(
id,mat,hnpc->symbol_index());
3351 invent.
delItem(
id,count,*
this);
3384 size_t id = sk->findNode(bone);
3400 step *= (float(dt)/1000.f);
3402 if(dx==0.f && dz==0.f) {
3410 float a = angleDir(dx,dz);
3414 if(
float(std::abs(
int(da)%180))<=(step*2.f)) {
3423 const auto sgn = std::sin(
double(da)*M_PI/180.0);
3427 const int rot = (sgn<0) ? +1 : -1;
3467 invent.
delItem(item,amount,*
this);
3475 invent.
use(item,*
this,slotHint,force);
3503 updateWeaponSkeleton();
3508 currentSpellCast = size_t(-1);
3617 updateWeaponSkeleton();
3654 if(
auto sq = visual.
continueCombo(*
this,anim,bs,weaponSt,wlk)) {
3671 return setAnim(Anim::AttackBlock);
3675 if(currentTarget==
nullptr || !
canFinish(*currentTarget))
3680 currentTarget->checkHealth(
true,
false);
3733 currentSpellCast = active->clsId();
3735 hnpc->aivar[88] = 0;
3744 currentSpellCast = size_t(-1);
3753 Log::d(
"Couldn't start animation for spell '",currentSpellCast,
"'");
3762 Log::d(
"unexpected Spell_ProcessMana result: '",
int(code),
"' for spell '",currentSpellCast,
"'");
3770bool Npc::tickCast(uint64_t dt) {
3774 auto active = currentSpellCast!=size_t(-1) ? invent.
getItem(currentSpellCast) :
nullptr;
3776 if(currentSpellCast!=
size_t(-1)) {
3777 if(active==
nullptr || !active->isSpellOrRune() ||
isDown()) {
3780 currentSpellCast = size_t(-1);
3785 if(!
isPlayer() && currentTarget!=
nullptr) {
3792 if(active!=
nullptr) {
3794 bool g2 = owner.
version().game==2;
3811 currentSpellCast = size_t(-1);
3834 if(!
isPlayer() && aiExpectedInvest<=manaInvested) {
3844 int32_t castLvl = int(castLevel)-int(
CS_Invest_0);
3850 castNextTime += uint64_t(spl.time_per_mana);
3863 Log::d(
"unexpected Spell_ProcessMana result: '",
int(code),
"' for spell '",currentSpellCast,
"'");
3872 int32_t castLvl = int(castLevel)-int(
CS_Invest_0);
3916 const int32_t munition = active->handle().munition;
3923 auto itm = invent.
getItem(
size_t(munition));
3927 auto& b = owner.
shootBullet(*itm,*
this,currentTarget,focOverride);
3929 invent.
delItem(
size_t(munition),1,*
this);
3936 if(rgn!=
nullptr && rgn->isCrossbow())
3941 if(rgn!=
nullptr && rgn->isCrossbow())
3952 const int32_t munition = active->
handle().munition;
3953 if(munition<0 || invent.
itemCount(
size_t(munition))<=0)
3995 return hnpc->flags & zenkit::NpcFlag::IMMORTAL;
3999 perceptionTime = time;
4002uint64_t Npc::perceptionTimeClampt()
const {
4003 return std::max<uint64_t>(perceptionTime, 1);
4008 perception[t].func = fn;
4024 static bool dbg =
false;
4025 static int kId = -1;
4026 if(dbg && hnpc->id!=kId)
4034 perceptionNextTime = owner.
tickCount()+perceptionTimeClampt();
4038 const float quadDist = pl.
qDistTo(*
this);
4051 nearestEnemy =
nullptr;
4064 perceptionNextTime = owner.
tickCount()+perceptionTimeClampt();
4069 if(!aiState.started) {
4074 float r = float(
world().script().percRanges().at(perc, hnpc->senses_range));
4086 if(defaultFn.isValid())
4094 return perception[perc].func.isValid();
4098 return perceptionNextTime;
4102 if(currentInteract==
id)
4105 if(currentInteract!=
nullptr) {
4106 return currentInteract->
detach(*
this,quick);
4110 return (currentInteract==
nullptr);
4113 currentInteract = id;
4125 if(currentInteract==
nullptr)
4130 currentInteract=
nullptr;
4136 invTorch = !invTorch;
4146 if(currentInteract!=
nullptr)
4147 return currentInteract;
4148 if((moveMobCacheKey-
position()).quadLength()<10.f*10.f)
4154 return aiState.funcIni==stateFn;
4158 auto& rout = currentRoutine();
4159 return rout.callback==stateFn && aiState.funcIni==stateFn;
4163 return aiPrevState==stateFn;
4171 aiState.sTime = owner.
tickCount()-uint64_t(time);
4175 auto wp =
world().findPoint(point,
false);
4180 r.callback = callback;
4183 r.fallbackName = point;
4184 routines.push_back(r);
4186 std::stable_sort(routines.begin(), routines.end(), [](
const Routine& l,
const Npc::Routine& r) {
4187 return l.start < r.start;
4221 switch(physic.
tryMove(to, out)) {
4229 setViewPosition(to);
4238 float s = std::sin(rot), c = std::cos(rot);
4239 Vec3 dp = Vec3{len*s, 0, -len*c};
4246 ret.
anim = Anim::Idle;
4250 const float jumpLow = float(g.jumplow_height[gl]);
4251 const float jumpMid = float(g.jumpmid_height[gl]);
4252 const float jumpUp = float(g.jumpup_height[gl]);
4260 ret.
anim = Anim::Jump;
4265 auto lnd = owner.
physic()->
landRay(pos0 + dp + Vec3(0, jumpUp + jumpLow, 0));
4266 float jumpY = lnd.
v.y;
4267 auto pos1 = Vec3(pos0.x,jumpY,pos0.z);
4268 auto pos2 = pos1 + dp;
4270 float dY = jumpY - y;
4273 !physic.
testMove(pos2,pos1,info)) {
4274 ret.
anim = Anim::JumpUp;
4280 if(!physic.
testMove(pos1,pos0,info) ||
4281 !physic.
testMove(pos2,pos1,info)) {
4283 ret.
anim = Anim::Jump;
4288 if(dY>=jumpUp || dY>=jumpMid) {
4290 ret.
anim = Anim::JumpUp;
4296 if(mvAlgo.
testSlide(Vec3{pos0.x,jumpY,pos0.z}+dp,out)) {
4298 ret.
anim = Anim::Jump;
4305 ret.
anim = Anim::JumpHang;
4311 ret.
anim = Anim::Idle;
4318 ret.
anim = Anim::JumpUpLow;
4325 ret.
anim = Anim::JumpUpMid;
4338 if(transformSpl==
nullptr)
4340 transformSpl->undo(*
this);
4350 transformSpl.reset();
4358 return aiQueue.
size()==0 &&
4370 currentLookAt =
nullptr;
4371 currentLookAtNpc =
nullptr;
4375 aiQueueOverlay.
clear();
4386 currentFpLock =
FpLock(currentFp);
4400 stopWalkAnimation();
4407 const auto ppos = oth.physic.
position();
4427 if(currentLookAtNpc!=
nullptr)
4428 return canSeeNpc(*currentLookAtNpc,
false);
4437 const float range = float(hnpc->senses_range) + extRange;
4441 static const double ref = std::cos(100*M_PI/180.0);
4449 float dx = x-pos.x, dz=z-pos.z;
4450 float dir = angleDir(dx,dz);
4452 if(
double(std::cos(da))<=ref) {
4463 const bool isNoisy =
false;
4469 const float range = float(hnpc->senses_range)+extRange;
4489 static const double ref = std::cos(100*M_PI/180.0);
4492 const float range = float(hnpc->senses_range);
4493 if(
qDistTo(itMid)>range*range)
4497 float dx = x-itMid.x, dz=z-itMid.z;
4498 float dir = angleDir(dx,dz);
4500 if(
double(std::cos(da))>ref)
4506 auto r = w->
ray(head,itMid);
4507 auto err = (head-itMid)*(1.f-r.hitFraction);
4508 if(!r.hasCol || err.length()<25.f) {
4511 if(y<=itMid.y && itMid.y<=head.y) {
4512 auto pl = Vec3(head.x,itMid.y,head.z);
4513 r = w->
ray(pl,itMid);
4514 err = (pl-itMid)*(1.f-r.hitFraction);
4515 if(!r.hasCol || err.length()<65.f)
4521bool Npc::isAlignedToGround()
const {
4526Vec3 Npc::groundNormal()
const {
4528 const bool align = isAlignedToGround();
4537Matrix4x4 Npc::mkPositionMatrix()
const {
4538 const auto ground = groundNormal();
4539 const bool align = isAlignedToGround();
4541 float angY = mvAlgo.
isDive() ? angleY : 0;
4544 float s = std::sin(rot), c = std::cos(rot);
4545 auto dir = Vec3(s,0,-c);
4546 auto norm = Vec3::normalize(ground);
4548 float cx = Vec3::dotProduct(norm,dir);
4549 angY = -std::asin(cx)*180.f/float(M_PI);
4552 Matrix4x4 mt = Matrix4x4();
4554 mt.translate(x,y,z);
4555 mt.rotateOY(180-angle);
4559 mt.rotateOZ(runAng);
4561 mt.scale(sz[0],sz[1],sz[2]);
4571 if(
isPlayer() && camera!=
nullptr && camera->isFree())
4575 const auto ground = groundNormal();
4576 if(lastGroundNormal!=ground) {
4577 durtyTranform |= TR_Rot;
4578 lastGroundNormal = ground;
4583 if(durtyTranform==TR_Pos) {
4589 pos = mkPositionMatrix();
4594 float y = pos.at(3,1);
4595 pos.set(3,1,y+chest);
virtual bool printScr(Npc &npc, int time, std::string_view msg, int x, int y, std::string_view font)=0
virtual bool outputSvm(Npc &npc, std::string_view text)=0
virtual bool outputOv(Npc &npc, std::string_view text)=0
virtual bool isFinished()=0
virtual bool output(Npc &npc, std::string_view text)=0
void pushFront(AiAction &&a)
int aiOutputOrderId() const
void onWldItemRemoved(const Item &itm)
static AiAction aiRemoveWeapon()
void pushBack(AiAction &&a)
void save(Serialize &fout) const
void load(Serialize &fin)
void setObjMatrix(const Tempest::Matrix4x4 &m)
void setHitChance(float v)
void setCritChance(float v)
void setTarget(const Npc *n)
void setDamage(DamageCalculator::Damage d)
static auto rangeDamageValue(Npc &src) -> Damage
static Val damageValue(Npc &src, Npc &other, const Bullet *b, bool isSpell, const DamageCalculator::Damage &splDmg, const CollideMask bMsk)
static Val damageFall(Npc &src, float speed)
static int32_t damageTypeMask(Npc &npc)
RayLandResult landRay(const Tempest::Vec3 &from, float maxDy=0) const
RayLandResult ray(const Tempest::Vec3 &from, const Tempest::Vec3 &to) const
NpcItem ghostObj(std::string_view visual)
RayQueryResult rayNpc(const Tempest::Vec3 &from, const Tempest::Vec3 &to, const Npc *except) const
void setTarget(const Npc *npc)
static void onCollide(World &owner, const VisualFx *root, const Tempest::Vec3 &pos, Npc *npc, Npc *other, int32_t splId)
void load(Serialize &fin)
Action nextFromQueue(Npc &npc, Npc &tg, GameScript &owner)
bool isInWRange(const Npc &npc, const Npc &tg, GameScript &owner) const
float prefferedAttackDistance(const Npc &npc, const Npc &tg, GameScript &owner) const
bool hasInstructions() const
void save(Serialize &fout)
bool fetchInstructions(Npc &npc, Npc &tg, GameScript &owner)
float attackFinishDistance(GameScript &owner) const
bool isInAttackRange(const Npc &npc, const Npc &tg, GameScript &owner) const
bool isInFinishRange(const Npc &npc, const Npc &tg, GameScript &owner) const
bool isInGRange(const Npc &npc, const Npc &tg, GameScript &owner) const
bool isInFocusAngle(const Npc &npc, const Npc &tg) const
void invokeRefreshAtInsert(Npc &npc)
size_t findSymbolIndex(std::string_view s)
int invokeMana(Npc &npc, Npc *target, int mana)
bool isTalk(const Npc &pl)
std::string_view spellCastAnim(Npc &npc, Item &fn)
uint32_t messageTime(std::string_view id) const
bool isFriendlyFire(const Npc &src, const Npc &dst) const
const AiState & aiState(ScriptFn id)
void fixNpcPosition(Npc &npc, float angle0, float distBias)
void invokeState(const std::shared_ptr< zenkit::INpc > &hnpc, const std::shared_ptr< zenkit::INpc > &hother, const char *name)
zenkit::DaedalusSymbol * findSymbol(std::string_view s)
const zenkit::ISpell & spellDesc(int32_t splId)
std::string_view messageFromSvm(std::string_view id, int voice) const
AiOuputPipe * openAiOuput()
AiOuputPipe * openDlgOuput(Npc &player, Npc &npc)
bool isUnconscious(const Npc &pl)
ScriptFn playerPercAssessMagic()
void invokeSpell(Npc &npc, Npc *target, Item &fn)
const zenkit::IGuildValues & guildVal() const
uint32_t rand(uint32_t max)
Attitude personAttitude(const Npc &p0, const Npc &p1) const
int invokeManaRelease(Npc &npc, Npc *target, int mana)
void printCannotBuyError(Npc &npc)
auto dialogChoices(std::shared_ptr< zenkit::INpc > self, std::shared_ptr< zenkit::INpc > npc, const std::vector< uint32_t > &except, bool includeImp) -> std::vector< DlgChoice >
const VisualFx * spellVfx(int32_t splId)
bool isAttack(const Npc &pl) const
auto canNpcCollideWithSpell(Npc &npc, Npc *shooter, int32_t spellId) -> CollideMask
void initializeInstanceNpc(const std::shared_ptr< zenkit::INpc > &npc, size_t instance)
bool isDead(const Npc &pl)
void eventPlayAni(Npc &npc, std::string_view ani)
Tempest::Vec3 nearestPoint(const Npc &to) const
bool detach(Npc &npc, bool quick)
bool isAttached(const Npc &to)
void unequipArmor(GameScript &vm, Npc &owner)
int32_t sellPriceOf(size_t item) const
void clear(GameScript &vm, Npc &owner, bool includeMissionItm=false)
void equipBestArmor(Npc &owner)
void setCurrentItem(size_t cls)
void equipArmor(int32_t cls, Npc &owner)
bool putState(Npc &owner, size_t cls, int state)
Item * currentSpell(uint8_t s)
void equipBestMeleeWeapon(Npc &owner)
bool equip(size_t cls, Npc &owner, bool force)
void putToSlot(Npc &owner, size_t cls, std::string_view slot)
Item * currentRangedWeapon()
void switchActiveWeaponFist()
static void transfer(Inventory &to, Inventory &from, Npc *fromNpc, size_t cls, size_t count, World &wrld)
void invalidateCond(Npc &owner)
bool unequip(size_t cls, Npc &owner)
bool use(size_t cls, Npc &owner, uint8_t slotHint, bool force)
const Item * activeWeapon() const
void switchActiveWeapon(Npc &owner, uint8_t slot)
void switchActiveSpell(int32_t spell, Npc &owner)
void putAmmunition(Npc &owner, size_t cls, std::string_view slot)
Item * addItem(std::unique_ptr< Item > &&p)
Item * currentMeleeWeapon()
void delItem(size_t cls, size_t count, Npc &owner)
int32_t priceOf(size_t item) const
static void moveItem(Npc &owner, Inventory &invNpc, Interactive &mobsi)
void putCurrentToSlot(Npc &owner, std::string_view slot)
void equipBestRangedWeapon(Npc &owner)
void load(Serialize &s, Npc &owner)
void unequipWeapons(GameScript &vm, Npc &owner)
void autoEquipWeapons(Npc &owner)
bool clearSlot(Npc &owner, std::string_view slot, bool remove)
bool hasMissionItems() const
void save(Serialize &s) const
size_t itemCount(const size_t id) const
Item * getItem(size_t instance)
void updateView(Npc &owner)
Tempest::Vec3 midPosition() const
virtual bool isTorchBurn() const
void setCount(size_t cnt)
const zenkit::IItem & handle() const
Tempest::Vec3 position() const
std::string_view visualSkeletonScheme() const
void setMagicWeaponKey(World &owner, SpellFxKey key, int32_t keyLvl=0)
bool hasAnim(std::string_view scheme) const
void setArmor(Npc &npc, MeshObjects::Mesh &&armor)
void setBody(Npc &npc, MeshObjects::Mesh &&body, const int32_t version)
void processLayers(World &world)
void stopDlgAnim(Npc &npc)
bool isUsingTorch() const
void setHeadRotation(float dx, float dz)
auto mapBone(const size_t boneId) const -> Tempest::Vec3
void stopAnim(Npc &npc, std::string_view anim)
void setVisualBody(World &owner, MeshObjects::Mesh &&body)
const Skeleton * visualSkeleton() const
void stopWalkAnim(Npc &npc)
auto mapWeaponBone() const -> Tempest::Vec3
void startEffect(World &owner, Effect &&pfx, int32_t slot, bool noSlot)
void setAmmoItem(MeshObjects::Mesh &&ammo, std::string_view bone)
float viewDirection() const
void setObjMatrix(const Tempest::Matrix4x4 &m, bool syncAttach=false)
void clearSlotItem(std::string_view bone)
void setNpcEffect(World &owner, Npc &npc, std::string_view s, zenkit::NpcFlag flags)
const Animation::Sequence * startAnimItem(Npc &npc, std::string_view scheme, int state)
bool startAnim(Npc &npc, WeaponState st)
const Animation::Sequence * startAnimAndGet(std::string_view name, uint64_t tickCount, bool forceAnim=false)
void save(Serialize &fout, const Npc &npc) const
void setSword(MeshObjects::Mesh &&sword)
void setSlotItem(MeshObjects::Mesh &&itm, std::string_view bone)
bool updateAnimation(Npc *npc, Interactive *mobsi, World &world, uint64_t dt, bool force)
bool startAnimDialog(Npc &npc)
void delOverlay(std::string_view sk)
auto mapHeadBone() const -> Tempest::Vec3
void setVisual(const Skeleton *visual)
void setStateItem(MeshObjects::Mesh &&itm, std::string_view bone)
bool setToFightMode(const WeaponState ws)
Tempest::Vec3 displayPosition() const
const Pose & pose() const
void setRangedWeapon(MeshObjects::Mesh &&bow)
WeaponState fightMode() const
void setShield(MeshObjects::Mesh &&shield)
bool setFightMode(zenkit::MdsFightMode mode)
bool processEvents(World &world, uint64_t &barrier, Animation::EvCount &ev)
const Tempest::Matrix4x4 & transform() const
void dropShield(Npc &owner)
Tempest::Vec2 headRotation() const
void addOverlay(const Skeleton *sk, uint64_t time)
void setAnimWhirl(Npc &npc, int dir)
void stopEffect(const VisualFx &vfx)
bool stopItemStateAnim(Npc &npc)
const Animation::Sequence * startAnimSpell(Npc &npc, std::string_view scheme, bool invest)
const Animation::Sequence * continueCombo(Npc &npc, AnimationSolver::Anim a, BodyState bs, WeaponState st, WalkBit wlk)
void load(Serialize &fin, Npc &npc)
void setAnimRotate(Npc &npc, int dir)
void updateWeaponSkeleton(const Item *sword, const Item *bow)
bool hasOverlay(const Skeleton *sk) const
bool isAnimExist(std::string_view name) const
void setMagicWeapon(Effect &&spell, World &owner)
void startFaceAnim(Npc &npc, std::string_view anim, float intensity, uint64_t duration)
void dropWeapon(Npc &owner)
void startMMAnim(Npc &npc, std::string_view anim, std::string_view node)
void emitBlockEffect(Npc &dst, Npc &src)
void setTorch(bool t, World &owner)
static const float closeToPointThreshold
zenkit::MaterialGroup groundMaterial() const
bool canFlyOverWater() const
auto groundNormal() const -> Tempest::Vec3
auto formerPortalName() -> std::string_view
void load(Serialize &fin)
bool testSlide(const Tempest::Vec3 &p, DynamicWorld::CollisionTest &out, bool cont=false) const
auto portalName() -> std::string_view
void save(Serialize &fout) const
static const float climbMove
bool startClimb(JumpStatus ani)
void accessDamFly(float dx, float dz)
bool checkLastBounce() const
static bool isClose(const Npc &npc, const Npc &p, float dist)
void tick(uint64_t dt, MvFlags fai=NoFlag)
auto mapHeadBone() const -> Tempest::Vec3
auto mapWeaponBone() const -> Tempest::Vec3
bool startState(ScriptFn id, std::string_view wp)
auto canSenseNpc(const Npc &oth, bool freeLos, float extRange=0.f) const -> SensesBit
auto cameraBone(bool isFirstPerson=false) const -> Tempest::Vec3
void setToFightMode(const size_t item)
Npc(World &owner, size_t instance, std::string_view waypoint, NpcProcessPolicy aiPolicy=NpcProcessPolicy::AiNormal)
void startDialog(Npc &other)
void setTalentSkill(Talent t, int32_t lvl)
auto mapBone(std::string_view bone) const -> Tempest::Vec3
float qDistTo(const Tempest::Vec3 pos) const
bool hasOverlay(std::string_view sk) const
int32_t protection(Protection p) const
auto displayPosition() const -> Tempest::Vec3
auto weaponState() const -> WeaponState
bool canSeeSource() const
bool doAttack(Anim anim, BodyState bs)
bool startClimb(JumpStatus jump)
void stopAnim(std::string_view ani)
auto currentTaPoint() const -> const WayPoint *
void addRoutine(gtime s, gtime e, uint32_t callback, std::string_view point)
void setRangedWeapon(MeshObjects::Mesh &&bow)
void takeDamage(Npc &other, const Bullet *b)
auto playAnimByName(std::string_view name, BodyState bs) -> const Animation::Sequence *
bool tryTranslate(const Tempest::Vec3 &to)
bool isInState(ScriptFn stateFn) const
void invalidateTalentOverlays()
bool canSeeNpc(const Npc &oth, bool freeLos) const
void setActiveSpellInfo(int32_t info)
bool drawMage(uint8_t slot)
int32_t experience() const
bool checkGoToNpcdistance(const Npc &other)
void setWalkMode(WalkBit m)
void setShield(MeshObjects::Mesh &&shield)
void emitSoundSVM(std::string_view sound)
Npc * lookAtTarget() const
bool wasInState(ScriptFn stateFn) const
void setVisualBody(int32_t headTexNr, int32_t teethTexNr, int32_t bodyVer, int32_t bodyColor, std::string_view body, std::string_view head)
void changeProtection(Protection p, int32_t val)
auto animMoveSpeed(uint64_t dt) const -> Tempest::Vec3
int32_t hitChance(Talent t) const
void aiPush(AiQueue::AiAction &&a)
size_t itemCount(size_t id) const
float rotationRad() const
void moveItem(size_t id, Interactive &to, size_t count=1)
void setRefuseTalk(uint64_t milis)
bool rotateTo(float dx, float dz, float speed, AnimationSolver::TurnType anim, uint64_t dt)
void setPerceptionDisable(PercType t)
MoveAlgo::JumpStatus JumpStatus
void setDirection(const Tempest::Vec3 &pos)
void setSlotItem(MeshObjects::Mesh &&itm, std::string_view slot)
auto transform() const -> Tempest::Matrix4x4
void updateAnimation(uint64_t dt, bool force=false)
void setAnimRotate(int rot)
void clearState(bool noFinalize)
bool isFallingDeep() const
int32_t talentSkill(Talent t) const
BodyState bodyState() const
bool isUsingTorch() const
bool shootBow(Interactive *focOverride=nullptr)
bool setPosition(float x, float y, float z)
bool hasAnim(std::string_view scheme) const
void setPerceptionEnable(PercType t, size_t fn)
void setProcessPolicy(NpcProcessPolicy t)
int32_t talentValue(Talent t) const
int32_t activeSpellLevel() const
bool hasState(BodyState s) const
int32_t attribute(Attribute a) const
auto detectedMob() const -> Interactive *
void onWldItemRemoved(const Item &itm)
void unequipItem(size_t item)
void processDefInvTorch()
void stopEffect(const VisualFx &vfx)
auto dialogChoices(Npc &player, const std::vector< uint32_t > &except, bool includeImp) -> std::vector< GameScript::DlgChoice >
int32_t experienceNext() const
bool testMove(const Tempest::Vec3 &pos)
auto setAnimAngGet(Anim a) -> const Animation::Sequence *
void setDirectionY(float rotation)
void setVisual(std::string_view visual)
auto inventory() const -> const Inventory &
auto interactive() const -> Interactive *
Item * currentMeleeWeapon()
void runEffect(Effect &&e)
void dropItem(size_t id, size_t count=1)
auto formerPortalName() -> std::string_view
bool closeWeapon(bool noAnim)
bool setAnimItem(std::string_view scheme, int state)
void endCastSpell(bool playerCtrl=false)
AnimationSolver::Anim Anim
auto centerPosition() const -> Tempest::Vec3
void setTalentValue(Talent t, int32_t lvl)
std::string_view displayName() const
void startFaceAnim(std::string_view anim, float intensity, uint64_t duration)
void setSword(MeshObjects::Mesh &&sword)
auto position() const -> Tempest::Vec3
bool isRotationAllowed() const
bool hasPerc(PercType perc) const
void buyItem(size_t id, Npc &from, size_t count=1)
void setRunAngle(float angle)
bool isUnconscious() const
void attachToPoint(const WayPoint *p)
bool perceptionProcess(Npc &pl)
int32_t learningPoints() const
bool isAttackAnim() const
void excRoutine(size_t callback)
void changeAttribute(Attribute a, int32_t val, bool allowUnconscious)
void useItem(size_t item)
void setStateItem(MeshObjects::Mesh &&itm, std::string_view slot)
int32_t magicCyrcle() const
int32_t mageCycle() const
void setStateTime(int64_t time)
bool turnTo(float dx, float dz, bool noAnim, uint64_t dt)
void setPhysic(DynamicWorld::NpcItem &&item)
bool drawSpell(int32_t spell)
void load(Serialize &fout, size_t id, std::string_view directory)
float rotationYRad() const
void sellItem(size_t id, Npc &to, size_t count=1)
uint32_t instanceSymbol() const
bool setInteraction(Interactive *id, bool quick=false)
bool isAiQueueEmpty() const
void setAiOutputBarrier(uint64_t dt, bool overlay)
bool isRefuseTalk() const
bool hasSwimAnimations() const
bool hasAmmunition() const
void setAttitude(Attitude att)
void setCurrentItem(size_t item)
void delItem(size_t id, uint32_t amount)
bool tryMove(const Tempest::Vec3 &dp)
auto processPolicy() const -> NpcProcessPolicy
bool isTargetableBySpell(TargetType t) const
void save(Serialize &fout, size_t id, std::string_view directory)
auto cameraMatrix(bool isFirstPerson=false) const -> Tempest::Matrix4x4
void emitSoundEffect(std::string_view sound, float range, bool freeSlot)
void setTempAttitude(Attitude att)
Item * currentRangedWeapon()
bool isInRoutine(ScriptFn stateFn) const
void setScale(float x, float y, float z)
void addOverlay(std::string_view sk, uint64_t time)
void setDetectedMob(Interactive *id)
uint64_t percNextTime() const
Item * addItem(size_t id, size_t amount)
uint64_t stateTime() const
Item * getItem(size_t id)
bool isEnemy(const Npc &other) const
void setTrueGuild(int32_t g)
bool canSeeItem(const Item &it, bool freeLos) const
BodyState bodyStateMasked() const
void delOverlay(std::string_view sk)
void emitSoundGround(std::string_view sound, float range, bool freeSlot)
void clearSlotItem(std::string_view slot)
bool canSwitchWeapon() const
void setMagicWeapon(Effect &&spell)
void setAmmoItem(MeshObjects::Mesh &&itm, std::string_view slot)
void startEffect(Npc &to, const VisualFx &vfx)
bool canRayHitPoint(const Tempest::Vec3 pos, bool freeLos=true, float extRange=0.f) const
bool hasStateFlag(BodyState flg) const
void setPerceptionTime(uint64_t time)
auto portalName() -> std::string_view
int32_t trueGuild() const
bool isFinishingMove() const
auto beginCastSpell() -> BeginCastResult
BodyState bodyState() const
auto rootNode() const -> const Tempest::Matrix4x4
bool isJumpBack(uint64_t tickCount) const
static uint8_t calcAniComb(const Tempest::Vec3 &dpos, float rotation)
bool isAttackAnim() const
bool isInAnim(std::string_view sq) const
bool hasState(BodyState s) const
uint64_t animationTotalTime() const
auto rootBone() const -> const Tempest::Matrix4x4
auto bone(size_t id) const -> const Tempest::Matrix4x4 &
Tempest::Vec3 animMoveSpeed(uint64_t tickCount, uint64_t dt) const
bool isPrehit(uint64_t now) const
static uint8_t calcAniCombVert(const Tempest::Vec3 &dpos)
uint64_t atkTotalTime() const
bool hasStateFlag(BodyState f) const
size_t findNode(std::string_view b) const
static const Skeleton * loadSkeleton(std::string_view name)
void readNpc(zenkit::DaedalusVm &vm, std::shared_ptr< zenkit::INpc > &npc)
void write(const Arg &... a)
bool setEntry(const Args &... args)
std::string_view worldName() const
size_t findNode(std::string_view name, size_t def=size_t(-1)) const
std::string_view name() const
void setPosition(const Tempest::Vec3 &pos)
void save(Serialize &fout)
const WayPoint * first() const
const WayPoint * last() const
void load(Serialize &fin)
uint32_t useCounter() const
Tempest::Vec3 position() const
Tempest::Vec3 direction() const
bool canSeeSource(const Tempest::Vec3 &npc) const
auto version() const -> const VersionInfo &
void sendPassivePerc(Npc &self, Npc &other, int32_t perc)
CsCamera * currentCs() const
DynamicWorld * physic() const
Interactive * availableMob(const Npc &pl, std::string_view name)
WayPath wayTo(const Npc &pos, const WayPoint &end) const
uint64_t tickCount() const
const WayPoint * findNextFreePoint(const Npc &pos, std::string_view name) const
const WayPoint * findPoint(std::string_view name, bool inexact=true) const
void detectNpcNear(std::function< void(Npc &)> f)
Item * addItemDyn(size_t itemInstance, const Tempest::Matrix4x4 &pos, size_t owner)
const WayPoint & deadPoint() const
auto takeItem(Item &it) -> std::unique_ptr< Item >
void detectNpc(const Tempest::Vec3 &p, const float r, const std::function< void(Npc &)> &f)
MeshObjects::Mesh addView(std::string_view visual) const
const WayPoint * findWayPoint(std::string_view name) const
Sound addWeaponHitEffect(Npc &src, const Bullet *srcArrow, Npc &reciver)
const WayPoint * findNextPoint(const WayPoint &pos) const
GameScript & script() const
Bullet & shootSpell(const Item &itm, const Npc &npc, const Npc *target)
Bullet & shootBullet(const Item &itmId, const Npc &npc, const Npc *target, const Interactive *inter)
void addMilis(uint64_t t)
static const gtime endOfTime()
static CommandLine * instance
@ GIL_SHADOWBEAST_SKELETON
@ GIL_SUMMONED_GOBBO_SKELETON
@ SPL_STATUS_CANINVEST_NO_MANADEC
const char * MaterialGroupNames[]
@ PERC_ASSESSREMOVEWEAPON
@ PERC_ASSESSOTHERSDAMAGE
std::string addExt(const std::string &s, const char *ext)
static std::string_view humansTorchOverlay
zenkit::MdsFightMode weaponCh
std::vector< EvMorph > morph
std::vector< EvTimed > timed
bool testMove(const Tempest::Vec3 &to, CollisionTest &out)
void setPosition(const Tempest::Vec3 &pos)
void setUserPointer(void *p)
const Tempest::Vec3 & position() const
auto tryMove(const Tempest::Vec3 &to, CollisionTest &out) -> DynamicWorld::MoveCode
AnimationSolver::Anim anim