diff --git a/game/game/constants.h b/game/game/constants.h index 27739151f..5be8661a7 100644 --- a/game/game/constants.h +++ b/game/game/constants.h @@ -289,7 +289,7 @@ enum ItmFlags : uint32_t { enum Action:uint32_t { AI_None =0, - AI_LookAt, + AI_LookAtNpc, AI_StopLookAt, AI_RemoveWeapon, AI_TurnToNpc, @@ -336,6 +336,7 @@ enum Action:uint32_t { AI_PointAt, AI_StopPointAt, AI_PrintScreen, + AI_LookAt }; diff --git a/game/game/gamescript.cpp b/game/game/gamescript.cpp index 94bef2d66..a07f97266 100644 --- a/game/game/gamescript.cpp +++ b/game/game/gamescript.cpp @@ -209,6 +209,7 @@ void GameScript::initCommon() { bindExternal("ai_standupquick", &GameScript::ai_standupquick); bindExternal("ai_continueroutine", &GameScript::ai_continueroutine); bindExternal("ai_stoplookat", &GameScript::ai_stoplookat); + bindExternal("ai_lookat", &GameScript::ai_lookat); bindExternal("ai_lookatnpc", &GameScript::ai_lookatnpc); bindExternal("ai_removeweapon", &GameScript::ai_removeweapon); bindExternal("ai_turntonpc", &GameScript::ai_turntonpc); @@ -2545,11 +2546,18 @@ void GameScript::ai_stoplookat(std::shared_ptr selfRef) { self->aiPush(AiQueue::aiStopLookAt()); } +void GameScript::ai_lookat(std::shared_ptr selfRef, std::string_view waypoint) { + auto self = findNpc(selfRef); + auto to = world().findPoint(waypoint); + if(self!=nullptr) + self->aiPush(AiQueue::aiLookAt(to)); + } + void GameScript::ai_lookatnpc(std::shared_ptr selfRef, std::shared_ptr npcRef) { auto npc = findNpc(npcRef); auto self = findNpc(selfRef); if(self!=nullptr) - self->aiPush(AiQueue::aiLookAt(npc)); + self->aiPush(AiQueue::aiLookAtNpc(npc)); } void GameScript::ai_removeweapon(std::shared_ptr npcRef) { diff --git a/game/game/gamescript.h b/game/game/gamescript.h index 20f5b0747..98bf0a8b7 100644 --- a/game/game/gamescript.h +++ b/game/game/gamescript.h @@ -341,6 +341,7 @@ class GameScript final { void ai_standupquick (std::shared_ptr selfRef); void ai_continueroutine (std::shared_ptr selfRef); void ai_stoplookat (std::shared_ptr selfRef); + void ai_lookat (std::shared_ptr selfRef, std::string_view waypoint); void ai_lookatnpc (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_removeweapon (std::shared_ptr npcRef); void ai_turntonpc (std::shared_ptr selfRef, std::shared_ptr npcRef); diff --git a/game/game/serialize.h b/game/game/serialize.h index 2c892680a..e1a9a7d15 100644 --- a/game/game/serialize.h +++ b/game/game/serialize.h @@ -33,7 +33,7 @@ class SaveGameHeader; class Serialize { public: enum Version : uint16_t { - Current = 41 + Current = 42 }; Serialize(Tempest::ODevice& fout); Serialize(Tempest::IDevice& fin); diff --git a/game/world/aiqueue.cpp b/game/world/aiqueue.cpp index afa495413..15f5ec56d 100644 --- a/game/world/aiqueue.cpp +++ b/game/world/aiqueue.cpp @@ -36,7 +36,7 @@ void AiQueue::clear() { void AiQueue::pushBack(AiAction&& a) { if(aiActions.size()>0) { - if(aiActions.back().act==AI_LookAt && a.act==AI_LookAt) { + if(aiActions.back().act==AI_LookAtNpc && a.act==AI_LookAtNpc) { aiActions.back() = a; return; } @@ -72,9 +72,16 @@ void AiQueue::onWldItemRemoved(const Item& itm) { i.item = nullptr; } -AiQueue::AiAction AiQueue::aiLookAt(Npc* other) { +AiQueue::AiAction AiQueue::aiLookAt(const WayPoint* to) { AiAction a; a.act = AI_LookAt; + a.point = to; + return a; + } + +AiQueue::AiAction AiQueue::aiLookAtNpc(Npc* other) { + AiAction a; + a.act = AI_LookAtNpc; a.target = other; return a; } diff --git a/game/world/aiqueue.h b/game/world/aiqueue.h index eeaa995d1..2c2b91cdd 100644 --- a/game/world/aiqueue.h +++ b/game/world/aiqueue.h @@ -42,7 +42,8 @@ class AiQueue { void onWldItemRemoved(const Item& itm); - static AiAction aiLookAt(Npc* other); + static AiAction aiLookAt(const WayPoint* to); + static AiAction aiLookAtNpc(Npc* other); static AiAction aiStopLookAt(); static AiAction aiRemoveWeapon(); static AiAction aiTurnToNpc(Npc *other); diff --git a/game/world/objects/npc.cpp b/game/world/objects/npc.cpp index 70f2e6d78..e7fb8e2db 100644 --- a/game/world/objects/npc.cpp +++ b/game/world/objects/npc.cpp @@ -201,7 +201,7 @@ void Npc::save(Serialize &fout, size_t id) { saveAiState(fout); fout.write(currentInteract,currentOther,currentVictum); - fout.write(currentLookAt,currentTarget,nearestEnemy); + fout.write(currentLookAt,currentLookAtNpc,currentTarget,nearestEnemy); go2.save(fout); fout.write(currentFp,currentFpLock); @@ -257,7 +257,9 @@ void Npc::load(Serialize &fin, size_t id) { loadAiState(fin); fin.read(currentInteract,currentOther,currentVictum); - fin.read(currentLookAt,currentTarget,nearestEnemy); + if(fin.version()>=42) + fin.read(currentLookAt); + fin.read(currentLookAtNpc,currentTarget,nearestEnemy); go2.load(fin); fin.read(currentFp,currentFpLock); @@ -677,7 +679,7 @@ Vec3 Npc::centerPosition() const { } Npc *Npc::lookAtTarget() const { - return currentLookAt; + return currentLookAtNpc; } std::string_view Npc::portalName() { @@ -1224,11 +1226,18 @@ bool Npc::implPointAt(const Tempest::Vec3& to) { return (setAnimAngGet(Npc::Anim::PointAt,comb)!=nullptr); } -bool Npc::implLookAt(uint64_t dt) { +bool Npc::implLookAtWp(uint64_t dt) { if(currentLookAt==nullptr) return false; + auto dvec = currentLookAt->position(); + return implLookAt(dvec.x,dvec.y,dvec.z,dt); + } + +bool Npc::implLookAtNpc(uint64_t dt) { + if(currentLookAtNpc==nullptr) + return false; auto selfHead = visual.mapHeadBone(); - auto otherHead = currentLookAt->visual.mapHeadBone(); + auto otherHead = currentLookAtNpc->visual.mapHeadBone(); auto dvec = otherHead - selfHead; return implLookAt(dvec.x,dvec.y,dvec.z,dt); } @@ -1988,7 +1997,8 @@ void Npc::tick(uint64_t dt) { } if(!isDown()) { - implLookAt(dt); + implLookAtNpc(dt); + implLookAtWp(dt); if(implAtack(dt)) return; @@ -2010,8 +2020,14 @@ void Npc::nextAiAction(AiQueue& queue, uint64_t dt) { auto act = queue.pop(); switch(act.act) { case AI_None: break; + case AI_LookAtNpc:{ + currentLookAt=nullptr; + currentLookAtNpc=act.target; + break; + } case AI_LookAt:{ - currentLookAt=act.target; + currentLookAtNpc=nullptr; + currentLookAt=act.point; break; } case AI_TurnToNpc: { @@ -2073,6 +2089,7 @@ void Npc::nextAiAction(AiQueue& queue, uint64_t dt) { break; } case AI_StopLookAt: + currentLookAtNpc=nullptr; currentLookAt=nullptr; visual.setHeadRotation(0,0); break; diff --git a/game/world/objects/npc.h b/game/world/objects/npc.h index b9d6dec10..076998850 100644 --- a/game/world/objects/npc.h +++ b/game/world/objects/npc.h @@ -455,7 +455,8 @@ class Npc final { gtime endTime(const Routine& r) const; bool implPointAt(const Tempest::Vec3& to); - bool implLookAt (uint64_t dt); + bool implLookAtWp(uint64_t dt); + bool implLookAtNpc(uint64_t dt); bool implLookAt (float dx, float dy, float dz, uint64_t dt); bool implTurnTo (const Npc& oth, uint64_t dt); bool implTurnTo (const Npc& oth, bool noAnim, uint64_t dt); @@ -571,7 +572,8 @@ class Npc final { Npc* currentOther =nullptr; Npc* currentVictum =nullptr; - Npc* currentLookAt =nullptr; + const WayPoint* currentLookAt=nullptr; + Npc* currentLookAtNpc=nullptr; Npc* currentTarget =nullptr; Npc* nearestEnemy =nullptr; AiOuputPipe* outputPipe =nullptr;