diff --git a/client/client_application.cpp b/client/client_application.cpp index 38bdf67..7009125 100644 --- a/client/client_application.cpp +++ b/client/client_application.cpp @@ -318,6 +318,7 @@ void ClientApplication::ProcessEvents() { #include "options_menu.hpp" #include "lobby_menu.hpp" #include "world.hpp" +#include "combat.hpp" #include "disconnected_screen.hpp" void ClientApplication::ProcessSceneSignal(SceneSignal signal) { @@ -341,6 +342,9 @@ void ClientApplication::ProcessSceneSignal(SceneSignal signal) { case SceneSignal::WORLD: activeScene = new World(&clientIndex, &accountIndex); break; + case SceneSignal::COMBAT: + activeScene = new Combat(&clientIndex, &accountIndex); + break; case SceneSignal::DISCONNECTEDSCREEN: activeScene = new DisconnectedScreen(); break; diff --git a/client/entities/base_barrier.cpp b/client/entities/base_barrier.cpp new file mode 100644 index 0000000..ae94f60 --- /dev/null +++ b/client/entities/base_barrier.cpp @@ -0,0 +1,53 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#include "base_barrier.hpp" + +#include "config_utility.hpp" + +#include + +void BaseBarrier::CorrectSprite() { + //TODO: (9) BaseBarrier::CorrectSprite() +} + +int BaseBarrier::SetStatus(int k, int v) { + if (k >= 8 || k < 0) { + return -1; + } + return status[k] = v; +} + +int BaseBarrier::FindStatus(int k) { + if (k >= 8 || k < 0) { + return -1; + } + return status[k]; +} + +int* BaseBarrier::SetStatusArray(int* ptr) { + memcpy(status, ptr, sizeof(int) * 8); + return status; +} + +int* BaseBarrier::GetStatusArray() { + return status; +} diff --git a/client/entities/base_barrier.hpp b/client/entities/base_barrier.hpp new file mode 100644 index 0000000..26a0146 --- /dev/null +++ b/client/entities/base_barrier.hpp @@ -0,0 +1,43 @@ +/* Copyright: (c) Kayne Ruse 2013-2016 + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. +*/ +#pragma once + +#include "entity.hpp" + +class BaseBarrier: public Entity { +public: + BaseBarrier() = default; + virtual ~BaseBarrier() = default; + + void CorrectSprite(); + + int SetStatus(int, int); + int FindStatus(int); + + int* SetStatusArray(int*); + int* GetStatusArray(); + + +protected: + //metadata + int status[8]; +}; diff --git a/client/scenes/world.cpp b/client/scenes/world.cpp index 9b086c0..3c5fa3f 100644 --- a/client/scenes/world.cpp +++ b/client/scenes/world.cpp @@ -424,6 +424,22 @@ void World::HandlePacket(SerialPacket* const argPacket) { hCreatureMovement(static_cast(argPacket)); break; + //barrier management + case SerialPacketType::BARRIER_UPDATE: + hBarrierUpdate(static_cast(argPacket)); + break; + + case SerialPacketType::BARRIER_CREATE: + hBarrierCreate(static_cast(argPacket)); + break; + case SerialPacketType::BARRIER_UNLOAD: + hBarrierUnload(static_cast(argPacket)); + break; + + case SerialPacketType::QUERY_BARRIER_EXISTS: + hQueryBarrierExists(static_cast(argPacket)); + break; + //chat case SerialPacketType::TEXT_BROADCAST: hTextBroadcast(static_cast(argPacket)); @@ -797,8 +813,6 @@ void World::hCreatureUpdate(CreaturePacket* const argPacket) { return; } - std::cout << "hCreatureUpdate" << std::endl; - //check if this creature exists std::map::iterator creatureIt = creatureMap.find(argPacket->creatureIndex); if (creatureIt != creatureMap.end()) { @@ -898,6 +912,94 @@ void World::hCreatureMovement(CreaturePacket* const argPacket) { creatureIt->second.SetMotion(argPacket->motion); } +//------------------------- +//barrier management +//------------------------- + +void World::hBarrierUpdate(BarrierPacket* const argPacket) { + //Cull barriers that are too far away + if ( (localCharacter->GetOrigin() - argPacket->origin).Length() > INFLUENCE_RADIUS) { + //ignore beyond 1000 units + return; + } + + //check if this barrier exists + std::map::iterator barrierIt = barrierMap.find(argPacket->barrierIndex); + if (barrierIt != barrierMap.end()) { + //update the origin and motion, if there's a difference + if (barrierIt->second.GetOrigin() != argPacket->origin) { + barrierIt->second.SetOrigin(argPacket->origin); + } + } + else { + hBarrierCreate(argPacket); + } +} + +void World::hBarrierCreate(BarrierPacket* const argPacket) { + //check for logic errors + if (barrierMap.find(argPacket->barrierIndex) != barrierMap.end()) { + std::ostringstream msg; + msg << "Double barrier creation event; "; + msg << "Index: " << argPacket->barrierIndex; + throw(std::runtime_error(msg.str())); + } + + //ignore barriers from other rooms + if (roomIndex != argPacket->roomIndex) { + //temporary error checking + std::ostringstream msg; + msg << "Barrier from the wrong room received: "; + msg << "barrierIndex: " << argPacket->barrierIndex << ", roomIndex: " << argPacket->roomIndex; + throw(std::runtime_error(msg.str())); + } + + //implicitly create the element + BaseBarrier* barrier = &barrierMap[argPacket->barrierIndex]; + + //fill the barrier's info + barrier->SetBounds(argPacket->bounds); + barrier->SetOrigin(argPacket->origin); + barrier->SetStatusArray(argPacket->status); + + //debug + std::cout << "Barrier Create, total: " << barrierMap.size() << std::endl; +} + +void World::hBarrierUnload(BarrierPacket* const argPacket) { + //ignore if this barrier doesn't exist + std::map::iterator barrierIt = barrierMap.find(argPacket->barrierIndex); + if (barrierIt == barrierMap.end()) { + return; + } + + //remove this barrier + barrierMap.erase(barrierIt); + + //debug + std::cout << "Barrier Unload, total: " << barrierMap.size() << std::endl; +} + +void World::hQueryBarrierExists(BarrierPacket* const argPacket) { + std::cout << "Barrier Query" << std::endl; + + //ignore barriers in a different room (sub-optimal) + if (argPacket->roomIndex != roomIndex) { + return; + } + + //implicitly create the element + BaseBarrier* barrier = &barrierMap[argPacket->barrierIndex]; + + //fill the barrier's info + barrier->SetBounds(argPacket->bounds); + barrier->SetOrigin(argPacket->origin); + barrier->SetStatusArray(argPacket->status); + + //debug + std::cout << "Barrier Query, total: " << barrierMap.size() << std::endl; +} + //------------------------- //chat //------------------------- diff --git a/client/scenes/world.hpp b/client/scenes/world.hpp index c8b21d0..ba0bfcb 100644 --- a/client/scenes/world.hpp +++ b/client/scenes/world.hpp @@ -40,6 +40,7 @@ //client #include "base_scene.hpp" +#include "base_barrier.hpp" #include "base_creature.hpp" #include "local_character.hpp" @@ -113,6 +114,12 @@ private: void hQueryCreatureExists(CreaturePacket* const); void hCreatureMovement(CreaturePacket* const); + //barrier management + void hBarrierUpdate(BarrierPacket* const); + void hBarrierCreate(BarrierPacket* const); + void hBarrierUnload(BarrierPacket* const); + void hQueryBarrierExists(BarrierPacket* const); + //chat //TODO: ui chat engine void hTextBroadcast(TextPacket* const); @@ -151,6 +158,7 @@ private: } camera; //entities + std::map barrierMap; std::map characterMap; std::map creatureMap; LocalCharacter* localCharacter = nullptr; diff --git a/common/network/serial_packet.hpp b/common/network/serial_packet.hpp index 9b23f20..ecf08cb 100644 --- a/common/network/serial_packet.hpp +++ b/common/network/serial_packet.hpp @@ -34,7 +34,7 @@ typedef SerialPacketBase SerialPacket; //DOCS: NETWORK_VERSION is used to discern compatible servers and clients -constexpr int NETWORK_VERSION = 20160321; +constexpr int NETWORK_VERSION = 20160404; union MaxPacket { CharacterPacket a; diff --git a/common/network/serial_packet_type.hpp b/common/network/serial_packet_type.hpp index 65171f5..69307a9 100644 --- a/common/network/serial_packet_type.hpp +++ b/common/network/serial_packet_type.hpp @@ -154,6 +154,11 @@ enum class SerialPacketType { BARRIER_UPDATE = 701, + BARRIER_CREATE = 702, + BARRIER_UNLOAD = 703, + + QUERY_BARRIER_EXISTS = 704, + FORMAT_END_COMBAT = 799, //------------------------- diff --git a/server/combat/barrier_data.cpp b/server/combat/barrier_data.cpp index d07662c..bd9212d 100644 --- a/server/combat/barrier_data.cpp +++ b/server/combat/barrier_data.cpp @@ -27,6 +27,7 @@ BarrierData::BarrierData(int i): Entity::Entity("barrier") { + instanceIndex = i; memcpy(status, 0, sizeof(int) * 8); } diff --git a/server/combat/barrier_data.hpp b/server/combat/barrier_data.hpp index b534906..c91cd9f 100644 --- a/server/combat/barrier_data.hpp +++ b/server/combat/barrier_data.hpp @@ -50,7 +50,7 @@ private: int scriptRef = LUA_NOREF; std::map tags; - int instanceIndex = -1; + int instanceIndex; int status[8]; }; \ No newline at end of file diff --git a/server/rooms/room_data.cpp b/server/rooms/room_data.cpp index 5e85b20..3602d4e 100644 --- a/server/rooms/room_data.cpp +++ b/server/rooms/room_data.cpp @@ -46,67 +46,27 @@ void RoomData::RunFrame() { lua_pop(lua, 1); } + //lists of non-character entities that need updating client-side + std::list> creatureList; + std::list> barrierList; + //update the entities in the room for (auto& it : characterList) { it->Update(); } + creatureMgr.Update(&creatureList); + barrierMgr.Update(&barrierList); - //build a list of characters for use with the triggers + //build a list of entities for use with the triggers std::stack entityStack; for (auto& it : characterList) { entityStack.push(it); } //Compare the triggers to the entities, using their real hitboxes - //NOTE: this stack solution should prevent problems when modifying the various lists - while(entityStack.size()) { - //get the entity & hitbox - Entity* entity = entityStack.top(); - BoundingBox entityBox = entity->GetBounds() + entity->GetOrigin(); + triggerMgr.Compare(entityStack); - //get the trigger pair & hitbox - for (auto& triggerPair : *triggerMgr.GetContainer()) { - BoundingBox triggerBox = triggerPair.second.GetBoundingBox() + triggerPair.second.GetOrigin(); - - //find all collisions - if (entityBox.CheckOverlap(triggerBox)) { - //skip members of the exclusion list - if (std::any_of(triggerPair.second.GetExclusionList()->begin(), triggerPair.second.GetExclusionList()->end(), [entity](Entity* ptr) -> bool { - return entity == ptr; - })) { - continue; - } - - //run the trigger script - lua_rawgeti(lua, LUA_REGISTRYINDEX, triggerPair.second.GetScriptReference()); - lua_pushlightuserdata(lua, entity); - - if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { - //error - throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); - } - - //push to the exclusion list - triggerPair.second.GetExclusionList()->push_back(entity); - } - else { - //remove members of the exclusion list - //NOTE: characters in different rooms won't be removed, but that shouldn't be a problem - triggerPair.second.GetExclusionList()->remove_if([entity](Entity* ptr) -> bool { - return entity == ptr; - }); - } - } - - //next - entityStack.pop(); - } - - //a list of creatures that need to be updated client-side - std::list< std::pair> creatureList; - creatureMgr.Update(&creatureList); - - //send the updates + //set the creature updates for (auto& it : creatureList) { CreaturePacket packet; copyCreatureToPacket(&packet, it.second, it.first); @@ -115,10 +75,6 @@ void RoomData::RunFrame() { pumpPacketProximity(reinterpret_cast(&packet), roomIndex, it.second->GetOrigin(), INFLUENCE_RADIUS); } - //a list of barriers that need to be updated client-side - std::list< std::pair> barrierList; - barrierMgr.Update(&barrierList); - //send the updates for (auto& it : barrierList) { BarrierPacket packet; @@ -128,7 +84,7 @@ void RoomData::RunFrame() { pumpPacketProximity(reinterpret_cast(&packet), roomIndex, it.second->GetOrigin(), INFLUENCE_RADIUS); } - //TODO: creature/character collisions + //TODO: (0) creature/character collisions } std::string RoomData::SetName(std::string s) { diff --git a/server/triggers/trigger_manager.cpp b/server/triggers/trigger_manager.cpp index f72aa03..dba1af6 100644 --- a/server/triggers/trigger_manager.cpp +++ b/server/triggers/trigger_manager.cpp @@ -29,6 +29,53 @@ TriggerManager::~TriggerManager() { UnloadAll(); } +//Compare the triggers to the entities, using their real hitboxes +void TriggerManager::Compare(std::stack entityStack) { + //NOTE: this stack solution should prevent problems when modifying the various lists + while(entityStack.size()) { + //get the entity & hitbox + Entity* entity = entityStack.top(); + BoundingBox entityBox = entity->GetBounds() + entity->GetOrigin(); + + //get the trigger pair & hitbox + for (auto& triggerPair : elementMap) { + BoundingBox triggerBox = triggerPair.second.GetBoundingBox() + triggerPair.second.GetOrigin(); + + //find all collisions + if (entityBox.CheckOverlap(triggerBox)) { + //skip members of the exclusion list + if (std::any_of(triggerPair.second.GetExclusionList()->begin(), triggerPair.second.GetExclusionList()->end(), [entity](Entity* ptr) -> bool { + return entity == ptr; + })) { + continue; + } + + //run the trigger script + lua_rawgeti(lua, LUA_REGISTRYINDEX, triggerPair.second.GetScriptReference()); + lua_pushlightuserdata(lua, entity); + + if (lua_pcall(lua, 1, 0, 0) != LUA_OK) { + //error + throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) )); + } + + //push to the exclusion list + triggerPair.second.GetExclusionList()->push_back(entity); + } + else { + //remove members of the exclusion list + //NOTE: characters in different rooms won't be removed, but that shouldn't be a problem + triggerPair.second.GetExclusionList()->remove_if([entity](Entity* ptr) -> bool { + return entity == ptr; + }); + } + } + + //next + entityStack.pop(); + } +} + int TriggerManager::Create(std::string handle) { //implicitly creates the element TriggerData& triggerData = elementMap[counter]; diff --git a/server/triggers/trigger_manager.hpp b/server/triggers/trigger_manager.hpp index 76c2059..c6010bb 100644 --- a/server/triggers/trigger_manager.hpp +++ b/server/triggers/trigger_manager.hpp @@ -29,6 +29,7 @@ #include #include +#include #include class TriggerManager { @@ -36,6 +37,8 @@ public: TriggerManager(); ~TriggerManager(); + void Compare(std::stack entityStack); + //common public methods int Create(std::string handle); //TODO: return the Trigger itself? void Unload(int uid);