diff --git a/game/game/playercontrol.cpp b/game/game/playercontrol.cpp index b85cc3d72..be640a5bb 100644 --- a/game/game/playercontrol.cpp +++ b/game/game/playercontrol.cpp @@ -10,6 +10,8 @@ #include "ui/inventorymenu.h" #include "gothic.h" +using MobsiState=Interactive::MobsiState; + PlayerControl::PlayerControl(DialogMenu& dlg, InventoryMenu &inv) :dlg(dlg),inv(inv) { } @@ -176,19 +178,6 @@ void PlayerControl::onKeyReleased(KeyCodec::Action a) { } else { std::memset(actrl,0,sizeof(actrl)); } - - if(pl!=nullptr && pl->interactive()!=nullptr) { - auto inter = pl->interactive(); - if (inter->isLadder()) { - bool g1c=Gothic::inst().settingsGetI("GAME","USEGOTHIC1CONTROLS")!=0 ; - if (a==KeyCodec::ActionGeneric && !g1c) { - inter->nextState(*pl,4); - return; - } - if ((a==KeyCodec::Forward || a==KeyCodec::Back) && (g1c || (!g1c && isPressed(KeyCodec::ActionGeneric)))) - inter->nextState(*pl,0); - } - } } bool PlayerControl::isPressed(KeyCodec::Action a) const { @@ -802,7 +791,9 @@ void PlayerControl::implMove(uint64_t dt) { void PlayerControl::implMoveMobsi(Npc& pl, uint64_t /*dt*/) { // animation handled in MOBSI auto inter = pl.interactive(); - if(ctrl[KeyCodec::Back]) { + bool g1c=Gothic::inst().settingsGetI("GAME","USEGOTHIC1CONTROLS")!=0; + + if(ctrl[KeyCodec::Back] && !inter->isLadder()) { pl.setInteraction(nullptr); return; } @@ -817,6 +808,30 @@ void PlayerControl::implMoveMobsi(Npc& pl, uint64_t /*dt*/) { if(inter->canQuitAtState(pl,stateId-ladder)) pl.setInteraction(nullptr,ladder); } + + if (inter->isLadder()) { + if(!g1c && !ctrl[KeyCodec::ActionGeneric]) { + if (inter->stateId()<1) { + inter->nextState(pl,MobsiState::Prev); + return; + } + if (inter->stateId()>inter->stateCount()-2) { + inter->nextState(pl,MobsiState::Next); + return; + } + inter->nextState(pl,MobsiState::Quit); + return; + } + if(ctrl[KeyCodec::Forward]) { + inter->nextState(pl,MobsiState::Next); + return; + } + if(ctrl[KeyCodec::Back]) { + inter->nextState(pl,MobsiState::Prev); + if (inter->stateId()==-1) + ctrl[KeyCodec::ActionGeneric] = false; + } + } } void PlayerControl::processPickLock(Npc& pl, Interactive& inter, KeyCodec::Action k) { @@ -870,31 +885,25 @@ void PlayerControl::processPickLock(Npc& pl, Interactive& inter, KeyCodec::Actio } void PlayerControl::processLadder(Npc& pl, Interactive& inter, KeyCodec::Action key) { - bool g1c=Gothic::inst().settingsGetI("GAME","USEGOTHIC1CONTROLS")!=0 ; - + bool g1c=Gothic::inst().settingsGetI("GAME","USEGOTHIC1CONTROLS")!=0; if(key==KeyCodec::ActionGeneric) { ctrl[key] = true; if (g1c) { - pl.stopAnim(""); - pl.setInteraction(nullptr); + inter.nextState(pl,MobsiState::Quit); ctrl[key] = false; } - else { - inter.nextState(pl,0); - } return; } - if (!g1c && !isPressed(KeyCodec::ActionGeneric)) + if (!g1c && !ctrl[KeyCodec::ActionGeneric]) return; - if(key==KeyCodec::Forward ) { - inter.nextState(pl,1); + if(key==KeyCodec::Forward) { + ctrl[key] = true; + inter.nextState(pl,MobsiState::Next); return; } if(key==KeyCodec::Back) { - if (g1c) - inter.nextState(pl,2); - else - inter.nextState(pl,3); + ctrl[key] = true; + inter.nextState(pl,MobsiState::Prev); } } diff --git a/game/graphics/mdlvisual.cpp b/game/graphics/mdlvisual.cpp index 41827a7e0..a8e22eecf 100644 --- a/game/graphics/mdlvisual.cpp +++ b/game/graphics/mdlvisual.cpp @@ -5,6 +5,7 @@ #include "graphics/mesh/skeleton.h" #include "game/serialize.h" #include "world/objects/npc.h" +#include "world/objects/interactive.h" #include "world/objects/item.h" #include "world/world.h" #include "utils/fileext.h" @@ -591,7 +592,7 @@ const Animation::Sequence* MdlVisual::startAnimAndGet(Npc& npc, AnimationSolver: auto inter = npc.interactive(); const Animation::Sequence *sq = solver.solveAnim(inter,a,*skInst); if(sq!=nullptr){ - auto bs=sq->name.find("T_LADDER")==0 ? BS_CLIMB : BS_MOBINTERACT; + auto bs=inter->isLadder() ? BS_CLIMB : BS_MOBINTERACT; if(skInst->startAnim(solver,sq,comb,bs,Pose::NoHint,npc.world().tickCount())) return sq; } diff --git a/game/world/objects/interactive.cpp b/game/world/objects/interactive.cpp index 5f626418e..0147f6e77 100644 --- a/game/world/objects/interactive.cpp +++ b/game/world/objects/interactive.cpp @@ -234,7 +234,6 @@ void Interactive::implTick(Pos& p, uint64_t /*dt*/) { mat.project(x0,y0,z0); mat.project(x1,y1,z1); npc.setDirectionY(y1-y0); - wait = true; } auto sq = npc.setAnimAngGet(Npc::Anim::InteractFromStand); uint64_t t = sq==nullptr ? 0 : uint64_t(sq->totalTime()); @@ -260,10 +259,15 @@ void Interactive::implTick(Pos& p, uint64_t /*dt*/) { } } - if(isLadder() && wait && p.started) + if(isLadder()) { + if (state==-1) { + loopState=true; + reverseState=false; + } return; + } - if((!attach && state==0) || (isLadder() && attach && state==stateNum-1)) { + if(!attach && state==0) { implQuitInteract(p); return; } @@ -306,33 +310,42 @@ void Interactive::implTick(Pos& p, uint64_t /*dt*/) { loopState = (prev==state); } -void Interactive::nextState(Npc& npc, const uint8_t st) { - wait = !st; - switch(st){ - case 1 : - reverseState=false; - return; - case 2 : - reverseState=true; - return; - case 3 : - if (state==0) - wait=true; - else - reverseState=true; +void Interactive::nextState(Npc& npc, MobsiState st) { + if (st==MobsiState::Quit) { + npc.stopAnim(""); + npc.setInteraction(nullptr); + return; + } + + if(world.tickCount()stateNum-2) { - reverseState=false; - return; - } - npc.stopAnim(""); - npc.setInteraction(nullptr); + waitAnim=world.tickCount()+uint64_t(sq->totalTime()); + npc.setDirectionY(0); + if (state==0) { + setState(-1); + } else { + setState(stateNum); + } + return; } + if (!setAnim(&npc,Anim::In)) + return; + if (reverseState) { + setState(std::max(0,state-1)); + } else { + setState(std::min(state+1,stateNum)); + } + loopState = (prev==state); } void Interactive::implQuitInteract(Interactive::Pos &p) { @@ -340,15 +353,9 @@ void Interactive::implQuitInteract(Interactive::Pos &p) { return; Npc& npc = *p.user; const Animation::Sequence* sq = nullptr; - if(state==0 || (isLadder() && state==stateNum-1)) { + if(state==0) { // S0 -> STAND sq = npc.setAnimAngGet(Npc::Anim::InteractToStand); - if (isLadder() && state==stateNum-1) { - waitAnim=world.tickCount()+uint64_t(sq->totalTime()); - setState(stateNum); - npc.setDirectionY(0); - return; - } } if(sq==nullptr && !(npc.isDown() || npc.setAnim(Npc::Anim::Idle))) return; @@ -923,7 +930,7 @@ const Animation::Sequence* Interactive::animNpc(const AnimationSolver &solver, A if(t==Anim::FromStand) { st[0] = -1; st[1] = state<1 ? 0 : stateNum - 1; - } + } else if(t==Anim::ToStand) { st[0] = state<1 ? 0 : stateNum - 1; st[1] = -1; diff --git a/game/world/objects/interactive.h b/game/world/objects/interactive.h index 495df4ea5..5db05d03c 100644 --- a/game/world/objects/interactive.h +++ b/game/world/objects/interactive.h @@ -24,6 +24,12 @@ class Interactive : public Vob { FromStand = 11, }; + enum MobsiState : uint8_t { + Next = 1, + Prev = 2, + Quit = 3, + }; + Interactive(Vob* parent, World& world, ZenLoad::zCVobData& vob, Flags flags); void load(Serialize& fin) override; @@ -76,7 +82,7 @@ class Interactive : public Vob { auto animNpc(const AnimationSolver &solver, Anim t) -> const Animation::Sequence*; void marchInteractives(DbgPainter& p) const; - void nextState(Npc& npc, const uint8_t st); + void nextState(Npc& npc, MobsiState st); protected: Tempest::Matrix4x4 nodeTranform(std::string_view nodeName) const; @@ -151,7 +157,6 @@ class Interactive : public Vob { int stepsCount = 0; int32_t state = -1; - bool wait = false; bool reverseState = false; bool loopState = false; bool isLockCracked = false;