Skip to content

Commit

Permalink
Issue #66 Handle door interactable (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
xavieran authored Jul 5, 2024
1 parent d1e0c5d commit 5b80334
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 26 deletions.
1 change: 1 addition & 0 deletions bak/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ std::ostream& operator<<(std::ostream& os, const GenericContainer& gc)
{
os << "GenericContainer{ " << gc.GetHeader();
if (gc.HasLock()) os << gc.GetLock();
if (gc.HasDoor()) os << gc.GetDoor();
if (gc.HasDialog()) os << gc.GetDialog();
if (gc.HasShop()) os << gc.GetShop();
if (gc.HasEncounter()) os << gc.GetEncounter();
Expand Down
30 changes: 19 additions & 11 deletions bak/container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ enum class ContainerProperty
HasDialog = 1, // 0x2
HasShop = 2, // 0x4
HasEncounter = 3, // 0x8
HasTime = 4 // 0x10
HasTime = 4, // 0x10
HasDoor = 5 // 0x20
};

struct ContainerWorldLocationTag {};
Expand Down Expand Up @@ -93,6 +94,7 @@ class ContainerHeader
bool HasShop() const { return CheckBitSet(mFlags, ContainerProperty::HasShop); }
bool HasEncounter() const { return CheckBitSet(mFlags, ContainerProperty::HasEncounter); }
bool HasTime() const { return CheckBitSet(mFlags, ContainerProperty::HasTime); }
bool HasDoor() const { return CheckBitSet(mFlags, ContainerProperty::HasDoor); }
bool HasInventory() const { return mCapacity != 0; }

