diff --git a/CMakeSettings.json b/CMakeSettings.json index 0c5fbf94..255a890a 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -1,16 +1,5 @@ { "configurations": [ - { - "name": "x64-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "" - }, { "name": "x64-Release", "generator": "Ninja", @@ -20,8 +9,7 @@ "cmakeCommandArgs": "", "buildCommandArgs": "", "ctestCommandArgs": "", - "inheritEnvironments": [ "msvc_x64_x64" ], - "variables": [] + "inheritEnvironments": [ "msvc_x64_x64" ] } ] } \ No newline at end of file diff --git a/assets/imgs/frame.png b/assets/imgs/frame.png new file mode 100644 index 00000000..c5ab3938 Binary files /dev/null and b/assets/imgs/frame.png differ diff --git a/assets/imgs/pot_health.png b/assets/imgs/pot_health.png new file mode 100644 index 00000000..a6968c37 Binary files /dev/null and b/assets/imgs/pot_health.png differ diff --git a/assets/imgs/pot_invisibility.png b/assets/imgs/pot_invisibility.png new file mode 100644 index 00000000..6c98b3f1 Binary files /dev/null and b/assets/imgs/pot_invisibility.png differ diff --git a/assets/imgs/pot_nausea.png b/assets/imgs/pot_nausea.png new file mode 100644 index 00000000..63bc788b Binary files /dev/null and b/assets/imgs/pot_nausea.png differ diff --git a/assets/imgs/selected_frame.png b/assets/imgs/selected_frame.png new file mode 100644 index 00000000..3b450cd2 Binary files /dev/null and b/assets/imgs/selected_frame.png differ diff --git a/include/client/gui/gui.hpp b/include/client/gui/gui.hpp index 3c617e75..e6ccdfef 100644 --- a/include/client/gui/gui.hpp +++ b/include/client/gui/gui.hpp @@ -346,6 +346,12 @@ class GUI { * TODO: this is not implemented yet */ void _layoutGameHUD(); + + /** + * @brief Displays the Game HUD for both gameHUD and EscMenu layout + */ + void _sharedGameHUD(); + /** * @brief Displays the menu which appears when the player presses Escape while playing * diff --git a/include/client/gui/img/img.hpp b/include/client/gui/img/img.hpp index 4c9521ad..2a0f5bd8 100644 --- a/include/client/gui/img/img.hpp +++ b/include/client/gui/img/img.hpp @@ -16,10 +16,15 @@ namespace gui::img { */ enum class ImgID { Yoshi, - AwesomeSauce + AwesomeSauce, + ItemFrame, + SelectedFrame, + HealthPotion, + NauseaPotion, + InvisPotion }; #define GET_ALL_IMG_IDS() \ - {ImgID::Yoshi, ImgID::AwesomeSauce} + {ImgID::Yoshi, ImgID::AwesomeSauce, ImgID::ItemFrame, ImgID::SelectedFrame, ImgID::HealthPotion, ImgID::NauseaPotion, ImgID::InvisPotion} /** * Representation of a loaded image diff --git a/include/server/game/constants.hpp b/include/server/game/constants.hpp index 91b1ce26..c37518be 100644 --- a/include/server/game/constants.hpp +++ b/include/server/game/constants.hpp @@ -10,7 +10,6 @@ #define MAX_WALLS 1000 #define MAX_TRAPS 10 #define MAX_SPELLS 4 -#define MAX_ITEMS 4 /* Constants */ #define FIRST_TIMESTEP 0 @@ -27,6 +26,19 @@ // Player Stat Constants #define INITIAL_HEALTH 100 +/* Inventory */ +#define INVENTORY_SIZE 4 + +/* Potion Stats */ +#define RESTORE_HEALTH 20 +#define HEALTH_DURATION 0 + +#define NAUSEA_SCALAR -1.0f +#define NAUSEA_DURATION 10 + +#define INVIS_OPACITY 0.5 +#define INVIS_DURATION 15 + /* Game */ #define GRAVITY 0.03f #define PLAYER_SPEED 1.5f diff --git a/include/server/game/gridcell.hpp b/include/server/game/gridcell.hpp index d6e166db..161a3fca 100644 --- a/include/server/game/gridcell.hpp +++ b/include/server/game/gridcell.hpp @@ -6,6 +6,9 @@ enum class CellType { Spawn, Enemy, SpikeTrap, + HealthPotion, + NauseaPotion, + InvisibilityPotion, Unknown }; diff --git a/include/server/game/item.hpp b/include/server/game/item.hpp index 68caad40..9f7f1411 100644 --- a/include/server/game/item.hpp +++ b/include/server/game/item.hpp @@ -1,18 +1,28 @@ #pragma once -#include "server/game/constants.hpp" #include "server/game/object.hpp" -#include "shared/game/sharedobject.hpp" +#include "server/game/servergamestate.hpp" class Item : public Object { public: + SharedItemInfo iteminfo; - SharedItemInfo iteminfo{}; + /** + * @param type Type of Object + * @param movable Movable factor for physics + * @param corner Corner position of the item + * @param model Model applied for the item + * @param dimensions Dimensions of the item + */ - Item(); - ~Item(); + Item(ObjectType type, bool movable, glm::vec3 corner, ModelType model, glm::vec3 dimensions); + + virtual void useItem(Object* other, ServerGameState& state); + + void doCollision(Object* other, ServerGameState* state) override; + + SharedObject toShared() override; - virtual SharedObject toShared() override; private: }; \ No newline at end of file diff --git a/include/server/game/object.hpp b/include/server/game/object.hpp index 807871f1..2f2b4470 100644 --- a/include/server/game/object.hpp +++ b/include/server/game/object.hpp @@ -33,12 +33,13 @@ struct Physics { * set by the setModel function! * NOTE: velocity defaults to 0 * NOTE: velocityMultitplier defaults to 1 + * NOTE: Dizziness defaults to 1 */ Physics(bool movable, Collider collider, glm::vec3 corner, glm::vec3 facing, glm::vec3 dimensions = glm::vec3(1.0f)): shared{.corner=corner, .facing=facing, .dimensions=dimensions}, - movable(movable), velocity(glm::vec3(0.0f)), velocityMultiplier(glm::vec3(1.0f)), + movable(movable), velocity(glm::vec3(0.0f)), velocityMultiplier(glm::vec3(1.0f)), nauseous(1.0f), collider(collider) {} @@ -64,6 +65,11 @@ struct Physics { */ glm::vec3 velocityMultiplier; + /** + * @brief Factor for potion of nausea + */ + float nauseous; + /** * @brief This object's collider type. */ diff --git a/include/server/game/objectmanager.hpp b/include/server/game/objectmanager.hpp index be1708f5..00f5a098 100644 --- a/include/server/game/objectmanager.hpp +++ b/include/server/game/objectmanager.hpp @@ -3,12 +3,12 @@ #include #include "server/game/object.hpp" -#include "server/game/item.hpp" #include "server/game/player.hpp" #include "server/game/enemy.hpp" #include "server/game/solidsurface.hpp" class Trap; // forward declaration to use Trap* +class Item; #include "shared/utilities/smartvector.hpp" diff --git a/include/server/game/player.hpp b/include/server/game/player.hpp index 7869a0d4..2d0be437 100644 --- a/include/server/game/player.hpp +++ b/include/server/game/player.hpp @@ -4,10 +4,14 @@ #include "server/game/object.hpp" #include "server/game/creature.hpp" #include "shared/game/sharedobject.hpp" +#include class Player : public Creature { public: SharedPlayerInfo info; + SharedInventory sharedInventory; + + std::unordered_map inventory; /** * @param Corner corner position of the player diff --git a/include/server/game/potion.hpp b/include/server/game/potion.hpp new file mode 100644 index 00000000..cd1c7018 --- /dev/null +++ b/include/server/game/potion.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "server/game/item.hpp" +#include "server/game/object.hpp" +#include + +enum class PotionType { + Health, + Nausea, + Invisibility +}; + +class Potion : public Item { +public: + Potion(glm::vec3 corner, glm::vec3 dimensions, PotionType type); + + int duration; + int effectScalar; + PotionType potType; + + void useItem(Object* other, ServerGameState& state) override; + bool timeOut(); + void revertEffect(ServerGameState& state); + +private: + std::chrono::time_point used_time; + Player* usedPlayer; +}; \ No newline at end of file diff --git a/include/server/game/servergamestate.hpp b/include/server/game/servergamestate.hpp index e93771bd..559b8776 100644 --- a/include/server/game/servergamestate.hpp +++ b/include/server/game/servergamestate.hpp @@ -5,9 +5,9 @@ #include "shared/utilities/config.hpp" #include "shared/utilities/smartvector.hpp" #include "server/game/object.hpp" -#include "server/game/objectmanager.hpp" #include "shared/game/event.hpp" #include "server/game/grid.hpp" +#include "server/game/objectmanager.hpp" #include #include @@ -68,9 +68,7 @@ class ServerGameState { void updateMovement(); - // TODO: Add implementations of items - - void useItem(); + void updateItems(); void updateTraps(); diff --git a/include/server/game/spell.hpp b/include/server/game/spell.hpp new file mode 100644 index 00000000..d4e4ce29 --- /dev/null +++ b/include/server/game/spell.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "server/game/servergamestate.hpp" +#include "server/game/object.hpp" + +enum class SpellType { + Fireball, +}; + +class Spell : public Item { +public: + Spell(glm::vec3 corner, glm::vec3 dimensions); + + SpellType spellType; + + void setSpellType(SpellType type); + +private: + +}; \ No newline at end of file diff --git a/include/shared/game/event.hpp b/include/shared/game/event.hpp index a0014119..7e7cb880 100644 --- a/include/shared/game/event.hpp +++ b/include/shared/game/event.hpp @@ -34,6 +34,9 @@ enum class EventType { MoveRelative, MoveAbsolute, SpawnEntity, + SelectItem, + UseItem, + DropItem, }; enum class ActionType { @@ -180,6 +183,50 @@ struct SpawnEntityEvent { DEF_SERIALIZE(Archive& ar, const unsigned int version) { // TODO: } +}; + +/** + * Event for selecting which item to use + */ +struct SelectItemEvent { + SelectItemEvent() {} + explicit SelectItemEvent(EntityID playerEID, int itemNum) : playerEID(playerEID), itemNum(itemNum) {} + + EntityID playerEID; + int itemNum; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar& playerEID& itemNum; + } + +}; + +/** + * Event for entity to use item + */ +struct UseItemEvent { + UseItemEvent() {} + explicit UseItemEvent(EntityID playerEID) : playerEID(playerEID) {} + + EntityID playerEID; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar& playerEID; + } +}; + +/** + * Event for dropping item in inventory + */ +struct DropItemEvent { + DropItemEvent() {} + explicit DropItemEvent(EntityID playerEID) : playerEID(playerEID){} + + EntityID playerEID; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar& playerEID; + } }; @@ -195,7 +242,10 @@ using EventData = boost::variant< StopActionEvent, MoveRelativeEvent, MoveAbsoluteEvent, - SpawnEntityEvent + SpawnEntityEvent, + SelectItemEvent, + UseItemEvent, + DropItemEvent >; /** diff --git a/include/shared/game/sharedmodel.hpp b/include/shared/game/sharedmodel.hpp index 84a80b71..beedae51 100644 --- a/include/shared/game/sharedmodel.hpp +++ b/include/shared/game/sharedmodel.hpp @@ -6,5 +6,9 @@ enum class ModelType { Cube, Player, - WarrenBear + WarrenBear, + HealthPotion, + NauseaPotion, + InvisibilityPotion, + Frame }; \ No newline at end of file diff --git a/include/shared/game/sharedobject.hpp b/include/shared/game/sharedobject.hpp index dbd9cb14..0e79567c 100644 --- a/include/shared/game/sharedobject.hpp +++ b/include/shared/game/sharedobject.hpp @@ -14,11 +14,12 @@ */ enum class ObjectType { Object, // Generic object type (base class) - Item, SolidSurface, + Potion, Player, Enemy, - SpikeTrap + SpikeTrap, + Spell }; /** @@ -42,17 +43,34 @@ struct SharedStats { } }; -struct SharedItemInfo { - enum ItemType { healing, swiftness, invisible, key }; +struct SharedInventory { + // need to share itemtype data... + int selected; + int inventory_size; + std::unordered_map inventory; + + DEF_SERIALIZE(Archive& ar, const unsigned int version) { + ar& selected& inventory_size& inventory; + } +}; + +/** + * @brief An enum for the type of an item + */ +enum class ItemType { + Weapon, + Spell, + Potion, + Blank +}; + +struct SharedItemInfo { bool held; // for rendering - bool used; - float scalar; - float timer; - ItemType type; + bool used; // for rendering DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & held & used & scalar & timer & type; + ar& used& held; } }; @@ -138,12 +156,13 @@ class SharedObject { boost::optional solidSurface; boost::optional trapInfo; boost::optional playerInfo; + boost::optional inventoryInfo; SharedObject() {} // cppcheck-suppress uninitMemberVar ~SharedObject() {} DEF_SERIALIZE(Archive& ar, const unsigned int version) { - ar & globalID & type & physics & modelType & stats & iteminfo & solidSurface & trapInfo & playerInfo; + ar & globalID & type & physics & modelType & stats & iteminfo & solidSurface & trapInfo & playerInfo & inventoryInfo; } private: }; \ No newline at end of file diff --git a/maps/room.maze b/maps/room.maze index 3d53dad4..d57af3c5 100644 --- a/maps/room.maze +++ b/maps/room.maze @@ -1,5 +1,9 @@ ######### -#.@.@.@.# -#.@@@@@.# -#.@.@.@X# +#h.n.i.h# +#.......# +#.......# +#.......# +#.......# +#.......# +#.@.n..X# ######### \ No newline at end of file diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 260cd04f..251bbbca 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -76,7 +76,7 @@ glm::vec3 Camera::move(bool is_x_axis, float dir) { glm::vec3 effCameraFront = glm::normalize(glm::vec3(cameraFront.x, 0.0f, cameraFront.z)); if (is_x_axis) { - return dir * glm::normalize(glm::cross(effCameraFront, cameraUp)) * speed; + return dir * speed * glm::normalize(glm::cross(effCameraFront, cameraUp)); } else { return dir * speed * effCameraFront; // return dir * speed * cameraRight; diff --git a/src/client/client.cpp b/src/client/client.cpp index 5a74d539..d86f74e6 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -362,6 +362,19 @@ void Client::draw() { true); break; } + case ObjectType::Potion: { + if (!sharedObject->iteminfo->held && !sharedObject->iteminfo->used) { + auto cube = std::make_unique(glm::vec3(1.0f)); + cube->scaleAbsolute(sharedObject->physics.dimensions); + cube->translateAbsolute(sharedObject->physics.getCenterPosition()); + cube->draw(this->cube_shader, + this->cam->getViewProj(), + this->cam->getPos(), + glm::vec3(), + true); + } + break; + } default: break; } @@ -419,6 +432,42 @@ void Client::keyCallback(GLFWwindow *window, int key, int scancode, int action, Client::time_of_last_keystroke = getMsSinceEpoch(); break; + case GLFW_KEY_E: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::UseItem, UseItemEvent(eid.value()))); + } + break; + + case GLFW_KEY_Q: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::DropItem, DropItemEvent(eid.value()))); + } + break; + + case GLFW_KEY_1: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 1))); + } + break; + + case GLFW_KEY_2: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 2))); + } + break; + + case GLFW_KEY_3: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 3))); + } + break; + + case GLFW_KEY_4: + if (eid.has_value()) { + this->session->sendEventAsync(Event(eid.value(), EventType::SelectItem, SelectItemEvent(eid.value(), 4))); + } + break; + /* For movement keys (WASD), activate flags and use it to generate * movement in idleCallback() instead of sending individual events */ diff --git a/src/client/gui/gui.cpp b/src/client/gui/gui.cpp index fc88f2dc..0ae829d9 100644 --- a/src/client/gui/gui.cpp +++ b/src/client/gui/gui.cpp @@ -6,6 +6,7 @@ #include "shared/utilities/rng.hpp" #include "client/client.hpp" #include "shared/game/sharedgamestate.hpp" +#include "shared/game/sharedobject.hpp" #include "shared/utilities/time.hpp" namespace gui { @@ -139,6 +140,7 @@ void GUI::layoutFrame(GUIState state) { break; case GUIState::GAME_ESC_MENU: glfwSetInputMode(client->window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + this->_sharedGameHUD(); this->_layoutGameEscMenu(); break; case GUIState::LOBBY_BROWSER: @@ -147,6 +149,7 @@ void GUI::layoutFrame(GUIState state) { break; case GUIState::GAME_HUD: glfwSetInputMode(client->window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + this->_sharedGameHUD(); this->_layoutGameHUD(); break; case GUIState::LOBBY: @@ -360,6 +363,65 @@ void GUI::_layoutLobby() { this->addWidget(std::move(waiting_msg)); } +void GUI::_sharedGameHUD() { + auto self_eid = client->session->getInfo().client_eid; + if (!self_eid.has_value()) { + return; + } + + auto self = client->gameState.objects.at(*self_eid); + auto inventory_size = self->inventoryInfo->inventory_size; + + // Flexbox for the items + // Loading itemframe again if no item + auto itemflex = widget::Flexbox::make( + glm::vec2(0.0f, 0.0f), + glm::vec2(WINDOW_WIDTH, 0.0f), + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) + ); + for (int i = 1; i <= inventory_size; i++) { + if (self->inventoryInfo->inventory.contains(i)) { + switch (self->inventoryInfo->inventory.at(i)) { + case ModelType::HealthPotion: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::HealthPotion))); + break; + } + case ModelType::NauseaPotion: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::NauseaPotion))); + break; + } + case ModelType::InvisibilityPotion: { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::InvisPotion))); + break; + } + } + } + else { + itemflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemFrame))); + } + } + + this->addWidget(std::move(itemflex)); + + // Flexbox for the item frames + auto frameflex = widget::Flexbox::make( + glm::vec2(0.0f, 0.0f), //position relative to screen + glm::vec2(WINDOW_WIDTH, 0.0f), //dimensions of the flexbox + widget::Flexbox::Options(widget::Dir::HORIZONTAL, widget::Align::CENTER, 0.0f) //last one is padding + ); + + for (int i = 1; i <= inventory_size; i++) { + if (self->inventoryInfo->selected == i) { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::SelectedFrame))); + } + else { + frameflex->push(widget::StaticImg::make(glm::vec2(0.0f), images.getImg(img::ImgID::ItemFrame))); + } + } + + this->addWidget(std::move(frameflex)); +} + void GUI::_layoutGameHUD() { auto self_eid = client->session->getInfo().client_eid; if (!self_eid.has_value()) { @@ -374,7 +436,7 @@ void GUI::_layoutGameHUD() { font::Size::MEDIUM, font::Color::RED, fonts, - font::getRelativePixels(10) + font::getRelativePixels(70) ); this->addWidget(std::move(health_txt)); } diff --git a/src/client/gui/imgs/img.cpp b/src/client/gui/imgs/img.cpp index f153eaa2..425a0360 100644 --- a/src/client/gui/imgs/img.cpp +++ b/src/client/gui/imgs/img.cpp @@ -10,7 +10,11 @@ std::string getImgFilepath(ImgID img) { default: case ImgID::Yoshi: return (img_root / "Yoshi.png").string(); case ImgID::AwesomeSauce: return (img_root / "awesomeface.png").string(); - + case ImgID::ItemFrame: return (img_root / "frame.png").string(); + case ImgID::SelectedFrame: return (img_root / "selected_frame.png").string(); + case ImgID::HealthPotion: return (img_root / "pot_health.png").string(); + case ImgID::NauseaPotion: return (img_root / "pot_nausea.png").string(); + case ImgID::InvisPotion: return (img_root / "pot_invisibility.png").string(); } } diff --git a/src/client/gui/widget/staticimg.cpp b/src/client/gui/widget/staticimg.cpp index 0ac80959..3dc3fc3b 100644 --- a/src/client/gui/widget/staticimg.cpp +++ b/src/client/gui/widget/staticimg.cpp @@ -66,6 +66,7 @@ void StaticImg::render() { glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); + glDisable(GL_CULL_FACE); StaticImg::shader->use(); // glm::mat4 projection = GUI_PROJECTION_MATRIX(); @@ -83,6 +84,8 @@ void StaticImg::render() { glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); glUseProgram(0); + + glEnable(GL_CULL_FACE); } } diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index de09191d..c3002ffe 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -17,6 +17,7 @@ set(FILES game/grid.cpp game/trap.cpp game/spiketrap.cpp + game/potion.cpp ) add_library(${LIB_NAME} STATIC ${FILES}) diff --git a/src/server/game/gridcell.cpp b/src/server/game/gridcell.cpp index 22a3ad70..cf94f12f 100644 --- a/src/server/game/gridcell.cpp +++ b/src/server/game/gridcell.cpp @@ -15,6 +15,12 @@ CellType charToCellType(char c) { return CellType::Enemy; case 'X': return CellType::SpikeTrap; + case 'h': + return CellType::HealthPotion; + case 'n': + return CellType::NauseaPotion; + case 'i': + return CellType::InvisibilityPotion; default: return CellType::Unknown; } diff --git a/src/server/game/item.cpp b/src/server/game/item.cpp index f9589a81..1d510715 100644 --- a/src/server/game/item.cpp +++ b/src/server/game/item.cpp @@ -1,15 +1,33 @@ #include "server/game/item.hpp" #include "shared/game/sharedobject.hpp" +#include "server/game/servergamestate.hpp" +#include "server/game/objectmanager.hpp" /* Constructors and Destructors */ -// TODO: actually make the item constructor take params -// and fill in these values (probably on ted's branch, hi ted!) -Item::Item(): - Object(ObjectType::Item, Physics(false, Collider::Box, glm::vec3(0.0f), glm::vec3(0.0f)), ModelType::Cube) { // cppcheck-suppress uninitMemberVar +Item::Item(ObjectType type, bool movable, glm::vec3 corner, ModelType model, glm::vec3 dimensions): + Object(type, Physics(movable, Collider::Box, corner, dimensions), ModelType::Cube), + iteminfo(SharedItemInfo{ .held = false, .used = false }) +{} + +void Item::useItem(Object* other, ServerGameState& state) { } -Item::~Item() { +void Item::doCollision(Object* other, ServerGameState* state) { + + auto player = dynamic_cast(other); + if (player == nullptr) return; // only allow players to pick up items + if (player->inventory.size() < player->sharedInventory.inventory_size) { + for (int i = 1; i <= player->sharedInventory.inventory_size; i++) { + if (!player->inventory.contains(i)) { + player->inventory[i] = this->typeID; + player->sharedInventory.inventory[i] = this->modelType; + break; + } + } + this->iteminfo.held = true; + this->physics.collider = Collider::None; + } } /* SharedGameState generation */ diff --git a/src/server/game/objectmanager.cpp b/src/server/game/objectmanager.cpp index 5aa2a27b..f7ff67fa 100644 --- a/src/server/game/objectmanager.cpp +++ b/src/server/game/objectmanager.cpp @@ -1,6 +1,7 @@ #include "server/game/objectmanager.hpp" #include "server/game/enemy.hpp" #include "server/game/spiketrap.hpp" +#include "server/game/potion.hpp" #include @@ -30,8 +31,8 @@ SpecificID ObjectManager::createObject(Object* object) { case ObjectType::SpikeTrap: object->typeID = this->traps.push(dynamic_cast(object)); break; - case ObjectType::Item: - object->typeID = this->items.push(dynamic_cast(object)); + case ObjectType::Potion: + object->typeID = this->items.push(dynamic_cast(object)); break; case ObjectType::SolidSurface: object->typeID = this->solid_surfaces.push(dynamic_cast(object)); @@ -69,6 +70,9 @@ bool ObjectManager::removeObject(EntityID globalID) { // SmartVector this->base_objects.remove(object->typeID); break; + case ObjectType::Potion: + this->items.remove(object->typeID); + break; } // Delete object diff --git a/src/server/game/player.cpp b/src/server/game/player.cpp index c57543e2..50602525 100644 --- a/src/server/game/player.cpp +++ b/src/server/game/player.cpp @@ -1,19 +1,22 @@ #include "server/game/player.hpp" #include "shared/game/sharedobject.hpp" #include "shared/game/stat.hpp" +#include "server/game/constants.hpp" #include SharedObject Player::toShared() { auto so = Creature::toShared(); so.playerInfo = this->info; + so.inventoryInfo = this->sharedInventory; return so; } Player::Player(glm::vec3 corner, glm::vec3 facing): Creature(ObjectType::Player, corner, facing, ModelType::Player, SharedStats( - Stat(0, 100, 100), + Stat(0, 100, 50), Stat(0, 10, 5) - )) + )), + sharedInventory(SharedInventory { .selected = 1, .inventory_size = INVENTORY_SIZE }) { this->info.is_alive = true; this->info.respawn_time = NULL; diff --git a/src/server/game/potion.cpp b/src/server/game/potion.cpp new file mode 100644 index 00000000..e149583c --- /dev/null +++ b/src/server/game/potion.cpp @@ -0,0 +1,80 @@ +#pragma once + +#include "server/game/potion.hpp" +#include "server/game/constants.hpp" +#include "shared/game/sharedobject.hpp" +#include + +class Player; + +Potion::Potion(glm::vec3 corner, glm::vec3 dimensions, PotionType type): + Item(ObjectType::Potion, false, corner, ModelType::Cube, dimensions) +{ + this->duration = 0; + this->effectScalar = 0; + this->potType = type; + this->usedPlayer = nullptr; + + switch (type) { + case PotionType::Health: + this->duration = HEALTH_DURATION; + this->effectScalar = RESTORE_HEALTH; + this->modelType = ModelType::HealthPotion; + break; + case PotionType::Nausea: + this->duration = NAUSEA_DURATION; + this->effectScalar = NAUSEA_SCALAR; + this->modelType = ModelType::NauseaPotion; + break; + case PotionType::Invisibility: + this->duration = INVIS_DURATION; + this->effectScalar = INVIS_OPACITY; + this->modelType = ModelType::InvisibilityPotion; + break; + } +} + +void Potion::useItem(Object* other, ServerGameState& state) { + Player* player = dynamic_cast(other); + this->usedPlayer = player; + + this->used_time = std::chrono::system_clock::now(); + + switch (this->potType) { + case PotionType::Health: { + player->stats.health.adjustMod(this->effectScalar); + //state.objects.removeObject(this->globalID); + break; + } + case PotionType::Nausea: { + player->physics.nauseous = this->effectScalar; + break; + } + case PotionType::Invisibility: { + + break; + } + } + + this->iteminfo.used = true; + this->iteminfo.held = false; +} + +bool Potion::timeOut() { + auto now = std::chrono::system_clock::now(); + return (now - this->used_time) > std::chrono::seconds(this->duration); +} + +void Potion::revertEffect(ServerGameState& state) { + switch (this->potType) { + case PotionType::Nausea: { + this->usedPlayer->physics.nauseous = 1.0f; + break; + } + case PotionType::Invisibility: { + + break; + } + } + //state.objects.removeObject(this->globalID); +} \ No newline at end of file diff --git a/src/server/game/servergamestate.cpp b/src/server/game/servergamestate.cpp index a0e80ad3..d7b358e4 100644 --- a/src/server/game/servergamestate.cpp +++ b/src/server/game/servergamestate.cpp @@ -1,7 +1,9 @@ #include "server/game/servergamestate.hpp" #include "shared/game/sharedgamestate.hpp" #include "server/game/spiketrap.hpp" +#include "server/game/potion.hpp" #include "shared/utilities/root_path.hpp" +#include "server/game/constants.hpp" #include "shared/utilities/time.hpp" #include @@ -124,13 +126,48 @@ void ServerGameState::update(const EventList& events) { break; } - case EventType::MoveRelative: + case EventType::MoveRelative: { //currently just sets the velocity to given - auto moveRelativeEvent = boost::get(event.data); - Object* objMoveRel = this->objects.getObject(moveRelativeEvent.entity_to_move); - objMoveRel->physics.velocity += moveRelativeEvent.movement; - break; + auto moveRelativeEvent = boost::get(event.data); + Object* objMoveRel = this->objects.getObject(moveRelativeEvent.entity_to_move); + objMoveRel->physics.velocity += moveRelativeEvent.movement; + break; + + } + case EventType::SelectItem: + { + auto selectItemEvent = boost::get(event.data); + player->sharedInventory.selected = selectItemEvent.itemNum; + break; + } + case EventType::UseItem: + { + auto useItemEvent = boost::get(event.data); + int itemSelected = player->sharedInventory.selected; + + if (player->inventory.find(itemSelected) != player->inventory.end()) { + Item* item = this->objects.getItem(player->inventory.at(itemSelected)); + item->useItem(player, *this); + player->inventory.erase(itemSelected); + player->sharedInventory.inventory.erase(itemSelected); + //TODO : should also remove item afterwards + } + break; + } + case EventType::DropItem: + { + auto dropItemEvent = boost::get(event.data); + int itemSelected = player->sharedInventory.selected; + if (player->inventory.find(itemSelected) != player->inventory.end()) { + Item* item = this->objects.getItem(player->inventory.at(itemSelected)); + item->iteminfo.held = false; + item->physics.collider = Collider::Box; + item->physics.shared.corner = (player->physics.shared.corner + (player->physics.shared.facing * 4.0f)) * glm::vec3(1.0f, 0.0f, 1.0f); + player->inventory.erase(itemSelected); + player->sharedInventory.inventory.erase(itemSelected); + } + break; } // default: @@ -139,8 +176,8 @@ void ServerGameState::update(const EventList& events) { } // TODO: fill update() method with updating object movement - useItem(); updateMovement(); + updateItems(); updateTraps(); handleDeaths(); handleRespawns(); @@ -170,6 +207,8 @@ void ServerGameState::updateMovement() { // Check for collision at position to move, if so, dont change position // O(n^2) naive implementation of collision detection glm::vec3 movementStep = object->physics.velocity * object->physics.velocityMultiplier; + movementStep.x *= object->physics.nauseous; + movementStep.z *= object->physics.nauseous; // Run collision detection movement if it has a collider if (object->physics.collider != Collider::None) { @@ -187,6 +226,12 @@ void ServerGameState::updateMovement() { if (otherObj->physics.collider == Collider::None) { continue; } if (detectCollision(object->physics, otherObj->physics)) { + + // If item, resolve it here + if (otherObj->type == ObjectType::Potion) { + otherObj->doCollision(object, this); + continue; + } collided = true; // Check x-axis collision @@ -234,23 +279,25 @@ void ServerGameState::updateMovement() { // potentially need to make this unconditional further down object->physics.shared.corner.y = 0; } - - } } } -void ServerGameState::useItem() { - // Update whatever is necesssary for item - // This method may need to be broken down for different types - // of item types - - SmartVector items = this->objects.getItems(); +void ServerGameState::updateItems() { + auto items = this->objects.getItems(); for (int i = 0; i < items.size(); i++) { - const Item* item = items.get(i); - - if (item == nullptr) - continue; + auto item = items.get(i); + if (item == nullptr) { continue; } + + if (item->type == ObjectType::Potion) { + Potion* pot = dynamic_cast(item); + if (pot->iteminfo.used) { + if (pot->timeOut()) { + pot->revertEffect(*this); + } + } + } + } } @@ -461,6 +508,36 @@ void ServerGameState::loadMaze() { GridCell* cell = this->grid.getCell(col, row); switch (cell->type) { + case CellType::HealthPotion: { + glm::vec3 dimensions(1.0f); + + glm::vec3 corner(cell->x * this->grid.getGridCellWidth() + 1, + 0, + cell->y * this->grid.getGridCellWidth() + 1); + + this->objects.createObject(new Potion(corner, dimensions, PotionType::Health)); + break; + } + case CellType::NauseaPotion: { + glm::vec3 dimensions(1.0f); + + glm::vec3 corner(cell->x* this->grid.getGridCellWidth() + 1, + 0, + cell->y* this->grid.getGridCellWidth() + 1); + + this->objects.createObject(new Potion(corner, dimensions, PotionType::Nausea)); + break; + } + case CellType::InvisibilityPotion: { + glm::vec3 dimensions(1.0f); + + glm::vec3 corner(cell->x* this->grid.getGridCellWidth() + 1, + 0, + cell->y* this->grid.getGridCellWidth() + 1); + + this->objects.createObject(new Potion(corner, dimensions, PotionType::Invisibility)); + break; + } case CellType::SpikeTrap: { const float HEIGHT_SHOWING = 0.5; glm::vec3 dimensions( diff --git a/src/server/game/spell.cpp b/src/server/game/spell.cpp new file mode 100644 index 00000000..1fdf2dad --- /dev/null +++ b/src/server/game/spell.cpp @@ -0,0 +1,16 @@ +#pragma once + +#include "server/game/spell.hpp" +#include "server/game/servergamestate.hpp" +#include "server/game/objectmanager.hpp" +#include "server/game/object.hpp" + +Spell::Spell(glm::vec3 corner, glm::vec3 dimensions): + Item(ObjectType::Spell, false, corner, ModelType::Cube, dimensions) +{ + this->spellType = SpellType::Fireball; +} + +void Spell::setSpellType(SpellType type) { + this->spellType = type; +} \ No newline at end of file diff --git a/src/server/server.cpp b/src/server/server.cpp index e87f81df..1cd65591 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -14,6 +14,7 @@ #include #include "boost/variant/get.hpp" +#include "server/game/potion.hpp" #include "server/game/enemy.hpp" #include "server/game/player.hpp" #include "shared/game/event.hpp" diff --git a/src/shared/game/event.cpp b/src/shared/game/event.cpp index a71b451b..178d8bb2 100644 --- a/src/shared/game/event.cpp +++ b/src/shared/game/event.cpp @@ -21,6 +21,9 @@ std::ostream& operator<<(std::ostream& os, const EventType& type) { TO_STR(MoveRelative); TO_STR(MoveAbsolute); TO_STR(SpawnEntity); + TO_STR(SelectItem); + TO_STR(UseItem); + TO_STR(DropItem); default: os << "Unknown EventType"; break; diff --git a/src/shared/game/sharedobject.cpp b/src/shared/game/sharedobject.cpp index effbcb2d..48b5089c 100644 --- a/src/shared/game/sharedobject.cpp +++ b/src/shared/game/sharedobject.cpp @@ -4,8 +4,6 @@ std::string objectTypeString(ObjectType type) { switch (type) { case ObjectType::Object: return "Object"; - case ObjectType::Item: - return "Item"; case ObjectType::SolidSurface: return "SolidSurface"; case ObjectType::Player: