From 75226a3f5c5a99b69b51988e6c747dc475fe4598 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 9 Jan 2025 18:00:05 +0100 Subject: [PATCH 1/2] Implement function AI_WhirlAround It is supposed to be a faster version of AI_TurnToNpc and seemlingly used in Gothic 1 in a number of places. The script does not contain specific values for whirl-speed, so just use double of the NPc's turn-speed. This looked quite nice with the guards at the New Camp when drawing a weapon behind them. --- game/game/constants.h | 3 ++- game/game/gamescript.cpp | 8 ++++++++ game/game/gamescript.h | 1 + game/world/aiqueue.cpp | 7 +++++++ game/world/aiqueue.h | 1 + game/world/objects/npc.cpp | 31 +++++++++++++++++++++++++++++++ game/world/objects/npc.h | 1 + 7 files changed, 51 insertions(+), 1 deletion(-) diff --git a/game/game/constants.h b/game/game/constants.h index 5f785662a..cc95707bd 100644 --- a/game/game/constants.h +++ b/game/game/constants.h @@ -384,7 +384,8 @@ enum Action:uint32_t { AI_PointAt, AI_StopPointAt, AI_PrintScreen, - AI_LookAt + AI_LookAt, + AI_WhirlToNpc, }; diff --git a/game/game/gamescript.cpp b/game/game/gamescript.cpp index 6927420fb..e017287ba 100644 --- a/game/game/gamescript.cpp +++ b/game/game/gamescript.cpp @@ -239,6 +239,7 @@ void GameScript::initCommon() { bindExternal("ai_removeweapon", &GameScript::ai_removeweapon); bindExternal("ai_unreadyspell", &GameScript::ai_unreadyspell); bindExternal("ai_turntonpc", &GameScript::ai_turntonpc); + bindExternal("ai_whirlaround", &GameScript::ai_whirlaround); bindExternal("ai_outputsvm", &GameScript::ai_outputsvm); bindExternal("ai_outputsvm_overlay", &GameScript::ai_outputsvm_overlay); bindExternal("ai_startstate", &GameScript::ai_startstate); @@ -2793,6 +2794,13 @@ void GameScript::ai_turntonpc(std::shared_ptr selfRef, std::shared self->aiPush(AiQueue::aiTurnToNpc(npc)); } +void GameScript::ai_whirlaround(std::shared_ptr selfRef, std::shared_ptr npcRef) { + auto npc = findNpc(npcRef); + auto self = findNpc(selfRef); + if(self!=nullptr) + self->aiPush(AiQueue::aiWhirlToNpc(npc)); + } + void GameScript::ai_outputsvm(std::shared_ptr selfRef, std::shared_ptr targetRef, std::string_view name) { auto target = findNpc(targetRef); auto self = findNpc(selfRef); diff --git a/game/game/gamescript.h b/game/game/gamescript.h index 74f23814a..4cab0683f 100644 --- a/game/game/gamescript.h +++ b/game/game/gamescript.h @@ -370,6 +370,7 @@ class GameScript final { void ai_removeweapon (std::shared_ptr npcRef); void ai_unreadyspell (std::shared_ptr npcRef); void ai_turntonpc (std::shared_ptr selfRef, std::shared_ptr npcRef); + void ai_whirlaround (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_outputsvm (std::shared_ptr selfRef, std::shared_ptr targetRef, std::string_view name); void ai_outputsvm_overlay(std::shared_ptr selfRef, std::shared_ptr targetRef, std::string_view name); void ai_startstate (std::shared_ptr selfRef, int func, int state, std::string_view wp); diff --git a/game/world/aiqueue.cpp b/game/world/aiqueue.cpp index 6a9d10dd9..c865f29ef 100644 --- a/game/world/aiqueue.cpp +++ b/game/world/aiqueue.cpp @@ -105,6 +105,13 @@ AiQueue::AiAction AiQueue::aiTurnToNpc(Npc *other) { return a; } +AiQueue::AiAction AiQueue::aiWhirlToNpc(Npc *other) { + AiAction a; + a.act = AI_WhirlToNpc; + a.target = other; + return a; + } + AiQueue::AiAction AiQueue::aiGoToNpc(Npc *other) { AiAction a; a.act = AI_GoToNpc; diff --git a/game/world/aiqueue.h b/game/world/aiqueue.h index 926a711de..acb3add94 100644 --- a/game/world/aiqueue.h +++ b/game/world/aiqueue.h @@ -47,6 +47,7 @@ class AiQueue { static AiAction aiStopLookAt(); static AiAction aiRemoveWeapon(); static AiAction aiTurnToNpc(Npc *other); + static AiAction aiWhirlToNpc(Npc *other); static AiAction aiGoToNpc (Npc *other); static AiAction aiGoToNextFp(std::string_view fp); static AiAction aiStartState(ScriptFn stateFn, int behavior, Npc *other, Npc* victum, std::string_view wp); diff --git a/game/world/objects/npc.cpp b/game/world/objects/npc.cpp index 51df6922e..f13860eaf 100644 --- a/game/world/objects/npc.cpp +++ b/game/world/objects/npc.cpp @@ -1352,6 +1352,16 @@ bool Npc::implTurnTo(float dx, float dz, bool noAnim, uint64_t dt) { return rotateTo(dx,dz,step,noAnim,dt); } +bool Npc::implWhirlTo(const Npc &oth, uint64_t dt) { + if(&oth==this) + return true; + auto dx = oth.x-x; + auto dz = oth.z-z; + auto gl = guild(); + float step = float(owner.script().guildVal().turn_speed[gl]) * 2; + return rotateTo(dx,dz,step,false,dt); + } + bool Npc::implGoTo(uint64_t dt) { float dist = 0; if(go2.npc) { @@ -2212,6 +2222,27 @@ void Npc::nextAiAction(AiQueue& queue, uint64_t dt) { // currentLookAtNpc = nullptr; break; } + case AI_WhirlToNpc: { + const auto st = bodyStateMasked(); + if(interactive()==nullptr && (st==BS_WALK || st==BS_SNEAK)) { + stopWalkAnimation(); + queue.pushFront(std::move(act)); + break; + } + if(interactive()==nullptr) { + stopWalkAnimation(); + visual.stopDlgAnim(*this); + } + if(act.target!=nullptr && implWhirlTo(*act.target,dt)) { + queue.pushFront(std::move(act)); + break; + } + // Not looking quite correct in dialogs, when npc turns around + // Example: Esteban dialog + // currentLookAt = nullptr; + // currentLookAtNpc = nullptr; + break; + } case AI_GoToNpc: if(!setInteraction(nullptr)) { queue.pushFront(std::move(act)); diff --git a/game/world/objects/npc.h b/game/world/objects/npc.h index 6ed1968b2..f39a24a65 100644 --- a/game/world/objects/npc.h +++ b/game/world/objects/npc.h @@ -474,6 +474,7 @@ class Npc final { bool implTurnTo (const Npc& oth, uint64_t dt); bool implTurnTo (const Npc& oth, bool noAnim, uint64_t dt); bool implTurnTo (float dx, float dz, bool noAnim, uint64_t dt); + bool implWhirlTo(const Npc& oth, uint64_t dt); bool implGoTo (uint64_t dt); bool implGoTo (uint64_t dt, float destDist); bool implAttack (uint64_t dt); From ac223f41861725a9073e15738e88f29db8645470 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Tue, 14 Jan 2025 21:18:36 +0100 Subject: [PATCH 2/2] Implmeent function ai_turnaway Defined as 'npc' turns away from 'other', just like turnTo but +180 degrees. One user is for example the crazy Baal Netbek in the swamp where the player character ends the dialog with turning away saying "This guy won't be of help" --- game/game/constants.h | 1 + game/game/gamescript.cpp | 8 ++++++ game/game/gamescript.h | 1 + game/world/aiqueue.cpp | 7 +++++ game/world/aiqueue.h | 1 + game/world/objects/npc.cpp | 52 +++++++++++++++++++++++++++++++++++--- game/world/objects/npc.h | 2 ++ 7 files changed, 68 insertions(+), 4 deletions(-) diff --git a/game/game/constants.h b/game/game/constants.h index cc95707bd..7965b0d58 100644 --- a/game/game/constants.h +++ b/game/game/constants.h @@ -386,6 +386,7 @@ enum Action:uint32_t { AI_PrintScreen, AI_LookAt, AI_WhirlToNpc, + AI_TurnAway, }; diff --git a/game/game/gamescript.cpp b/game/game/gamescript.cpp index e017287ba..02d148662 100644 --- a/game/game/gamescript.cpp +++ b/game/game/gamescript.cpp @@ -238,6 +238,7 @@ void GameScript::initCommon() { bindExternal("ai_lookatnpc", &GameScript::ai_lookatnpc); bindExternal("ai_removeweapon", &GameScript::ai_removeweapon); bindExternal("ai_unreadyspell", &GameScript::ai_unreadyspell); + bindExternal("ai_turnaway", &GameScript::ai_turnaway); bindExternal("ai_turntonpc", &GameScript::ai_turntonpc); bindExternal("ai_whirlaround", &GameScript::ai_whirlaround); bindExternal("ai_outputsvm", &GameScript::ai_outputsvm); @@ -2787,6 +2788,13 @@ void GameScript::ai_unreadyspell(std::shared_ptr npcRef) { npc->aiPush(AiQueue::aiRemoveWeapon()); } +void GameScript::ai_turnaway(std::shared_ptr selfRef, std::shared_ptr npcRef) { + auto npc = findNpc(npcRef); + auto self = findNpc(selfRef); + if(self!=nullptr) + self->aiPush(AiQueue::aiTurnAway(npc)); + } + void GameScript::ai_turntonpc(std::shared_ptr selfRef, std::shared_ptr npcRef) { auto npc = findNpc(npcRef); auto self = findNpc(selfRef); diff --git a/game/game/gamescript.h b/game/game/gamescript.h index 4cab0683f..26826144b 100644 --- a/game/game/gamescript.h +++ b/game/game/gamescript.h @@ -369,6 +369,7 @@ class GameScript final { void ai_lookatnpc (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_removeweapon (std::shared_ptr npcRef); void ai_unreadyspell (std::shared_ptr npcRef); + void ai_turnaway (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_turntonpc (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_whirlaround (std::shared_ptr selfRef, std::shared_ptr npcRef); void ai_outputsvm (std::shared_ptr selfRef, std::shared_ptr targetRef, std::string_view name); diff --git a/game/world/aiqueue.cpp b/game/world/aiqueue.cpp index c865f29ef..007df74b9 100644 --- a/game/world/aiqueue.cpp +++ b/game/world/aiqueue.cpp @@ -98,6 +98,13 @@ AiQueue::AiAction AiQueue::aiRemoveWeapon() { return a; } +AiQueue::AiAction AiQueue::aiTurnAway(Npc *other) { + AiAction a; + a.act = AI_TurnAway; + a.target = other; + return a; + } + AiQueue::AiAction AiQueue::aiTurnToNpc(Npc *other) { AiAction a; a.act = AI_TurnToNpc; diff --git a/game/world/aiqueue.h b/game/world/aiqueue.h index acb3add94..e8ba195d7 100644 --- a/game/world/aiqueue.h +++ b/game/world/aiqueue.h @@ -46,6 +46,7 @@ class AiQueue { static AiAction aiLookAtNpc(Npc* other); static AiAction aiStopLookAt(); static AiAction aiRemoveWeapon(); + static AiAction aiTurnAway (Npc *other); static AiAction aiTurnToNpc(Npc *other); static AiAction aiWhirlToNpc(Npc *other); static AiAction aiGoToNpc (Npc *other); diff --git a/game/world/objects/npc.cpp b/game/world/objects/npc.cpp index f13860eaf..3e618e666 100644 --- a/game/world/objects/npc.cpp +++ b/game/world/objects/npc.cpp @@ -1330,6 +1330,21 @@ bool Npc::implLookAt(float dx, float dy, float dz, uint64_t dt) { return false; } +bool Npc::implTurnAway(const Npc &oth, uint64_t dt) { + if(&oth==this) + return true; + + auto dx = oth.x-x; + auto dz = oth.z-z; + float a = angleDir(dx, dz) + 180; + if(a > 360) + a-= 360; + + auto gl = guild(); + float step = float(owner.script().guildVal().turn_speed[gl]); + return rotateTo(a,step,false,dt); + } + bool Npc::implTurnTo(const Npc &oth, uint64_t dt) { if(&oth==this) return true; @@ -2201,6 +2216,27 @@ void Npc::nextAiAction(AiQueue& queue, uint64_t dt) { currentLookAt=act.point; break; } + case AI_TurnAway: { + const auto st = bodyStateMasked(); + if(interactive()==nullptr && (st==BS_WALK || st==BS_SNEAK)) { + stopWalkAnimation(); + queue.pushFront(std::move(act)); + break; + } + if(interactive()==nullptr) { + stopWalkAnimation(); + visual.stopDlgAnim(*this); + } + if(act.target!=nullptr && implTurnAway(*act.target,dt)) { + queue.pushFront(std::move(act)); + break; + } + // Not looking quite correct in dialogs, when npc turns around + // Example: Esteban dialog + // currentLookAt = nullptr; + // currentLookAtNpc = nullptr; + break; + } case AI_TurnToNpc: { const auto st = bodyStateMasked(); if(interactive()==nullptr && (st==BS_WALK || st==BS_SNEAK)) { @@ -3251,18 +3287,26 @@ bool Npc::turnTo(float dx, float dz, bool noAnim, uint64_t dt) { } bool Npc::rotateTo(float dx, float dz, float step, bool noAnim, uint64_t dt) { - //step *= (float(dt)/1000.f)*60.f/100.f; - step *= (float(dt)/1000.f); - if(dx==0.f && dz==0.f) { setAnimRotate(0); return false; } + return rotateTo(angleDir(dx, dz), step, noAnim, dt); + } + +bool Npc::rotateTo(float a, float step, bool noAnim, uint64_t dt) { + if(a==0.f) { + setAnimRotate(0); + return false; + } + if(!isRotationAllowed()) return false; - float a = angleDir(dx,dz); + //step *= (float(dt)/1000.f)*60.f/100.f; + step *= (float(dt)/1000.f); + float da = a-angle; if(noAnim || std::cos(double(da)*M_PI/180.0)>0) { diff --git a/game/world/objects/npc.h b/game/world/objects/npc.h index f39a24a65..dd0c5ca71 100644 --- a/game/world/objects/npc.h +++ b/game/world/objects/npc.h @@ -357,6 +357,7 @@ class Npc final { bool turnTo (float dx, float dz, bool noAnim, uint64_t dt); bool rotateTo(float dx, float dz, float speed, bool anim, uint64_t dt); + bool rotateTo(float a, float step, bool noAnim, uint64_t dt); bool isRotationAllowed() const; auto playAnimByName(std::string_view name, BodyState bs) -> const Animation::Sequence*; @@ -471,6 +472,7 @@ class Npc final { bool implLookAtWp(uint64_t dt); bool implLookAtNpc(uint64_t dt); bool implLookAt (float dx, float dy, float dz, uint64_t dt); + bool implTurnAway(const Npc& oth, uint64_t dt); bool implTurnTo (const Npc& oth, uint64_t dt); bool implTurnTo (const Npc& oth, bool noAnim, uint64_t dt); bool implTurnTo (float dx, float dz, bool noAnim, uint64_t dt);