std::uint32_t GetAddress() const { return mAddress; }
Expand Down Expand Up @@ -136,6 +138,7 @@ class GenericContainer final : public IContainer {
GenericContainer(
ContainerHeader header,
std::optional<LockStats> lock,
std::optional<Door> door,
std::optional<ContainerDialog> dialog,
std::optional<ShopStats> shop,
std::optional<ContainerEncounter> encounter,
Expand All @@ -144,6 +147,7 @@ class GenericContainer final : public IContainer {
:
mHeader{header},
mLock{lock},
mDoor{door},
mDialog{dialog},
mShop{shop},
mEncounter{encounter},
Expand All @@ -167,6 +171,10 @@ class GenericContainer final : public IContainer {
const ContainerDialog& GetDialog() const { ASSERT(mDialog); return *mDialog; }
ContainerDialog& GetDialog() { ASSERT(mDialog); return *mDialog; }

bool HasDoor() const { return bool{mDoor}; }
DoorIndex GetDoor() const { ASSERT(mDoor); return mDoor->mDoorIndex; }
DoorIndex GetDoor() { ASSERT(mDoor); return mDoor->mDoorIndex; }

bool HasShop() const { return bool{mShop}; }
ShopStats& GetShop() override { ASSERT(mShop); return *mShop; }
const ShopStats& GetShop() const override { ASSERT(mShop); return *mShop; }
Expand Down Expand Up @@ -215,6 +223,7 @@ class GenericContainer final : public IContainer {
private:
ContainerHeader mHeader;
std::optional<LockStats> mLock;
std::optional<Door> mDoor;
std::optional<ContainerDialog> mDialog;
std::optional<ShopStats> mShop;
std::optional<ContainerEncounter> mEncounter;
Expand All @@ -228,28 +237,26 @@ GenericContainer LoadGenericContainer(FileBuffer& fb)
auto header = ContainerHeader{HeaderTag{}, fb};

auto lockData = std::optional<LockStats>{};
auto door = std::optional<Door>{};
auto dialog = std::optional<ContainerDialog>{};
auto shopData = std::optional<ShopStats>{};
auto encounter = std::optional<ContainerEncounter>{};
auto lastAccessed = std::optional<Time>{};

auto inventory = LoadInventory(fb, header.mItems, header.mCapacity);

if (header.mFlags == 0x21)
{
// This is not actually correct interpretation
// for this property type
const auto contextVar = fb.GetUint8();
const auto dialogOrder = fb.GetUint8();
const auto dialogKey = KeyTarget{fb.GetUint32LE()};
dialog = ContainerDialog{contextVar, dialogOrder, dialogKey};
}
else
{
if (header.HasLock())
{
lockData = LoadLock(fb);
}

if (header.HasDoor())
{
const auto doorIndex = fb.GetUint16LE();
door = Door{DoorIndex{doorIndex}};
}

if (header.HasDialog())
{
const auto contextVar = fb.GetUint8();
Expand Down Expand Up @@ -299,6 +306,7 @@ GenericContainer LoadGenericContainer(FileBuffer& fb)
return GenericContainer{
header,
lockData,
door,
dialog,
shopData,
encounter,
Expand Down
2 changes: 2 additions & 0 deletions bak/dialogSources.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class DialogSources
static constexpr auto mStump = KeyTarget{0xba};
static constexpr auto mTrappedAnimal = KeyTarget{0xab};
static constexpr auto mWell = KeyTarget{0xbc};
static constexpr auto mDoorTooClose = KeyTarget{0x9d};

static constexpr auto mCharacterIsNotASpellcaster = KeyTarget{0xd8};

Expand All @@ -201,6 +202,7 @@ class DialogSources
static constexpr auto mStartOfChapterActions =
KeyTarget{0x1e8497};
static constexpr auto mDragonsBreath = 0xc7;

};

}
2 changes: 1 addition & 1 deletion bak/gameState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ void GameState::SetDefaultDialogTextVariables()

void GameState::SetDialogTextVariable(unsigned index, unsigned attribute)
{
mLogger.Debug() << __FUNCTION__ << "(" << index << ", " << attribute << ")\n";
mLogger.Spam() << __FUNCTION__ << "(" << index << ", " << attribute << ")\n";
assert(attribute > 0);
// remember we always switch on attribute - 1...
switch (attribute - 1)
Expand Down
18 changes: 18 additions & 0 deletions bak/lock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ std::ostream& operator<<(std::ostream& os, const LockStats& lock)
return os;
}


LockStats LoadLock(FileBuffer& fb)
{
const auto lockFlag = fb.GetUint8();
Expand All @@ -23,6 +24,11 @@ LockStats LoadLock(FileBuffer& fb)
return LockStats{lockFlag, picklock, fairyChestIndex, damage};
}

std::ostream& operator<<(std::ostream& os, const Door& door)
{
os << "Door { index: " << door.mDoorIndex << "}";
return os;
}

LockType ClassifyLock(unsigned lockRating)
{
Expand All @@ -36,6 +42,18 @@ LockType ClassifyLock(unsigned lockRating)
return LockType::Unpickable;
}

LockType ClassifyDoor(unsigned lockRating)
{
if (lockRating < 50)
return LockType::Easy;
else if (lockRating < 80)
return LockType::Medium;
else if (lockRating < 100)
return LockType::Hard;
else
return LockType::Unpickable;
}

std::string_view ToString(LockType lock)
{
switch (lock)
Expand Down
7 changes: 7 additions & 0 deletions bak/lock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ struct FairyChest

std::ostream& operator<<(std::ostream&, const LockStats&);

struct Door
{
DoorIndex mDoorIndex;
};

std::ostream& operator<<(std::ostream&, const Door&);

// This is the lock "image" type
enum class LockType
{
Expand Down
5 changes: 5 additions & 0 deletions bak/save.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ void Save(const GenericContainer& gc, FileBuffer& fb)
fb.PutUint8(lock.mTrapDamage);
}

if (gc.HasDoor())
{
fb.Skip(2); // No need to write anything for doors
}

if (gc.HasDialog())
fb.Skip(6); // Don't need to write anything for dialog

Expand Down
1 change: 1 addition & 0 deletions bak/state/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_library(bakState
customStateChoice.hpp customStateChoice.cpp
dialog.hpp dialog.cpp
door.hpp door.cpp
encounter.hpp encounter.cpp
event.hpp event.cpp
item.hpp item.cpp
Expand Down
22 changes: 22 additions & 0 deletions bak/state/door.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "bak/state/door.hpp"

#include "bak/state/event.hpp"
#include "bak/state/offsets.hpp"

#include "bak/gameState.hpp"

#include "com/logger.hpp"

namespace BAK::State {

bool GetDoorState(const GameState& gs, unsigned doorIndex)
{
return gs.ReadEventBool(sDoorFlag + doorIndex);
}

void SetDoorState(FileBuffer& fb, unsigned doorIndex, bool state)
{
SetEventFlag(fb, sDoorFlag + doorIndex, state);
}

}
15 changes: 15 additions & 0 deletions bak/state/door.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include "bak/file/fileBuffer.hpp"

namespace BAK {
class GameState;
}

namespace BAK::State {

bool GetDoorState(const GameState&, unsigned doorIndex);
void SetDoorState(FileBuffer&, unsigned doorIndex, bool state);

}

1 change: 1 addition & 0 deletions bak/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using Chapter = Bounded<StrongType<unsigned, struct ChapterTag>, 1, 11>;
using EntityIndex = StrongType<unsigned, struct EntityIndexTag>;
using ChoiceIndex = StrongType<unsigned, struct ChoiceIndexTag>;
using CombatantIndex = StrongType<unsigned, struct CombatantIndexTag>;
using DoorIndex = StrongType<unsigned, struct DoorIndexTag>;
using MonsterIndex = StrongType<unsigned, struct MonsterIndexTag>;
using SpellIndex = StrongType<std::uint64_t, struct SpellIndexTag>;
using SongIndex = std::uint16_t;
Expand Down
1 change: 1 addition & 0 deletions game/gameRunner.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class GameRunner : public BAK::IZoneLoader
std::nullopt,
std::nullopt,
std::nullopt,
std::nullopt,
BAK::Inventory{0}},
mSystems{nullptr},
mSavedAngle{0},
Expand Down
79 changes: 67 additions & 12 deletions game/interactable/door.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,107 @@

#include "game/interactable/IInteractable.hpp"

#include "bak/dialogTarget.hpp"
#include "bak/state/door.hpp"

#include "bak/IContainer.hpp"
#include "bak/container.hpp"
#include "bak/dialog.hpp"
#include "bak/dialogSources.hpp"
#include "bak/gameState.hpp"
#include "bak/itemNumbers.hpp"

#include "graphics/glm.hpp"

#include "gui/IDialogScene.hpp"
#include "gui/IGuiManager.hpp"

#include <glm/geometric.hpp>


namespace Game::Interactable {

/*
* These containers have an optional dialog and inventory encounter
*/
class Door : public IInteractable
{
private:

public:
Door(
Gui::IGuiManager& guiManager,
BAK::Target target,
const EncounterCallback& encounterCallback)
BAK::GameState& gameState)
:
mGuiManager{guiManager},
mGameState{gameState},
mDialogScene{
[]{},
[]{},
[&](const auto& choice){ DialogFinished(choice); }},
mDefaultDialog{target},
mContainer{nullptr},
mEncounterCallback{encounterCallback}
mContainer{nullptr}
{}

void BeginInteraction(BAK::GenericContainer& container, BAK::EntityType) override
{
mContainer = &container;

StartDialog(mDefaultDialog);
assert(mContainer->HasDoor());
const auto doorIndex = mContainer->GetDoor();
const auto doorState = BAK::State::GetDoorState(mGameState, doorIndex.mValue);
Logging::LogInfo("Door") << "DoorIndex: " << doorIndex << " DoorOpen? " << std::boolalpha << doorState << " locked? " << (mContainer->HasLock() ? mContainer->GetLock().mRating : 0) << "\n";

const auto playerPos = glm::cast<float>(mGameState.GetLocation().mPosition);
const auto doorPos = glm::cast<float>(container.GetHeader().GetPosition());
if (glm::distance(playerPos, doorPos) < 800)
{
StartDialog(BAK::DialogSources::mDoorTooClose);
}
else if (doorState)
{
// Door opened, can always close it
CloseDoor();
}
else if (mContainer->HasLock() && mContainer->GetLock().mRating > 0)
{
mGameState.SetDialogContext_7530(1);
StartDialog(BAK::DialogSources::mChooseUnlock);
}
else
{
OpenDoor();
}
}

void DialogFinished(const std::optional<BAK::ChoiceIndex>& choice)
{
ASSERT(mContainer);
ASSERT(choice);

if (choice->mValue == BAK::Keywords::sYesIndex)
{
mGuiManager.ShowLock(
mContainer,
[this]{ LockFinished(); });
}
}

void LockFinished()
{
if (mGuiManager.IsLockOpened())
{
OpenDoor();
}
}

void OpenDoor()
{
const auto doorIndex = mContainer->GetDoor().mValue;
mGameState.Apply(BAK::State::SetDoorState, doorIndex, true);
Logging::LogInfo(__FUNCTION__) << " index; " << doorIndex << "\n";
}

void CloseDoor()
{
const auto doorIndex = mContainer->GetDoor().mValue;
mGameState.Apply(BAK::State::SetDoorState, doorIndex, false);
Logging::LogInfo(__FUNCTION__) << " index; " << doorIndex << "\n";
}

void EncounterFinished() override
Expand All @@ -64,10 +120,9 @@ class Door : public IInteractable

private:
Gui::IGuiManager& mGuiManager;
BAK::GameState& mGameState;
Gui::DynamicDialogScene mDialogScene;
BAK::Target mDefaultDialog;
BAK::GenericContainer* mContainer;
const EncounterCallback& mEncounterCallback;
};

}
3 changes: 1 addition & 2 deletions game/interactable/factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ class InteractableFactory
case InteractableType::Door:
return std::make_unique<Door>(
mGuiManager,
BAK::DialogSources::mUnknownObject,
[](auto){});
mGameState);
case InteractableType::Corn:
return MakeGeneric(BAK::DialogSources::mCorn);
case InteractableType::CrystalTree:
Expand Down

0 comments on commit 5b80334

Please sign in to comment.