23void PlayerControl::setupSettings() {
33 auto pl = w ? w->
player() :
nullptr;
34 if(pl==
nullptr || pl->isFinishingMove())
39 if(!(melle && ctrl[Action::ActionGeneric])) {
41 pl->setTarget(
nullptr);
51 auto pl = w ? w->player() :
nullptr;
53 uint8_t slot = pl ? pl->inventory().currentSpellSlot() :
Item::NSLOT;
55 if(w!=
nullptr && w->isCutsceneLock())
60 if(pl!=
nullptr && pl->interactive()!=
nullptr && c!=
nullptr && !c->isFree()) {
61 auto inter = pl->interactive();
62 if(inter->needToLockpick(*pl)) {
63 processPickLock(*pl,*inter,a);
66 if(inter->isLadder()) {
73 if(a==Action::Weapon) {
75 wctrl[WeaponClose] =
true;
77 if(wctrlLast>=WeaponAction::Weapon3 && pl->inventory().currentSpell(
static_cast<uint8_t
>(wctrlLast-3))==
nullptr)
78 wctrlLast=WeaponAction::WeaponBow;
79 if(wctrlLast==WeaponAction::WeaponBow && pl->currentRangedWeapon()==
nullptr)
80 wctrlLast=WeaponAction::WeaponMele;
81 wctrl[wctrlLast] =
true;
86 if(a==Action::WeaponMele) {
88 wctrl[WeaponClose] =
true;
else
89 wctrl[WeaponMele ] =
true;
93 if(a==Action::WeaponBow) {
95 wctrl[WeaponClose] =
true;
else
96 wctrl[WeaponBow ] =
true;
100 if(a>=Action::WeaponMage3 && a<=Action::WeaponMage10) {
101 int id = (a-Action::WeaponMage3+3);
103 wctrl[WeaponClose] =
true;
else
108 if(key==Tempest::KeyEvent::K_Return)
109 ctrl[Action::K_ENTER] =
true;
114 const bool actTunneling =
false;
118 if(a==Action::Forward) {
119 if(pl!=
nullptr && pl->target()!=
nullptr && pl->canFinish(*pl->target()) && !pl->isAttackAnim()) {
130 if(a==Action::Left || a==Action::RotateL)
132 if(a==Action::Right || a==Action::RotateR)
139 if(a==Action::ActionGeneric) {
140 if(pl!=
nullptr && pl->target()!=
nullptr && pl->canFinish(*pl->target()) && !pl->isAttackAnim()) {
143 if(this->wantsToMoveForward())
150 if(a==Action::Parade)
154 if(a==Action::ActionLeft)
156 if(a==Action::ActionRight)
162 std::memset(actrl,0,
sizeof(actrl));
171 FocusAction fk = ActGeneric;
172 if(this->wantsToMoveForward())
174 std::memset(actrl,0,
sizeof(actrl));
180 if(a==Action::Walk) {
185 if(a==Action::Sneak) {
190 if(a==Action::FirstPerson) {
192 c->setFirstPerson(!c->isFirstPerson());
208 auto pl = w ? w->
player() :
nullptr;
211 w->script().playerHotKeyScreenMap(*pl);
214 w->script().playerHotLameHeal(*pl);
217 w->script().playerHotLamePotion(*pl);
223 std::memset(actrl,0,
sizeof(actrl));
225 std::memset(actrl,0,
sizeof(actrl));
230 auto[action, mapping] = actionMapping;
232 if (action == Action::Forward)
233 movement.forwardBackward.main[mappingIndex] = pressed;
234 else if (action == Action::Back)
235 movement.forwardBackward.reverse[mappingIndex] = pressed;
236 else if (action == Action::Right)
237 movement.strafeRightLeft.main[mappingIndex] = pressed;
238 else if (action == Action::Left)
239 movement.strafeRightLeft.reverse[mappingIndex] = pressed;
240 else if (action == Action::RotateR)
241 movement.turnRightLeft.main[mappingIndex] = pressed;
242 else if (action == Action::RotateL)
243 movement.turnRightLeft.reverse[mappingIndex] = pressed;
251 dAngle = std::max(-40.f,std::min(dAngle,40.f));
252 rotMouse += dAngle*0.3f;
256 dAngle = std::max(-100.f,std::min(dAngle,100.f));
257 rotMouseY += dAngle*0.2f;
261 currentFocus = findFocus(¤tFocus);
264 if(!ctrl[Action::ActionGeneric])
267 auto focus = currentFocus;
284 currentFocus =
Focus();
300 if(!ctrl[Action::ActionGeneric])
302 return currentFocus.
npc!=
nullptr;
307 if(w==
nullptr || w->player()==
nullptr)
310 if(w->player()->isDown())
318 if(pl->setInteraction(&it)){
325 if(w==
nullptr || w->player()==
nullptr)
335 if(w->script().isDead(other) || w->script().isUnconscious(other)) {
336 if(!inv.
ransack(*w->player(),other))
337 w->script().printNothingToGet();
347 if(w==
nullptr || w->player()==
nullptr)
359void PlayerControl::moveFocus(FocusAction act) {
362 if(w==
nullptr || c==
nullptr || currentFocus.
npc==
nullptr)
370 auto npos = Tempest::Vec3();
371 for(uint32_t i=0; i<w->npcCount(); ++i) {
372 auto npc = w->npcById(i);
375 auto p = npc->position()+Tempest::Vec3(0,npc->translateY(),0);
378 if(std::abs(p.x)>1.f || std::abs(p.y)>1.f || p.z<0.f)
381 if(!w->testFocusNpc(npc))
384 if(act==ActLeft && p.x<pos.x && (next==
nullptr || npos.x<p.x)) {
388 if(act==ActRight && p.x>pos.x && (next==
nullptr || npos.x>p.x)) {
396 currentFocus.
npc = next;
399void PlayerControl::toggleWalkMode() {
401 if(w==
nullptr || w->player()==
nullptr)
407void PlayerControl::toggleSneakMode() {
409 if(w==
nullptr || w->player()==
nullptr)
416bool PlayerControl::canInteract()
const {
418 if(w==
nullptr || w->player()==
nullptr)
428 std::memset(ctrl, 0,
sizeof(ctrl));
429 std::memset(actrl,0,
sizeof(actrl));
430 std::memset(wctrl,0,
sizeof(wctrl));
433void PlayerControl::marvinF8(uint64_t dt) {
435 if(w==
nullptr || w->player()==
nullptr)
440 float rot = pl.rotationRad();
441 float s = std::sin(rot), c = std::cos(rot);
443 Tempest::Vec3 dp(s,0.8f,-c);
444 pos += dp*6000*float(dt)/1000.f;
448 pl.clearState(
false);
452 pl.setInteraction(
nullptr,
true);
459void PlayerControl::marvinK(uint64_t dt) {
461 if (w ==
nullptr || w->player() ==
nullptr)
466 float rot = pl.rotationRad();
467 float s = std::sin(rot), c = std::cos(rot);
469 Tempest::Vec3 dp(s, 0.0f, -c);
470 pos += dp * 6000 * float(dt) / 1000.f;
472 pl.clearState(
false);
475 pl.setInteraction(
nullptr,
true);
479void PlayerControl::marvinO() {
481 if (w ==
nullptr || w->player() ==
nullptr || w->player()->target() ==
nullptr)
486 w->setPlayer(target);
494 if(w->player()!=
nullptr && w->player()->isDown())
496 if(c!=
nullptr && c->isCutscene())
502 return w->findFocus(*prev);
503 return w->findFocus(
Focus());
511 Npc* pl = w->player();
513 if(camera==
nullptr || (pl!=
nullptr && !camera->isFree()))
518 camera->moveLeft(dt);
522 camera->moveRight(dt);
526 auto turningVal = movement.turnRightLeft.value();
528 camera->rotateRight(dt);
529 else if(turningVal < 0.f)
530 camera->rotateLeft(dt);
532 auto forwardVal = movement.forwardBackward.value();
534 camera->moveForward(dt);
535 else if(forwardVal < 0.f)
536 camera->moveBack(dt);
544 const float dtF = float(dt)/1000.f;
546 Npc* pl = w->player();
549 if(w->isCutsceneLock())
555 if(ctrl[Action::K_F8] &&
Gothic::inst().isMarvinEnabled())
557 if(ctrl[Action::K_K] &&
Gothic::inst().isMarvinEnabled())
559 cacheFocus = ctrl[Action::ActionGeneric];
561 camera->setLookBack(ctrl[Action::LookBack]);
566 static const float speedRotX = 750.f;
567 rotMouse = std::min(std::abs(rotMouse), speedRotX*dtF) * (rotMouse>=0 ? 1 : -1);
571 if(runAngle!=0.f || std::fabs(runAngleDest)>0.01f) {
572 const float speed = 35.f;
573 if(runAngle<runAngleDest) {
575 if(runAngle>runAngleDest)
576 runAngle = runAngleDest;
579 else if(runAngle>runAngleDest) {
581 if(runAngle<runAngleDest)
582 runAngle = runAngleDest;
591void PlayerControl::implMove(uint64_t dt) {
593 Npc& pl = *w->player();
616 implMoveMobsi(pl,dt);
621 if(wctrl[WeaponClose]) {
625 if(wctrl[WeaponMele]) {
630 wctrl[WeaponMele] = !ret;
631 wctrlLast = WeaponMele;
634 if(wctrl[WeaponBow]) {
637 wctrlLast = WeaponBow;
639 wctrl[WeaponBow] =
false;
643 for(uint8_t i=0;i<8;++i) {
644 if(wctrl[Weapon3+i]){
645 if(pl.
inventory().currentSpell(i)!=
nullptr){
646 bool ret = pl.
drawMage(uint8_t(3+i));
647 wctrl[Weapon3+i] = !ret;
648 wctrlLast =
static_cast<WeaponAction
>(Weapon3+i);
650 if(
auto spl = pl.
inventory().currentSpell(i)) {
655 wctrl[Weapon3+i] =
false;
669 if(this->wantsToTurnLeft()) {
674 if(this->wantsToTurnRight()) {
679 if(std::fabs(rotMouse)>0.f) {
707 if(ctrl[Action::K_ENTER]) {
709 ctrl[Action::K_ENTER] =
false;
713 if(actrl[ActGeneric] || actrl[ActForward]) {
714 if(
auto other = pl.
target()) {
715 auto dp = other->position()-pl.
position();
716 pl.
turnTo(dp.x,dp.z,
true,dt);
721 pl.
turnTo(dp.x,dp.z,
false,dt);
726 actrl[ActLeft] =
false;
728 if(actrl[ActRight]) {
730 actrl[ActRight] =
false;
732 if(!actrl[ActForward])
739 if(
auto other = pl.
target()) {
740 auto dp = other->position()-pl.
position();
741 pl.
turnTo(dp.x,dp.z,
true,dt);
745 pl.
turnTo(dp.x,dp.z,
false,dt);
750 actrl[ActLeft] =
false;
752 if(actrl[ActRight]) {
754 actrl[ActRight] =
false;
756 if(!actrl[ActForward])
761 if(actrl[ActForward] || actrl[ActMove]) {
762 ctrl [Action::Forward] = actrl[ActMove];
763 actrl[ActMove] =
false;
765 actrl[ActForward] =
false;
766 if(!ctrl[Action::Forward])
788 actrl[ActForward] =
false;
797 actrl[ActKill] =
false;
800 if(actrl[ActLeft] || actrl[ActRight] || actrl[ActBack]) {
809 movement.strafeRightLeft.reset();
812 movement.strafeRightLeft.reset();
818 actrl[ActLeft] =
false;
819 actrl[ActRight] =
false;
826 actrl[ActLeft] =
false;
828 if(actrl[ActRight]) {
830 actrl[ActRight] =
false;
835 if(this->wantsToStrafeLeft()) {
836 ani = Npc::Anim::MoveL;
838 else if(this->wantsToStrafeRight()) {
839 ani = Npc::Anim::MoveR;
841 else if(this->wantsToMoveForward()) {
843 ani = Npc::Anim::Move;
850 else if(this->wantsToMoveBackward()) {
852 ani = Npc::Anim::MoveBack;
860 if(ctrl[Action::Jump]) {
862 ani = Npc::Anim::Idle;
865 ani = Npc::Anim::Move;
871 auto& g = w->script().guildVal();
872 auto gl = pl.
guild();
876 jump.
anim = Npc::Anim::JumpUp;
887 ani = Npc::Anim::Jump;
890 ani = Npc::Anim::Jump;
895 if(ani==Npc::Anim::Jump) {
901 if((ani==Npc::Anim::MoveL || ani==Npc::Anim::MoveR) && pl.
hasState(
BS_RUN)) {
902 ani = Npc::Anim::Idle;
907 ani = Npc::Anim::NoAnim;
909 if((ani==Npc::Anim::MoveL || ani==Npc::Anim::MoveR) &&
912 ani = Npc::Anim::NoAnim;
917 ani = (ani==Npc::Anim::Move) ? Npc::Anim::Idle :
Npc::Anim::NoAnim;
921 if(ani!=Npc::Anim::NoAnim)
925 setAnimRotate(pl, rot, ani==Npc::Anim::Idle ? rotation : 0, movement.turnRightLeft.any(), dt);
926 if(actrl[ActGeneric] || ani==Npc::Anim::MoveL || ani==Npc::Anim::MoveR || pl.
isFinishingMove()) {
927 processAutoRotate(pl,rot,dt);
930 if(ani==Npc::Anim::Move && (rotation!=0 || rotY!=0)) {
931 assignRunAngle(pl,rot,dt);
933 assignRunAngle(pl,pl.
rotation(),dt);
938void PlayerControl::implMoveMobsi(
Npc& pl, uint64_t ) {
947 if(inter->needToLockpick(pl) && !inter->isCracked()) {
951 if(!inter->isLadder() && inter->isStaticState() && !inter->isDetachState(pl)) {
952 auto stateId = inter->stateId();
953 if(inter->canQuitAtState(pl,stateId))
957 if(inter->isLadder()) {
973 auto& script = w->
script();
974 const size_t ItKE_lockpick = script.
lockPickId();
989 while(pickLockProgress<cmp.size()) {
990 auto c = cmp[pickLockProgress];
991 if(c==
'l' || c==
'L' || c==
'r' || c==
'R')
996 if(pickLockProgress<cmp.size() && std::toupper(cmp[pickLockProgress])!=ch) {
997 pickLockProgress = 0;
999 if(dex<=int32_t(script.rand(100))) {
1000 script.invokePickLock(pl,0,1);
1002 if(pl.
inventory().itemCount(ItKE_lockpick)==0) {
1007 script.invokePickLock(pl,0,0);
1011 if(pickLockProgress>=cmp.size()) {
1012 script.invokePickLock(pl,1,1);
1014 pickLockProgress = 0;
1016 script.invokePickLock(pl,1,0);
1029void PlayerControl::quitPicklock(
Npc& pl) {
1031 pickLockProgress = 0;
1035void PlayerControl::assignRunAngle(
Npc& pl,
float rotation, uint64_t dt) {
1036 float dtF = (float(dt)/1000.f);
1038 float dangle = (rotation-angle)/dtF;
1039 float sgn = (dangle>0 ? 1 : -1);
1040 auto& wrld = pl.
world();
1043 if(runAngleSmooth<wrld.tickCount())
1048 const float maxV = 15.0f;
1049 dangle = std::pow(std::abs(dangle)/maxV,2.f)*maxV*sgn;
1053 dest = std::min( dangle,maxV);
1055 dest = -std::min(-dangle,maxV);
1057 float a = std::clamp(dtF*2.5f, 0.f, 1.f);
1058 runAngleDest = runAngleDest*(1.f-a)+dest*a;
1059 runAngleSmooth = wrld.tickCount() + 200;
1062void PlayerControl::setAnimRotate(
Npc& pl,
float rotation,
int anim,
bool force, uint64_t dt) {
1063 float dtF = (float(dt)/1000.f);
1065 float dangle = (rotation-angle)/dtF;
1066 auto& wrld = pl.
world();
1068 if(std::fabs(dangle)<100.f && !force)
1072 if(rotationAni==anim && anim!=0)
1074 if(!force && wrld.tickCount()<turnAniSmooth)
1076 turnAniSmooth = wrld.tickCount() + 150;
1081void PlayerControl::processAutoRotate(
Npc& pl,
float& rot, uint64_t dt) {
1082 if(
auto other = pl.
target()) {
1087 auto dp = other->position()-pl.
position();
1088 auto gl = pl.
guild();
1089 float step = float(pl.
world().script().guildVal().turn_speed[gl]);
1090 if(actrl[ActGeneric])
Tempest::Matrix4x4 viewProj() const
Interactive * interactive
uint32_t lockPickId() const
Tempest::Signal< void()> onSettingsChanged
const World * world() const
auto version() const -> const VersionInfo &
static int settingsGetI(std::string_view sec, std::string_view name)
Tempest::Signal< void(std::string_view)> onPrint
std::string_view pickLockCode() const
void onKeyInput(KeyCodec::Action act)
void setAsCracked(bool c)
virtual bool isTorchBurn() const
void startDialog(Npc &other)
auto weaponState() const -> WeaponState
bool startClimb(JumpStatus jump)
bool isInState(ScriptFn stateFn) const
bool drawMage(uint8_t slot)
void setWalkMode(WalkBit m)
bool rotateTo(float dx, float dz, float speed, AnimationSolver::TurnType anim, uint64_t dt)
void setDirection(const Tempest::Vec3 &pos)
void setAnimRotate(int rot)
bool shootBow(Interactive *focOverride=nullptr)
int32_t talentValue(Talent t) const
bool hasState(BodyState s) const
int32_t attribute(Attribute a) const
void setDirectionY(float rotation)
auto inventory() const -> const Inventory &
auto interactive() const -> Interactive *
Item * currentMeleeWeapon()
bool closeWeapon(bool noAnim)
void endCastSpell(bool playerCtrl=false)
auto position() const -> Tempest::Vec3
bool isRotationAllowed() const
void setRunAngle(float angle)
bool isAttackAnim() const
bool turnTo(float dx, float dz, bool noAnim, uint64_t dt)
bool setInteraction(Interactive *id, bool quick=false)
bool isAiQueueEmpty() const
bool hasAmmunition() const
void delItem(size_t id, uint32_t amount)
Item * currentRangedWeapon()
BodyState bodyStateMasked() const
bool canSwitchWeapon() const
bool isFinishingMove() const
auto beginCastSpell() -> BeginCastResult
void actionFocus(Npc &other)
bool isPressed(KeyCodec::Action a) const
bool interact(Interactive &it)
void setTarget(Npc *other)
void onRotateMouse(float dAngle)
void onKeyReleased(KeyCodec::Action a, KeyCodec::Mapping mapping=KeyCodec::Mapping::Primary)
bool tickMove(uint64_t dt)
void onRotateMouseDy(float dAngle)
PlayerControl(DialogMenu &dlg, InventoryMenu &inv)
bool tickCameraMove(uint64_t dt)
bool hasActionFocus() const
void onKeyPressed(KeyCodec::Action a, Tempest::Event::KeyType key, KeyCodec::Mapping mapping=KeyCodec::Mapping::Primary)
Tempest::Vec3 position() const
GameScript & script() const
Encapsulates an in-game action and a key mapping that caused it to be fired.
AnimationSolver::Anim anim