Compare commits

..

2 Commits

Author SHA1 Message Date
Kayne Ruse a5757748cf That didn't go as well as I'd hoped 2015-01-21 04:20:31 +11:00
Kayne Ruse f4e4728ce3 Partial solution for collision problems 2015-01-21 03:06:35 +11:00
127 changed files with 1916 additions and 3247 deletions
-3
View File
@@ -9,9 +9,6 @@
Release/
Debug/
Out/
release/
debug/
out/
#Project generated files
*.db
+5 -7
View File
@@ -1,13 +1,12 @@
## Outline
Tortuga is a 2D MMORPG featuring permadeath, with an emphasis on multiplayer cooperation, exploration and customization. The game runs on customizable public and private servers.
Tortuga is a 2D multiplayer JRPG featuring permadeath, with an emphasis on multiplayer cooperation, exploration and customization. The game runs on customizable public and private servers.
This game is inspired by classic 2D RPGs (Final Fantasy, The Legend of Zelda), as well as more modern sandboxes and MMOs (Minecraft, EVE Online). This project is currently independently created and funded, with the goal of creating a game that will engage the players and inspire a large community.
This game is inspired by classic 2D RPGs (Final Fantasy, The Legend of Zelda), as well as more modern sandboxes amd MMOs (Minecraft, EVE Online). This project is currently independently created and funded, with the goal of creating a game that will engage the players and inspire a large community.
## Releases
* The most recent stable build for Windows can be found [here](https://dl.dropboxusercontent.com/u/46669050/Tortuga-win.rar).
* The most recent stable build for Linux can be found [here](https://dl.dropboxusercontent.com/u/46669050/Tortuga-linux.tar).
The most recent stable build for Windows can be found [here](https://dl.dropboxusercontent.com/u/46669050/Tortuga.rar).
## Documentation
@@ -17,14 +16,13 @@ This game is inspired by classic 2D RPGs (Final Fantasy, The Legend of Zelda), a
## External Dependencies
* [SDL 1.2](http://www.libsdl.org/) - Simple DirectMedia Layer API
* [SDL_net 2.0](http://www.libsdl.org/projects/SDL_net/) - SDL's networking extension
* [SDL_net 1.2](http://www.libsdl.org/projects/SDL_net/) - SDL's networking extension
* [lua 5.2](http://www.lua.org/) - The lua programming language
* [SQLite3](http://www.sqlite.org/) - A lightweight SQL database engine
## Tools
* [WinRAR](http://www.rarlab.com/) - The best compression tool available IMO; only needed for Windows distribution
* [Dropbox](https://www.dropbox.com/) - For hosting and distribution
* [WinRAR](http://www.rarlab.com/) - The best compression tool available IMO; only needed for distribution
## Copyright
View File
+1 -1
View File
@@ -127,7 +127,7 @@ void BaseScene::HandleEvents() {
break;
#ifdef USE_EVENT_JOYSTICK
//EMPTY
//TODO: joystick/gamepad support
#endif
#ifdef USE_EVENT_UNKNOWN
+1 -1
View File
@@ -59,7 +59,7 @@ protected:
virtual void KeyUp(SDL_KeyboardEvent const&) {}
#ifdef USE_EVENT_JOYSTICK
//EMPTY
//TODO: joystick/gamepad support
#endif
#ifdef USE_EVENT_UNKNOWN
+5 -8
View File
@@ -27,7 +27,6 @@
#include <stdexcept>
#include <chrono>
#include <iostream>
#include <sstream>
//-------------------------
//Scene headers
@@ -38,7 +37,7 @@
#include "main_menu.hpp"
#include "options_menu.hpp"
#include "lobby_menu.hpp"
#include "world.hpp"
#include "in_world.hpp"
#include "disconnected_screen.hpp"
//-------------------------
@@ -58,9 +57,7 @@ void ClientApplication::Init(int argc, char* argv[]) {
//initialize SDL
if (SDL_Init(SDL_INIT_VIDEO)) {
std::ostringstream os;
os << "Failed to initialize SDL: " << SDL_GetError();
throw(std::runtime_error(os.str()));
throw(std::runtime_error("Failed to initialize SDL"));
}
std::cout << "Initialized SDL" << std::endl;
@@ -86,6 +83,7 @@ void ClientApplication::Init(int argc, char* argv[]) {
//debug output
//-------------------------
//TODO: enable/disable these with a switch
#define DEBUG_OUTPUT_VAR(x) std::cout << "\t" << #x << ": " << x << std::endl;
std::cout << "Internal sizes:" << std::endl;
@@ -172,7 +170,6 @@ void ClientApplication::Quit() {
//-------------------------
void ClientApplication::LoadScene(SceneList sceneIndex) {
//BUG: #16 Resources are being reloaded between scenes
UnloadScene();
switch(sceneIndex) {
//add scene creation calls here
@@ -189,8 +186,8 @@ void ClientApplication::LoadScene(SceneList sceneIndex) {
case SceneList::LOBBYMENU:
activeScene = new LobbyMenu(&clientIndex, &accountIndex);
break;
case SceneList::WORLD:
activeScene = new World(&clientIndex, &accountIndex);
case SceneList::INWORLD:
activeScene = new InWorld(&clientIndex, &accountIndex);
break;
case SceneList::DISCONNECTEDSCREEN:
activeScene = new DisconnectedScreen();
@@ -22,7 +22,7 @@
#ifndef IPOPERATORS_HPP_
#define IPOPERATORS_HPP_
#include "SDL_net.h"
#include "SDL/SDL_net.h"
//these should've come standard
bool operator==(IPaddress lhs, IPaddress rhs);
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
-23
View File
@@ -21,26 +21,3 @@
*/
#include "base_monster.hpp"
#include "config_utility.hpp"
void BaseMonster::CorrectSprite() {
//TODO: (9) BaseMonster::CorrectSprite()
}
std::string BaseMonster::SetHandle(std::string s) {
return handle = s;
}
std::string BaseMonster::GetHandle() const {
return handle;
}
std::string BaseMonster::SetAvatar(std::string s) {
avatar = s;
sprite.LoadSurface(ConfigUtility::GetSingleton()["dir.sprites"] + avatar, 4, 1);
return avatar;
}
std::string BaseMonster::GetAvatar() const {
return avatar;
}
+1 -10
View File
@@ -29,17 +29,8 @@ public:
BaseMonster() = default;
virtual ~BaseMonster() = default;
void CorrectSprite();
std::string SetHandle(std::string s);
std::string GetHandle() const;
std::string SetAvatar(std::string s);
std::string GetAvatar() const;
protected:
//metadata
std::string handle;
std::string avatar;
//
};
#endif
+47 -6
View File
@@ -23,13 +23,54 @@
#include <iostream>
bool LocalCharacter::ProcessCollisionGrid(std::list<BoundingBox> boxList) {
bool LocalCharacter::ProcessCollisionGrid(std::list<BoundingBox> boxList, Uint8* keyState) {
//skip this if there's no movement
if (motion == 0) {
return false;
}
//determine the simple movement based on input
Vector2 newMotion = {0, 0};
if (keyState[SDLK_w]) {
newMotion.y -= CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_a]) {
newMotion.x -= CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_s]) {
newMotion.y += CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_d]) {
newMotion.x += CHARACTER_WALKING_SPEED;
}
bool ret = false;
for(auto& box : boxList) {
if (box.CheckOverlap(origin + bounds)) {
origin -= motion;
motion = {0, 0};
return true;
if (box.CheckCollision(origin + bounds)) {
//push the character to the closest non-contact position
Vector2 shift = box.CalcShift(origin + bounds);
origin += shift;
//set any motion in that direction to zero
if (shift.x != 0) {
newMotion.x = 0;
}
if (shift.y != 0) {
newMotion.y = 0;
}
ret = true;
}
}
return false;
//handle diagonals
if (newMotion.x != 0 && newMotion.y != 0) {
newMotion *= CHARACTER_WALKING_MOD;
}
//set the new motion
motion = newMotion;
//signal for updates
return ret;
}
+1 -1
View File
@@ -33,7 +33,7 @@ public:
LocalCharacter() = default;
virtual ~LocalCharacter() = default;
bool ProcessCollisionGrid(std::list<BoundingBox>);
bool ProcessCollisionGrid(std::list<BoundingBox>, Uint8* keyState);
private:
//NOTE: NO MEMBERS
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
@@ -49,11 +49,11 @@
#include <chrono>
class World: public BaseScene {
class InWorld : public BaseScene {
public:
//Public access members
World(int* const argClientIndex, int* const argAccountIndex);
~World();
InWorld(int* const argClientIndex, int* const argAccountIndex);
~InWorld();
protected:
//Frame loop
@@ -71,57 +71,43 @@ protected:
void KeyDown(SDL_KeyboardEvent const&);
void KeyUp(SDL_KeyboardEvent const&);
//handle incoming traffic
//Basic connections
void HandlePacket(SerialPacket* const);
void HandlePing(ServerPacket* const);
void HandlePong(ServerPacket* const);
//heartbeat system
void hPing(ServerPacket* const);
void hPong(ServerPacket* const);
//Connection control
void SendLogoutRequest();
void SendDisconnectRequest();
void SendShutdownRequest();
void HandleLogoutResponse(ClientPacket* const);
void HandleDisconnectResponse(ClientPacket* const);
void HandleDisconnectForced(ClientPacket* const);
void CheckHeartBeat();
//basic connections
void SendLogoutRequest();
void SendDisconnectRequest();
void SendAdminDisconnectForced();
void SendAdminShutdownRequest();
void hLogoutResponse(ClientPacket* const);
void hDisconnectResponse(ClientPacket* const);
void hAdminDisconnectForced(ClientPacket* const);
//map management
void SendRegionRequest(int roomIndex, int x, int y);
void hRegionContent(RegionPacket* const);
void HandleRegionContent(RegionPacket* const);
void UpdateMap();
//character management
void hCharacterUpdate(CharacterPacket* const);
void hCharacterCreate(CharacterPacket* const);
void hCharacterDelete(CharacterPacket* const);
void hQueryCharacterExists(CharacterPacket* const);
void hQueryCharacterStats(CharacterPacket* const);
void hQueryCharacterLocation(CharacterPacket* const);
void hCharacterMovement(CharacterPacket* const);
void hCharacterAttack(CharacterPacket* const);
void hCharacterDamage(CharacterPacket* const);
void HandleCharacterCreate(CharacterPacket* const);
void HandleCharacterDelete(CharacterPacket* const);
void HandleCharacterQueryExists(CharacterPacket* const);
void HandleCharacterMovement(CharacterPacket* const);
void HandleCharacterAttack(CharacterPacket* const);
//monster management
void hMonsterCreate(MonsterPacket* const);
void hMonsterDelete(MonsterPacket* const);
void hQueryMonsterExists(MonsterPacket* const);
void hQueryMonsterStats(MonsterPacket* const);
void hQueryMonsterLocation(MonsterPacket* const);
void hMonsterMovement(MonsterPacket* const);
void hMonsterAttack(MonsterPacket* const);
void hMonsterDamage(MonsterPacket* const);
void HandleMonsterCreate(MonsterPacket* const);
void HandleMonsterDelete(MonsterPacket* const);
void HandleMonsterQueryExists(MonsterPacket* const);
void HandleMonsterMovement(MonsterPacket* const);
void HandleMonsterAttack(MonsterPacket* const);
//chat
void hTextBroadcast(TextPacket* const);
void hTextSpeech(TextPacket* const);
void hTextWhisper(TextPacket* const);
//general gameplay
//player movement
void ProcessLocalCharacterMovement();
void SendLocalCharacterMovement();
std::list<BoundingBox> GenerateCollisionGrid(Entity*, int tileWidth, int tileHeight);
@@ -157,7 +143,7 @@ protected:
LocalCharacter* localCharacter = nullptr;
//heartbeat
//TODO: (2) Heartbeat needs it's own utility
//TODO: Heartbeat needs it's own utility
typedef std::chrono::steady_clock Clock;
Clock::time_point lastBeat = Clock::now();
int attemptedBeats = 0;
@@ -165,6 +151,7 @@ protected:
//ugly references; I hate this
ConfigUtility& config = ConfigUtility::GetSingleton();
UDPNetworkUtility& network = UDPNetworkUtility::GetSingleton();
Uint8* keyState = nullptr;
};
#endif
@@ -19,11 +19,10 @@
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#include "world.hpp"
#include "in_world.hpp"
#include "channels.hpp"
#include <cstring>
#include <iostream>
#include <sstream>
#include <stdexcept>
@@ -36,25 +35,7 @@
//DOCS: new characters will result in create messages
//DOCS: this client's character will exist in both (skipped)
void World::hCharacterUpdate(CharacterPacket* const argPacket) {
//TODO: (1) Authentication
//NOTE: applies to the local character too
//check that this character exists
std::map<int, BaseCharacter>::iterator characterIt = characterMap.find(argPacket->characterIndex);
if (characterIt != characterMap.end()) {
//update the origin and motion, if there's a difference
if (characterIt->second.GetOrigin() != argPacket->origin) {
characterIt->second.SetOrigin(argPacket->origin);
}
if (characterIt->second.GetMotion() != argPacket->motion) {
characterIt->second.SetMotion(argPacket->motion);
characterIt->second.CorrectSprite(); //only correct the sprite if the motion changes
}
}
}
void World::hCharacterCreate(CharacterPacket* const argPacket) {
void InWorld::HandleCharacterCreate(CharacterPacket* const argPacket) {
//prevent double message
if (characterMap.find(argPacket->characterIndex) != characterMap.end()) {
std::ostringstream msg;
@@ -68,42 +49,32 @@ void World::hCharacterCreate(CharacterPacket* const argPacket) {
BaseCharacter* character = &characterMap[argPacket->characterIndex];
//fill the character's info
character->SetOrigin(argPacket->origin);
character->SetMotion(argPacket->motion);
character->SetBounds({CHARACTER_BOUNDS_X, CHARACTER_BOUNDS_Y, CHARACTER_BOUNDS_WIDTH, CHARACTER_BOUNDS_HEIGHT});
character->SetHandle(argPacket->handle);
character->SetAvatar(argPacket->avatar);
character->SetOwner(argPacket->accountIndex);
character->SetOrigin(argPacket->origin);
character->SetMotion(argPacket->motion);
character->SetBounds(argPacket->bounds);
character->CorrectSprite();
//check for this player's character
if (character->GetOwner() == accountIndex) {
localCharacter = static_cast<LocalCharacter*>(character);
//focus the camera on this character's sprite
//focus the camera on this character
camera.marginX = (camera.width / 2 - localCharacter->GetSprite()->GetImage()->GetClipW() / 2);
camera.marginY = (camera.height/ 2 - localCharacter->GetSprite()->GetImage()->GetClipH() / 2);
//focus on this character's info
characterIndex = argPacket->characterIndex;
roomIndex = argPacket->roomIndex;
//query the world state (room)
CharacterPacket newPacket;
memset(&newPacket, 0, MAX_PACKET_SIZE);
newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS;
newPacket.roomIndex = roomIndex;
network.SendTo(Channels::SERVER, &newPacket);
newPacket.type = SerialPacketType::QUERY_MONSTER_EXISTS;
network.SendTo(Channels::SERVER, &newPacket);
}
//debug
std::cout << "Character Create, total: " << characterMap.size() << std::endl;
std::cout << "Create, total: " << characterMap.size() << std::endl;
}
void World::hCharacterDelete(CharacterPacket* const argPacket) {
void InWorld::HandleCharacterDelete(CharacterPacket* const argPacket) {
//ignore if this character doesn't exist
std::map<int, BaseCharacter>::iterator characterIt = characterMap.find(argPacket->characterIndex);
if (characterIt == characterMap.end()) {
@@ -120,24 +91,20 @@ void World::hCharacterDelete(CharacterPacket* const argPacket) {
//clear the room
roomIndex = -1;
regionPager.UnloadAll();
characterMap.clear();
monsterMap.clear();
}
else {
//remove this character
characterMap.erase(characterIt);
}
//remove this character
characterMap.erase(characterIt);
//debug
std::cout << "Character Delete, total: " << characterMap.size() << std::endl;
std::cout << "Delete, total: " << characterMap.size() << std::endl;
}
void World::hQueryCharacterExists(CharacterPacket* const argPacket) {
void InWorld::HandleCharacterQueryExists(CharacterPacket* const argPacket) {
//prevent a double message about this player's character
// if (argPacket->accountIndex == accountIndex) {
// return;
// }
if (argPacket->accountIndex == accountIndex) {
return;
}
//ignore characters in a different room (sub-optimal)
if (argPacket->roomIndex != roomIndex) {
@@ -157,19 +124,11 @@ void World::hQueryCharacterExists(CharacterPacket* const argPacket) {
character->CorrectSprite();
//debug
std::cout << "Character Query, total: " << characterMap.size() << std::endl;
std::cout << "Query, total: " << characterMap.size() << std::endl;
}
void World::hQueryCharacterStats(CharacterPacket* const argPacket) {
//TODO: (9) World::hQueryCharacterStats()
}
void World::hQueryCharacterLocation(CharacterPacket* const argPacket) {
//TODO: (9) World::hQueryCharacterLocation()
}
void World::hCharacterMovement(CharacterPacket* const argPacket) {
//TODO: (1) Authentication
void InWorld::HandleCharacterMovement(CharacterPacket* const argPacket) {
//TODO: Authentication
if (argPacket->characterIndex == characterIndex) {
return;
}
@@ -184,19 +143,72 @@ void World::hCharacterMovement(CharacterPacket* const argPacket) {
}
}
void World::hCharacterAttack(CharacterPacket* const argPacket) {
//TODO: (9) World::hCharacterAttack()
}
void World::hCharacterDamage(CharacterPacket* const argPacket) {
//TODO: (9) World::hCharacterDamage()
void InWorld::HandleCharacterAttack(CharacterPacket* const argPacket) {
//TODO: attack animation
}
//-------------------------
//player movement & collision
//monster management
//-------------------------
void World::SendLocalCharacterMovement() {
void InWorld::HandleMonsterCreate(MonsterPacket* const argPacket) {
//TODO
}
void InWorld::HandleMonsterDelete(MonsterPacket* const argPacket) {
//TODO
}
void InWorld::HandleMonsterQueryExists(MonsterPacket* const argPacket) {
//TODO
}
void InWorld::HandleMonsterMovement(MonsterPacket* const argPacket) {
//TODO
}
void InWorld::HandleMonsterAttack(MonsterPacket* const argPacket) {
//TODO
}
//-------------------------
//player movement
//-------------------------
void InWorld::ProcessLocalCharacterMovement() {
//character movement
if (!localCharacter) {
return;
}
Vector2 newMotion = {0, 0};
if (keyState[SDLK_w]) {
newMotion.y -= CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_a]) {
newMotion.x -= CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_s]) {
newMotion.y += CHARACTER_WALKING_SPEED;
}
if (keyState[SDLK_d]) {
newMotion.x += CHARACTER_WALKING_SPEED;
}
//handle diagonals
if (newMotion.x != 0 && newMotion.y != 0) {
newMotion *= CHARACTER_WALKING_MOD;
}
//set the info
if (localCharacter->GetMotion() != newMotion) {
localCharacter->SetMotion(newMotion);
localCharacter->CorrectSprite();
SendLocalCharacterMovement();
}
}
void InWorld::SendLocalCharacterMovement() {
CharacterPacket newPacket;
newPacket.type = SerialPacketType::CHARACTER_MOVEMENT;
@@ -209,7 +221,7 @@ void World::SendLocalCharacterMovement() {
network.SendTo(Channels::SERVER, &newPacket);
}
std::list<BoundingBox> World::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) {
std::list<BoundingBox> InWorld::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) {
//prepare for collisions
BoundingBox wallBounds = {0, 0, tileWidth, tileHeight};
std::list<BoundingBox> boxList;
@@ -0,0 +1,264 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "in_world.hpp"
#include "channels.hpp"
#include "ip_operators.hpp"
#include "terminal_error.hpp"
#include <chrono>
#include <sstream>
#include <stdexcept>
//-------------------------
//Basic connections
//-------------------------
void InWorld::HandlePacket(SerialPacket* const argPacket) {
switch(argPacket->type) {
//heartbeat system
case SerialPacketType::PING:
HandlePing(static_cast<ServerPacket*>(argPacket));
break;
case SerialPacketType::PONG:
HandlePong(static_cast<ServerPacket*>(argPacket));
break;
//game server connections
case SerialPacketType::LOGOUT_RESPONSE:
HandleLogoutResponse(static_cast<ClientPacket*>(argPacket));
break;
case SerialPacketType::DISCONNECT_RESPONSE:
HandleDisconnectResponse(static_cast<ClientPacket*>(argPacket));
break;
case SerialPacketType::DISCONNECT_FORCED:
HandleDisconnectForced(static_cast<ClientPacket*>(argPacket));
break;
//map management
case SerialPacketType::REGION_CONTENT:
HandleRegionContent(static_cast<RegionPacket*>(argPacket));
break;
//character management
case SerialPacketType::CHARACTER_CREATE:
HandleCharacterCreate(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_DELETE:
HandleCharacterDelete(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_CHARACTER_EXISTS:
HandleCharacterQueryExists(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_MOVEMENT:
HandleCharacterMovement(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_ATTACK:
HandleCharacterAttack(static_cast<CharacterPacket*>(argPacket));
break;
//monster management
case SerialPacketType::MONSTER_CREATE:
HandleMonsterCreate(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_DELETE:
HandleMonsterDelete(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_MONSTER_EXISTS:
HandleMonsterQueryExists(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_MOVEMENT:
HandleMonsterMovement(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_ATTACK:
HandleMonsterAttack(static_cast<MonsterPacket*>(argPacket));
break;
//rejection messages
case SerialPacketType::REGION_REJECTION:
case SerialPacketType::CHARACTER_REJECTION:
throw(terminal_error(static_cast<TextPacket*>(argPacket)->text));
break;
case SerialPacketType::SHUTDOWN_REJECTION:
throw(std::runtime_error(static_cast<TextPacket*>(argPacket)->text));
break;
//errors
default: {
std::ostringstream msg;
msg << "Unknown SerialPacketType encountered in InWorld: " << static_cast<int>(argPacket->type);
throw(std::runtime_error(msg.str()));
}
break;
}
}
void InWorld::HandlePing(ServerPacket* const argPacket) {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PONG;
network.SendTo(argPacket->srcAddress, &newPacket);
}
void InWorld::HandlePong(ServerPacket* const argPacket) {
if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) {
throw(std::runtime_error("Heartbeat message received from an unknown source"));
}
attemptedBeats = 0;
lastBeat = Clock::now();
}
//-------------------------
//Connection control
//-------------------------
void InWorld::SendLogoutRequest() {
ClientPacket newPacket;
//send a logout request
newPacket.type = SerialPacketType::LOGOUT_REQUEST;
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void InWorld::SendDisconnectRequest() {
ClientPacket newPacket;
//send a disconnect request
newPacket.type = SerialPacketType::DISCONNECT_REQUEST;
newPacket.clientIndex = clientIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void InWorld::SendShutdownRequest() {
ClientPacket newPacket;
//send a shutdown request
newPacket.type = SerialPacketType::SHUTDOWN_REQUEST;
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void InWorld::HandleLogoutResponse(ClientPacket* const argPacket) {
if (localCharacter) {
characterMap.erase(characterIndex);
localCharacter = nullptr;
}
accountIndex = -1;
characterIndex = -1;
//reset the camera
camera.marginX = camera.marginY = 0;
//because, why not? I guess...
SendDisconnectRequest();
}
void InWorld::HandleDisconnectResponse(ClientPacket* const argPacket) {
HandleLogoutResponse(argPacket);//shortcut
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have successfully logged out";
}
void InWorld::HandleDisconnectForced(ClientPacket* const argPacket) {
HandleDisconnectResponse(argPacket);//shortcut
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been forcibly disconnected by the server";
}
void InWorld::CheckHeartBeat() {
//check the connection (heartbeat)
if (Clock::now() - lastBeat > std::chrono::seconds(3)) {
if (attemptedBeats > 2) {
//escape to the disconnect screen
SendDisconnectRequest();
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server";
}
else {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PING;
network.SendTo(Channels::SERVER, &newPacket);
attemptedBeats++;
lastBeat = Clock::now();
}
}
}
//-------------------------
//map management
//-------------------------
void InWorld::SendRegionRequest(int roomIndex, int x, int y) {
RegionPacket packet;
//pack the region's data
packet.type = SerialPacketType::REGION_REQUEST;
packet.roomIndex = roomIndex;
packet.x = x;
packet.y = y;
network.SendTo(Channels::SERVER, &packet);
}
void InWorld::HandleRegionContent(RegionPacket* const argPacket) {
//replace existing regions
regionPager.UnloadIf([&](Region const& region) -> bool {
return region.GetX() == argPacket->x && region.GetY() == argPacket->y;
});
regionPager.PushRegion(argPacket->region);
//clean up after the serial code
delete argPacket->region;
argPacket->region = nullptr;
}
void InWorld::UpdateMap() {
if (roomIndex == -1) {
return;
}
//these represent the zone of regions that the client needs loaded, including the mandatory buffers (+1/-1)
int xStart = snapToBase(REGION_WIDTH, camera.x/tileSheet.GetTileW()) - REGION_WIDTH;
int xEnd = snapToBase(REGION_WIDTH, (camera.x+camera.width)/tileSheet.GetTileW()) + REGION_WIDTH;
int yStart = snapToBase(REGION_HEIGHT, camera.y/tileSheet.GetTileH()) - REGION_HEIGHT;
int yEnd = snapToBase(REGION_HEIGHT, (camera.y+camera.height)/tileSheet.GetTileH()) + REGION_HEIGHT;
//prune distant regions
regionPager.GetContainer()->remove_if([&](Region const& region) -> bool {
return region.GetX() < xStart || region.GetX() > xEnd || region.GetY() < yStart || region.GetY() > yEnd;
});
//request empty regions within this zone
for (int i = xStart; i <= xEnd; i += REGION_WIDTH) {
for (int j = yStart; j <= yEnd; j += REGION_HEIGHT) {
if (!regionPager.FindRegion(i, j)) {
SendRegionRequest(roomIndex, i, j);
}
}
}
}
+253
View File
@@ -0,0 +1,253 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "in_world.hpp"
#include "channels.hpp"
#include "terminal_error.hpp"
#include <stdexcept>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <sstream>
//-------------------------
//Public access members
//-------------------------
InWorld::InWorld(int* const argClientIndex, int* const argAccountIndex):
clientIndex(*argClientIndex),
accountIndex(*argAccountIndex),
keyState(SDL_GetKeyState(nullptr))
{
//setup the utility objects
buttonImage.LoadSurface(config["dir.interface"] + "button_menu.bmp");
buttonImage.SetClipH(buttonImage.GetClipH()/3);
font.LoadSurface(config["dir.fonts"] + "pk_white_8.bmp");
//pass the utility objects
disconnectButton.SetImage(&buttonImage);
disconnectButton.SetFont(&font);
shutDownButton.SetImage(&buttonImage);
shutDownButton.SetFont(&font);
//set the button positions
disconnectButton.SetX(50);
disconnectButton.SetY(50 + buttonImage.GetClipH() * 0);
shutDownButton.SetX(50);
shutDownButton.SetY(50 + buttonImage.GetClipH() * 1);
//set the button texts
disconnectButton.SetText("Disconnect");
shutDownButton.SetText("Shut Down");
//load the tilesheet
//TODO: add the tilesheet to the map system
//TODO: Tile size and tile sheet should be loaded elsewhere
tileSheet.Load(config["dir.tilesets"] + "overworld.bmp", 32, 32);
//Send the character data
//TODO: login scene, prompt, etc.
CharacterPacket newPacket;
newPacket.type = SerialPacketType::CHARACTER_LOAD;
strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE);
strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE);
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
//query the world state
memset(&newPacket, 0, MAX_PACKET_SIZE);
newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS;
network.SendTo(Channels::SERVER, &newPacket);
//set the camera's values
camera.width = GetScreen()->w;
camera.height = GetScreen()->h;
//debug
//
}
InWorld::~InWorld() {
//unload the local data
characterMap.clear();
monsterMap.clear();
}
//-------------------------
//Frame loop
//-------------------------
void InWorld::FrameStart() {
//
}
void InWorld::Update() {
//create and zero the buffer
SerialPacket* packetBuffer = reinterpret_cast<SerialPacket*>(new char[MAX_PACKET_SIZE]);
memset(packetBuffer, 0, MAX_PACKET_SIZE);
try {
//suck in and process all waiting packets
while(network.Receive(packetBuffer)) {
HandlePacket(packetBuffer);
}
}
catch(terminal_error& e) {
throw(e);
}
catch(std::exception& e) {
std::cerr << "HandlePacket Error: " << e.what() << std::endl;
}
//free the buffer
delete reinterpret_cast<char*>(packetBuffer);
//heartbeat system
CheckHeartBeat();
//update all entities
for (auto& it : characterMap) {
it.second.Update();
}
for (auto& it : monsterMap) {
it.second.Update();
}
//update the map
UpdateMap();
//skip the rest without a local character
if (!localCharacter) {
return;
}
//get the collidable boxes
std::list<BoundingBox> boxList = GenerateCollisionGrid(localCharacter, tileSheet.GetTileW(), tileSheet.GetTileH());
//process the collisions
if (localCharacter->ProcessCollisionGrid(boxList, keyState)) {
localCharacter->CorrectSprite();
SendLocalCharacterMovement();
}
//update the camera
camera.x = localCharacter->GetOrigin().x - camera.marginX;
camera.y = localCharacter->GetOrigin().y - camera.marginY;
}
void InWorld::FrameEnd() {
//
}
void InWorld::RenderFrame() {
SDL_FillRect(GetScreen(), 0, 0);
Render(GetScreen());
SDL_Flip(GetScreen());
fps.Calculate();
}
void InWorld::Render(SDL_Surface* const screen) {
//draw the map
for (std::list<Region>::iterator it = regionPager.GetContainer()->begin(); it != regionPager.GetContainer()->end(); it++) {
tileSheet.DrawRegionTo(screen, &(*it), camera.x, camera.y);
}
//draw the entities
for (auto& it : characterMap) {
//TODO: depth ordering
it.second.DrawTo(screen, camera.x, camera.y);
}
for (auto& it : monsterMap) {
//TODO: depth ordering
it.second.DrawTo(screen, camera.x, camera.y);
}
//draw UI
disconnectButton.DrawTo(screen);
shutDownButton.DrawTo(screen);
std::ostringstream msg;
msg << fps.GetFrameRate();
font.DrawStringTo(msg.str(), screen, 0, 0);
}
//-------------------------
//Event handlers
//-------------------------
void InWorld::QuitEvent() {
//two-step logout
SendDisconnectRequest();
SetNextScene(SceneList::QUIT);
}
void InWorld::MouseMotion(SDL_MouseMotionEvent const& motion) {
disconnectButton.MouseMotion(motion);
shutDownButton.MouseMotion(motion);
}
void InWorld::MouseButtonDown(SDL_MouseButtonEvent const& button) {
disconnectButton.MouseButtonDown(button);
shutDownButton.MouseButtonDown(button);
}
void InWorld::MouseButtonUp(SDL_MouseButtonEvent const& button) {
if (disconnectButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) {
SendLogoutRequest();
}
if (shutDownButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) {
SendShutdownRequest();
}
}
void InWorld::KeyDown(SDL_KeyboardEvent const& key) {
//hotkeys & player input
switch(key.keysym.sym) {
case SDLK_ESCAPE:
//TODO: the escape key should actually control menus and stuff
SendLogoutRequest();
return;
case SDLK_w:
case SDLK_a:
case SDLK_s:
case SDLK_d:
ProcessLocalCharacterMovement();
break;
default:
//DOCS: prevents wrong keys screwing with character movement
return;
}
}
void InWorld::KeyUp(SDL_KeyboardEvent const& key) {
switch(key.keysym.sym) {
case SDLK_w:
case SDLK_a:
case SDLK_s:
case SDLK_d:
ProcessLocalCharacterMovement();
break;
default:
//DOCS: prevents wrong keys screwing with character movement
return;
}
}
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
@@ -1,134 +0,0 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "world.hpp"
#include "channels.hpp"
#include "ip_operators.hpp"
#include <chrono>
#include <sstream>
#include <stdexcept>
//-------------------------
//heartbeat system
//-------------------------
void World::hPing(ServerPacket* const argPacket) {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PONG;
network.SendTo(argPacket->srcAddress, &newPacket);
}
void World::hPong(ServerPacket* const argPacket) {
if (*network.GetIPAddress(Channels::SERVER) != argPacket->srcAddress) {
throw(std::runtime_error("Heartbeat message received from an unknown source"));
}
attemptedBeats = 0;
lastBeat = Clock::now();
}
void World::CheckHeartBeat() {
//check the connection (heartbeat)
if (Clock::now() - lastBeat > std::chrono::seconds(3)) {
if (attemptedBeats > 2) {
//escape to the disconnect screen
SendDisconnectRequest();
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "Error: Lost connection to the server";
}
else {
ServerPacket newPacket;
newPacket.type = SerialPacketType::PING;
network.SendTo(Channels::SERVER, &newPacket);
attemptedBeats++;
lastBeat = Clock::now();
}
}
}
//-------------------------
//Connection control
//-------------------------
void World::SendLogoutRequest() {
ClientPacket newPacket;
//send a logout request
newPacket.type = SerialPacketType::LOGOUT_REQUEST;
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void World::SendDisconnectRequest() {
ClientPacket newPacket;
//send a disconnect request
newPacket.type = SerialPacketType::DISCONNECT_REQUEST;
newPacket.clientIndex = clientIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void World::SendAdminDisconnectForced() {
//TODO: (9) World::SendAdminDisconnectForced()
}
void World::SendAdminShutdownRequest() {
ClientPacket newPacket;
//send a shutdown request
newPacket.type = SerialPacketType::ADMIN_SHUTDOWN_REQUEST;
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
}
void World::hLogoutResponse(ClientPacket* const argPacket) {
if (localCharacter) {
characterMap.erase(characterIndex);
localCharacter = nullptr;
}
accountIndex = -1;
characterIndex = -1;
//reset the camera
camera.marginX = camera.marginY = 0;
//because, why not? I guess...
SendDisconnectRequest();
}
void World::hDisconnectResponse(ClientPacket* const argPacket) {
hLogoutResponse(argPacket);//shortcut
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have successfully logged out";
}
void World::hAdminDisconnectForced(ClientPacket* const argPacket) {
hDisconnectResponse(argPacket);//shortcut
SetNextScene(SceneList::DISCONNECTEDSCREEN);
ConfigUtility::GetSingleton()["client.disconnectMessage"] = "You have been forcibly disconnected by the server";
}
-428
View File
@@ -1,428 +0,0 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "world.hpp"
#include "channels.hpp"
#include "terminal_error.hpp"
#include <stdexcept>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <sstream>
//-------------------------
//Public access members
//-------------------------
World::World(int* const argClientIndex, int* const argAccountIndex):
clientIndex(*argClientIndex),
accountIndex(*argAccountIndex)
{
//setup the utility objects
buttonImage.LoadSurface(config["dir.interface"] + "button_menu.bmp");
buttonImage.SetClipH(buttonImage.GetClipH()/3);
font.LoadSurface(config["dir.fonts"] + "pk_white_8.bmp");
//pass the utility objects
disconnectButton.SetImage(&buttonImage);
disconnectButton.SetFont(&font);
shutDownButton.SetImage(&buttonImage);
shutDownButton.SetFont(&font);
//set the button positions
disconnectButton.SetX(50);
disconnectButton.SetY(50 + buttonImage.GetClipH() * 0);
shutDownButton.SetX(50);
shutDownButton.SetY(50 + buttonImage.GetClipH() * 1);
//set the button texts
disconnectButton.SetText("Disconnect");
shutDownButton.SetText("Shut Down");
//load the tilesheet
//TODO: (2) Tile size and tile sheet should be loaded elsewhere
tileSheet.Load(config["dir.tilesets"] + "overworld.bmp", 32, 32);
//Send the character data
CharacterPacket newPacket;
newPacket.type = SerialPacketType::CHARACTER_LOAD;
strncpy(newPacket.handle, config["client.handle"].c_str(), PACKET_STRING_SIZE);
strncpy(newPacket.avatar, config["client.avatar"].c_str(), PACKET_STRING_SIZE);
newPacket.accountIndex = accountIndex;
network.SendTo(Channels::SERVER, &newPacket);
//set the camera's values
camera.width = GetScreen()->w;
camera.height = GetScreen()->h;
//debug
//
}
World::~World() {
//unload the local data
characterMap.clear();
monsterMap.clear();
}
//-------------------------
//Frame loop
//-------------------------
void World::FrameStart() {
//
}
void World::Update() {
//create and zero the buffer
SerialPacket* packetBuffer = reinterpret_cast<SerialPacket*>(new char[MAX_PACKET_SIZE]);
memset(packetBuffer, 0, MAX_PACKET_SIZE);
try {
//suck in and process all waiting packets
while(network.Receive(packetBuffer)) {
HandlePacket(packetBuffer);
}
}
catch(terminal_error& e) {
throw(e);
}
catch(std::exception& e) {
std::cerr << "HandlePacket Error: " << e.what() << std::endl;
}
//free the buffer
delete reinterpret_cast<char*>(packetBuffer);
//heartbeat system
CheckHeartBeat();
//update all entities
for (auto& it : characterMap) {
it.second.Update();
}
for (auto& it : monsterMap) {
it.second.Update();
}
try {
//update the map
UpdateMap();
}
catch(terminal_error& e) {
throw(e);
}
catch(std::exception& e) {
std::cerr << "UpdateMap Error: " << e.what() << std::endl;
}
//skip the rest without a local character
if (!localCharacter) {
return;
}
//get the collidable boxes
std::list<BoundingBox> boxList = GenerateCollisionGrid(localCharacter, tileSheet.GetTileW(), tileSheet.GetTileH());
//process the collisions
if (localCharacter->ProcessCollisionGrid(boxList)) {
localCharacter->CorrectSprite();
SendLocalCharacterMovement();
}
//update the camera
camera.x = localCharacter->GetOrigin().x - camera.marginX;
camera.y = localCharacter->GetOrigin().y - camera.marginY;
}
void World::FrameEnd() {
//
}
void World::RenderFrame() {
// SDL_FillRect(GetScreen(), 0, 0);
Render(GetScreen());
SDL_Flip(GetScreen());
fps.Calculate();
}
void World::Render(SDL_Surface* const screen) {
//draw the map
for (std::list<Region>::iterator it = regionPager.GetContainer()->begin(); it != regionPager.GetContainer()->end(); it++) {
tileSheet.DrawRegionTo(screen, &(*it), camera.x, camera.y);
//debugging
// std::ostringstream msg;
// msg << it->GetX() << ", " << it->GetY();
// font.DrawStringTo(msg.str(), screen, it->GetX() * tileSheet.GetImage()->GetClipW() - camera.x, it->GetY() * tileSheet.GetImage()->GetClipH() - camera.y);
}
//draw the entities
for (auto& it : characterMap) {
//BUG: #29 Characters (and other entities) are drawn out of order
it.second.DrawTo(screen, camera.x, camera.y);
}
for (auto& it : monsterMap) {
it.second.DrawTo(screen, camera.x, camera.y);
}
//draw UI
disconnectButton.DrawTo(screen);
shutDownButton.DrawTo(screen);
std::ostringstream msg;
msg << fps.GetFrameRate();
font.DrawStringTo(msg.str(), screen, 0, 0);
}
//-------------------------
//Event handlers
//-------------------------
void World::QuitEvent() {
//two-step logout
SendDisconnectRequest();
SetNextScene(SceneList::QUIT);
}
void World::MouseMotion(SDL_MouseMotionEvent const& motion) {
disconnectButton.MouseMotion(motion);
shutDownButton.MouseMotion(motion);
}
void World::MouseButtonDown(SDL_MouseButtonEvent const& button) {
disconnectButton.MouseButtonDown(button);
shutDownButton.MouseButtonDown(button);
}
void World::MouseButtonUp(SDL_MouseButtonEvent const& button) {
if (disconnectButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) {
SendLogoutRequest();
}
if (shutDownButton.MouseButtonUp(button) == Button::State::HOVER && button.button == SDL_BUTTON_LEFT) {
SendAdminShutdownRequest();
}
}
void World::KeyDown(SDL_KeyboardEvent const& key) {
//hotkeys
switch(key.keysym.sym) {
case SDLK_ESCAPE:
//TODO: (3) the escape key should actually control menus and stuff
SendLogoutRequest();
return;
}
//character movement
if (!localCharacter) {
return;
}
Vector2 motion = localCharacter->GetMotion();
switch(key.keysym.sym) {
case SDLK_w:
motion.y -= CHARACTER_WALKING_SPEED;
break;
case SDLK_a:
motion.x -= CHARACTER_WALKING_SPEED;
break;
case SDLK_s:
motion.y += CHARACTER_WALKING_SPEED;
break;
case SDLK_d:
motion.x += CHARACTER_WALKING_SPEED;
break;
default:
//DOCS: prevents wrong keys screwing with character movement
return;
}
//handle diagonals
if (motion.x != 0 && motion.y != 0) {
motion *= CHARACTER_WALKING_MOD;
}
//set the info
localCharacter->SetMotion(motion);
localCharacter->CorrectSprite();
SendLocalCharacterMovement();
}
void World::KeyUp(SDL_KeyboardEvent const& key) {
//character movement
if (!localCharacter) {
return;
}
Vector2 motion = localCharacter->GetMotion();
switch(key.keysym.sym) {
case SDLK_w:
motion.y = std::min(0.0, motion.y += CHARACTER_WALKING_SPEED);
break;
case SDLK_a:
motion.x = std::min(0.0, motion.x += CHARACTER_WALKING_SPEED);
break;
case SDLK_s:
motion.y = std::max(0.0, motion.y -= CHARACTER_WALKING_SPEED);
break;
case SDLK_d:
motion.x = std::max(0.0, motion.x -= CHARACTER_WALKING_SPEED);
break;
default:
//DOCS: prevents wrong keys screwing with character movement
return;
}
//BUGFIX: reset cardinal direction speed on key release
if (motion.x > 0) {
motion.x = CHARACTER_WALKING_SPEED;
}
else if (motion.x < 0) {
motion.x = -CHARACTER_WALKING_SPEED;
}
if (motion.y > 0) {
motion.y = CHARACTER_WALKING_SPEED;
}
else if (motion.y < 0) {
motion.y = -CHARACTER_WALKING_SPEED;
}
//handle diagonals
if (motion.x != 0 && motion.y != 0) {
motion *= CHARACTER_WALKING_MOD;
}
//set the info
localCharacter->SetMotion(motion);
localCharacter->CorrectSprite();
SendLocalCharacterMovement();
}
//-------------------------
//Direct incoming traffic
//-------------------------
void World::HandlePacket(SerialPacket* const argPacket) {
switch(argPacket->type) {
//heartbeat system
case SerialPacketType::PING:
hPing(static_cast<ServerPacket*>(argPacket));
break;
case SerialPacketType::PONG:
hPong(static_cast<ServerPacket*>(argPacket));
break;
//game server connections
case SerialPacketType::LOGOUT_RESPONSE:
hLogoutResponse(static_cast<ClientPacket*>(argPacket));
break;
case SerialPacketType::DISCONNECT_RESPONSE:
hDisconnectResponse(static_cast<ClientPacket*>(argPacket));
break;
case SerialPacketType::ADMIN_DISCONNECT_FORCED:
hAdminDisconnectForced(static_cast<ClientPacket*>(argPacket));
break;
//map management
case SerialPacketType::REGION_CONTENT:
hRegionContent(static_cast<RegionPacket*>(argPacket));
break;
//character management
case SerialPacketType::CHARACTER_UPDATE:
hCharacterUpdate(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_CREATE:
hCharacterCreate(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_DELETE:
hCharacterDelete(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_CHARACTER_EXISTS:
hQueryCharacterExists(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_CHARACTER_STATS:
hQueryCharacterStats(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_CHARACTER_LOCATION:
hQueryCharacterLocation(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_MOVEMENT:
hCharacterMovement(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_ATTACK:
hCharacterAttack(static_cast<CharacterPacket*>(argPacket));
break;
case SerialPacketType::CHARACTER_DAMAGE:
hCharacterDamage(static_cast<CharacterPacket*>(argPacket));
break;
//monster management
case SerialPacketType::MONSTER_CREATE:
hMonsterCreate(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_DELETE:
hMonsterDelete(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_MONSTER_EXISTS:
hQueryMonsterExists(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_MONSTER_STATS:
hQueryMonsterStats(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::QUERY_MONSTER_LOCATION:
hQueryMonsterLocation(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_MOVEMENT:
hMonsterMovement(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_ATTACK:
hMonsterAttack(static_cast<MonsterPacket*>(argPacket));
break;
case SerialPacketType::MONSTER_DAMAGE:
hMonsterDamage(static_cast<MonsterPacket*>(argPacket));
break;
//chat
case SerialPacketType::TEXT_BROADCAST:
hTextBroadcast(static_cast<TextPacket*>(argPacket));
break;
case SerialPacketType::TEXT_SPEECH:
hTextSpeech(static_cast<TextPacket*>(argPacket));
break;
case SerialPacketType::TEXT_WHISPER:
hTextWhisper(static_cast<TextPacket*>(argPacket));
break;
//general rejection messages
case SerialPacketType::REGION_REJECTION:
case SerialPacketType::CHARACTER_REJECTION:
case SerialPacketType::QUERY_REJECTION:
throw(terminal_error(static_cast<TextPacket*>(argPacket)->text));
break;
case SerialPacketType::SHUTDOWN_REJECTION:
throw(std::runtime_error(static_cast<TextPacket*>(argPacket)->text));
break;
//errors
default: {
std::ostringstream msg;
msg << "Unknown SerialPacketType encountered in World: " << static_cast<int>(argPacket->type);
throw(std::runtime_error(msg.str()));
}
break;
}
}
-118
View File
@@ -1,118 +0,0 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "world.hpp"
#include "channels.hpp"
#include <sstream>
//-------------------------
//static functions
//-------------------------
static int regionChecksum(Region* const region) {
int sum = 0;
for(int i = 0; i < REGION_WIDTH; i++) {
for (int j = 0; j < REGION_HEIGHT; j++) {
for (int k = 0; k < REGION_DEPTH; k++) {
sum += region->GetTile(i, j, k);
}
}
}
return sum;
}
//-------------------------
//map management
//-------------------------
void World::SendRegionRequest(int roomIndex, int x, int y) {
RegionPacket packet;
//pack the region's data
packet.type = SerialPacketType::REGION_REQUEST;
packet.roomIndex = roomIndex;
packet.x = x;
packet.y = y;
network.SendTo(Channels::SERVER, &packet);
}
void World::hRegionContent(RegionPacket* const argPacket) {
//checksum
if (regionChecksum(argPacket->region) == 0) {
std::ostringstream msg;
msg << "Received region checksum failed: " << argPacket->x << ", " << argPacket->y;
throw(std::runtime_error(msg.str()));
}
//replace existing regions
regionPager.UnloadIf([&](Region const& region) -> bool {
return region.GetX() == argPacket->x && region.GetY() == argPacket->y;
});
regionPager.PushRegion(argPacket->region);
//clean up after the serial code
delete argPacket->region;
argPacket->region = nullptr;
}
void World::UpdateMap() {
if (roomIndex == -1) {
return;
}
//these represent the zone of regions that the client needs loaded, including the mandatory buffers (+1/-1)
int xStart = snapToBase(REGION_WIDTH, camera.x/tileSheet.GetTileW()) - REGION_WIDTH;
int xEnd = snapToBase(REGION_WIDTH, (camera.x+camera.width)/tileSheet.GetTileW()) + REGION_WIDTH;
int yStart = snapToBase(REGION_HEIGHT, camera.y/tileSheet.GetTileH()) - REGION_HEIGHT;
int yEnd = snapToBase(REGION_HEIGHT, (camera.y+camera.height)/tileSheet.GetTileH()) + REGION_HEIGHT;
//prune distant regions
regionPager.GetContainer()->remove_if([&](Region const& region) -> bool {
return region.GetX() < xStart || region.GetX() > xEnd || region.GetY() < yStart || region.GetY() > yEnd;
});
//request empty regions within this zone
for (int i = xStart; i <= xEnd; i += REGION_WIDTH) {
for (int j = yStart; j <= yEnd; j += REGION_HEIGHT) {
Region* region = regionPager.FindRegion(i, j);
if (!region) {
//request absent region
SendRegionRequest(roomIndex, i, j);
}
else if (regionChecksum(region) == 0) {
//checksum failed
//NOTE: this patches bug #45, but does not resolve it
regionPager.UnloadIf([region](Region const& ref) -> bool {
//remove the erroneous region
return region == &ref;
});
SendRegionRequest(roomIndex, i, j);
std::ostringstream msg;
msg << "Existing region checksum failed: " << roomIndex << ", " << i << ", " << j;
throw(std::runtime_error(msg.str()));
}
}
}
}
-126
View File
@@ -1,126 +0,0 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "world.hpp"
#include "channels.hpp"
#include <iostream>
#include <sstream>
#include <stdexcept>
//-------------------------
//monster management
//-------------------------
void World::hMonsterCreate(MonsterPacket* const argPacket) {
//check for logic errors
if (monsterMap.find(argPacket->monsterIndex) != monsterMap.end()) {
std::ostringstream msg;
msg << "Double monster creation event; ";
msg << "Index: " << argPacket->monsterIndex << "; ";
msg << "Handle: " << argPacket->handle;
throw(std::runtime_error(msg.str()));
}
//ignore monsters from other rooms
if (roomIndex != argPacket->roomIndex) {
//temporary error checking
std::ostringstream msg;
msg << "Monster from the wrong room received: ";
msg << "monsterIndex: " << argPacket->monsterIndex << ", roomIndex: " << argPacket->roomIndex;
throw(std::runtime_error(msg.str()));
}
//implicitly create the element
BaseMonster* monster = &monsterMap[argPacket->monsterIndex];
//fill the monster's info
monster->SetHandle(argPacket->handle);
monster->SetAvatar(argPacket->avatar);
monster->SetBounds(argPacket->bounds);
monster->SetOrigin(argPacket->origin);
monster->SetMotion(argPacket->motion);
//debug
std::cout << "Monster Create, total: " << monsterMap.size() << std::endl;
}
void World::hMonsterDelete(MonsterPacket* const argPacket) {
//ignore if this monster doesn't exist
std::map<int, BaseMonster>::iterator monsterIt = monsterMap.find(argPacket->monsterIndex);
if (monsterIt == monsterMap.end()) {
return;
}
//remove this monster
monsterMap.erase(monsterIt);
//debug
std::cout << "Monster Delete, total: " << monsterMap.size() << std::endl;
}
void World::hQueryMonsterExists(MonsterPacket* const argPacket) {
//ignore monsters in a different room (sub-optimal)
if (argPacket->roomIndex != roomIndex) {
return;
}
//implicitly create the element
BaseMonster* monster = &monsterMap[argPacket->monsterIndex];
//fill the monster's info
monster->SetHandle(argPacket->handle);
monster->SetAvatar(argPacket->avatar);
monster->SetBounds(argPacket->bounds);
monster->SetOrigin(argPacket->origin);
monster->SetMotion(argPacket->motion);
//debug
std::cout << "Monster Query, total: " << monsterMap.size() << std::endl;
}
void World::hQueryMonsterStats(MonsterPacket* const argPacket) {
//TODO: (9) World::hQueryMonsterStats()
}
void World::hQueryMonsterLocation(MonsterPacket* const argPacket) {
//TODO: (9) World::hQueryMonsterLocation()
}
void World::hMonsterMovement(MonsterPacket* const argPacket) {
//ignore if this monster doesn't exist
std::map<int, BaseMonster>::iterator monsterIt = monsterMap.find(argPacket->monsterIndex);
if (monsterIt == monsterMap.end()) {
return;
}
monsterIt->second.SetOrigin(argPacket->origin);
monsterIt->second.SetOrigin(argPacket->motion);
}
void World::hMonsterAttack(MonsterPacket* const argPacket) {
//TODO: (9) World::hMonsterAttack()
}
void World::hMonsterDamage(MonsterPacket* const argPacket) {
//TODO: (9) World::hMonsterDamage()
}
+1 -7
View File
@@ -1,5 +1,5 @@
#include directories
INCLUDES+=SDL . client_utilities entities gameplay_scenes menu_scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities
INCLUDES+=. client_utilities entities gameplay_scenes menu_scenes ../common/debugging ../common/gameplay ../common/graphics ../common/map ../common/network ../common/network/packet_types ../common/ui ../common/utilities
#libraries
#the order of the $(LIBS) is important, at least for MinGW
@@ -45,12 +45,6 @@ $(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe
else ifeq ($(shell uname), Linux)
find . -type f -name *.o -exec rm -f -r -v {} \;
find . -type f -name *.a -exec rm -f -r -v {} \;
rm -f -v out/client out/server
endif
rebuild: clean all
+8 -7
View File
@@ -100,15 +100,14 @@ void LobbyMenu::FrameEnd() {
}
void LobbyMenu::Render(SDL_Surface* const screen) {
//TODO: (2) I need a proper UI system for the entire client and the editor
//TODO: I need a proper UI system for the entire client and the editor
//UI
search.DrawTo(screen);
join.DrawTo(screen);
back.DrawTo(screen);
//TODO: (3) draw headers for the server list
//TODO: (3) ping/delay displayed in the server list
//TODO: draw headers for the server list
for (int i = 0; i < serverInfo.size(); i++) {
//draw the selected server's highlight
if (selection == &serverInfo[i]) {
@@ -132,6 +131,8 @@ void LobbyMenu::Render(SDL_Surface* const screen) {
if (!serverInfo[i].compatible) {
font.DrawStringTo("?", screen, listBox.x - font.GetCharW(), listBox.y + i*listBox.h);
}
//TODO: ping/delay?
}
}
@@ -165,7 +166,7 @@ void LobbyMenu::MouseButtonUp(SDL_MouseButtonEvent const& button) {
//has the user selected a server on the list?
BoundingBox tmpBox = listBox;
tmpBox.h *= serverInfo.size();
if (tmpBox.CheckOverlap({button.x, button.y})) {
if (tmpBox.CheckCollision({button.x, button.y, 0, 0})) {
selection = &serverInfo[(button.y - listBox.y)/listBox.h];
}
else {
@@ -249,15 +250,15 @@ void LobbyMenu::HandleLoginResponse(ClientPacket* const argPacket) {
throw(std::runtime_error("Client index invalid during login"));
}
accountIndex = argPacket->accountIndex;
SetNextScene(SceneList::WORLD);
SetNextScene(SceneList::INWORLD);
}
void LobbyMenu::HandleJoinRejection(TextPacket* const argPacket) {
//TODO: (9) LobbyMenu::HandleJoinRejection()
//TODO: Better output for join rejection
}
void LobbyMenu::HandleLoginRejection(TextPacket* const argPacket) {
//TODO: (9) LobbyMenu::HandleLoginRejection
//TODO: Better output for login rejection
}
//-------------------------
+4 -7
View File
@@ -88,7 +88,8 @@ void MainMenu::Render(SDL_Surface* const screen) {
//text
font.DrawStringTo("Thanks for playing!", screen, 50, screen->h - 50 - image.GetClipH() * 2);
font.DrawStringTo("You can get the latest version at: ", screen, 50, screen->h - 50 - image.GetClipH() * 1);
font.DrawStringTo("krgamestudios.com", screen, 50, screen->h - 50 - image.GetClipH() * 0);
font.DrawStringTo("https://github.com/Ratstail91/Tortuga", screen, 50, screen->h - 50 - image.GetClipH() * 0);
//TODO: replace this with a website address
}
//-------------------------
@@ -108,7 +109,7 @@ void MainMenu::MouseButtonDown(SDL_MouseButtonEvent const& button) {
}
void MainMenu::MouseButtonUp(SDL_MouseButtonEvent const& button) {
//TODO: (2) Buttons should only register as "selected" when the left button is used
//TODO: Buttons should only register as "selected" when the left button is used
if (startButton.MouseButtonUp(button) == Button::State::HOVER) {
SetNextScene(SceneList::LOBBYMENU);
}
@@ -125,9 +126,5 @@ void MainMenu::KeyDown(SDL_KeyboardEvent const& key) {
}
void MainMenu::KeyUp(SDL_KeyboardEvent const& key) {
switch(key.keysym.sym) {
case SDLK_ESCAPE:
QuitEvent();
break;
}
//
}
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+1 -1
View File
@@ -28,7 +28,7 @@
#include "raster_font.hpp"
#include "button.hpp"
//NOTE: The options screen needs to be USED
//TODO: The options screen needs to be USED
class OptionsMenu : public BaseScene {
public:
//Public access members
+1 -1
View File
@@ -33,7 +33,7 @@ enum class SceneList {
MAINMENU,
OPTIONSMENU,
LOBBYMENU,
WORLD,
INWORLD,
DISCONNECTEDSCREEN,
};
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+5
View File
@@ -6,3 +6,8 @@ all:
$(MAKE) -C network
$(MAKE) -C ui
$(MAKE) -C utilities
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+55
View File
@@ -0,0 +1,55 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "map_system_api.hpp"
//all map API headers
#include "region_api.hpp"
#include "region_pager_api.hpp"
//useful "globals"
//...
//This mimics linit.c to create a nested collection of all map modules.
static const luaL_Reg funcs[] = {
{nullptr, nullptr}
};
static const luaL_Reg libs[] = {
{"Region", openRegionAPI},
{"RegionPager", openRegionPagerAPI},
{nullptr, nullptr}
};
int openMapSystemAPI(lua_State* L) {
//create the table
luaL_newlibtable(L, libs);
//push the "global" functions
luaL_setfuncs(L, funcs, 0);
//push the substable
for (const luaL_Reg* lib = libs; lib->func; lib++) {
lib->func(L);
lua_setfield(L, -2, lib->name);
}
return 1;
}
@@ -19,12 +19,12 @@
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef TRIGGERAPI_HPP_
#define TRIGGERAPI_HPP_
#ifndef MAPSYSTEMAPI_HPP_
#define MAPSYSTEMAPI_APP_
#include "lua.hpp"
#define TORTUGA_TRIGGER_API "trigger"
LUAMOD_API int openTriggerAPI(lua_State* L);
#define TORTUGA_MAP_SYSTEM_API "map_system"
LUAMOD_API int openMapSystemAPI(lua_State* L);
#endif
+1 -1
View File
@@ -24,7 +24,7 @@
#include "lua.hpp"
#define TORTUGA_REGION_API "region"
#define TORTUGA_REGION_NAME "region"
LUAMOD_API int openRegionAPI(lua_State* L);
#endif
-1
View File
@@ -25,7 +25,6 @@
#include "region.hpp"
//DOCS: These glue functions simply wrap RegionPagerLua's methods
//NOTE: zero indexing is used here, but not in the region API
static int setTile(lua_State* L) {
RegionPagerLua* pager = reinterpret_cast<RegionPagerLua*>(lua_touserdata(L, 1));
+1 -1
View File
@@ -24,7 +24,7 @@
#include "lua.hpp"
#define TORTUGA_REGION_PAGER_API "region_pager"
#define TORTUGA_REGION_PAGER_NAME "region_pager"
LUAMOD_API int openRegionPagerAPI(lua_State* L);
#endif
-1
View File
@@ -70,7 +70,6 @@ Region* RegionPagerBase::FindRegion(int x, int y) {
}
Region* RegionPagerBase::PushRegion(Region* const ptr) {
//BUG: #45 Some regions are occasionally losing their tile data
regionList.push_front(*ptr);
return &regionList.front();
}
+5
View File
@@ -31,3 +31,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
@@ -36,18 +36,11 @@ void serializeCharacter(void* buffer, CharacterPacket* packet) {
//location
serialCopy(&buffer, &packet->roomIndex, sizeof(int));
serialCopy(&buffer, &packet->origin.x, sizeof(double));
serialCopy(&buffer, &packet->origin.y, sizeof(double));
serialCopy(&buffer, &packet->motion.x, sizeof(double));
serialCopy(&buffer, &packet->motion.y, sizeof(double));
serialCopy(&buffer, &packet->bounds.x, sizeof(int));
serialCopy(&buffer, &packet->bounds.y, sizeof(int));
serialCopy(&buffer, &packet->bounds.w, sizeof(int));
serialCopy(&buffer, &packet->bounds.h, sizeof(int));
//gameplay components: equipment, items, buffs, debuffs...
}
@@ -64,17 +57,10 @@ void deserializeCharacter(void* buffer, CharacterPacket* packet) {
//location
deserialCopy(&buffer, &packet->roomIndex, sizeof(int));
deserialCopy(&buffer, &packet->origin.x, sizeof(double));
deserialCopy(&buffer, &packet->origin.y, sizeof(double));
deserialCopy(&buffer, &packet->motion.x, sizeof(double));
deserialCopy(&buffer, &packet->motion.y, sizeof(double));
deserialCopy(&buffer, &packet->bounds.x, sizeof(int));
deserialCopy(&buffer, &packet->bounds.y, sizeof(int));
deserialCopy(&buffer, &packet->bounds.w, sizeof(int));
deserialCopy(&buffer, &packet->bounds.h, sizeof(int));
//gameplay components: equipment, items, buffs, debuffs...
}
@@ -24,7 +24,6 @@
#include "serial_packet_base.hpp"
#include "bounding_box.hpp"
#include "vector2.hpp"
struct CharacterPacket : SerialPacketBase {
@@ -35,12 +34,14 @@ struct CharacterPacket : SerialPacketBase {
//the owner
int accountIndex;
//TODO: Authentication token?
//location
int roomIndex;
Vector2 origin;
Vector2 motion;
BoundingBox bounds;
//gameplay components: equipment, items, buffs, debuffs...
};
void serializeCharacter(void* buffer, CharacterPacket* packet);
@@ -28,7 +28,6 @@ struct ClientPacket : SerialPacketBase {
int clientIndex;
int accountIndex;
char username[PACKET_STRING_SIZE];
//TODO: (3) password, auth token
};
void serializeClient(void* buffer, ClientPacket* packet);
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
@@ -44,6 +44,9 @@ void serializeMonster(void* buffer, MonsterPacket* packet) {
serialCopy(&buffer, &packet->origin.y, sizeof(double));
serialCopy(&buffer, &packet->motion.x, sizeof(double));
serialCopy(&buffer, &packet->motion.y, sizeof(double));
//attack data
//TODO
}
void deserializeMonster(void* buffer, MonsterPacket* packet) {
@@ -67,4 +70,7 @@ void deserializeMonster(void* buffer, MonsterPacket* packet) {
deserialCopy(&buffer, &packet->origin.y, sizeof(double));
deserialCopy(&buffer, &packet->motion.x, sizeof(double));
deserialCopy(&buffer, &packet->motion.y, sizeof(double));
//attack data
//TODO
}
@@ -38,6 +38,8 @@ struct MonsterPacket : SerialPacketBase {
int roomIndex;
Vector2 origin;
Vector2 motion;
//TODO: attack data
};
void serializeMonster(void* buffer, MonsterPacket* packet);
@@ -24,7 +24,7 @@
#include "serial_packet_type.hpp"
#include "SDL_net.h"
#include "SDL/SDL_net.h"
constexpr int PACKET_STRING_SIZE = 100;
@@ -29,12 +29,6 @@ void serializeText(void* buffer, TextPacket* packet) {
//content
serialCopy(&buffer, packet->name, PACKET_STRING_SIZE);
serialCopy(&buffer, packet->text, PACKET_STRING_SIZE);
//location
serialCopy(&buffer, &packet->roomIndex, sizeof(int));
serialCopy(&buffer, &packet->origin.x, sizeof(double));
serialCopy(&buffer, &packet->origin.y, sizeof(double));
serialCopy(&buffer, &packet->range, sizeof(int));
}
void deserializeText(void* buffer, TextPacket* packet) {
@@ -43,10 +37,4 @@ void deserializeText(void* buffer, TextPacket* packet) {
//content
deserialCopy(&buffer, packet->name, PACKET_STRING_SIZE);
deserialCopy(&buffer, packet->text, PACKET_STRING_SIZE);
//location
deserialCopy(&buffer, &packet->roomIndex, sizeof(int));
deserialCopy(&buffer, &packet->origin.x, sizeof(double));
deserialCopy(&buffer, &packet->origin.y, sizeof(double));
deserialCopy(&buffer, &packet->range, sizeof(int));
}
@@ -24,14 +24,9 @@
#include "serial_packet_base.hpp"
#include "vector2.hpp"
struct TextPacket : SerialPacketBase {
char name[PACKET_STRING_SIZE];
char text[PACKET_STRING_SIZE];
int roomIndex;
Vector2 origin;
int range;
};
void serializeText(void* buffer, TextPacket* packet);
+1 -1
View File
@@ -34,7 +34,7 @@
typedef SerialPacketBase SerialPacket;
//DOCS: NETWORK_VERSION is used to discern compatible servers and clients
constexpr int NETWORK_VERSION = 20150304;
constexpr int NETWORK_VERSION = -1;
union MaxPacket {
CharacterPacket a;
+13 -50
View File
@@ -25,9 +25,9 @@
/* DOCS: The headers indicate what packet type is used for each message
* different messages under the same header will carry different amounts of
* valid data, but it will still be carried in that packet's format.
* FORMAT_* is for internal use, deviding the different format bounds.
*/
//TODO: This needs to be smoothed out
enum class SerialPacketType {
//default: there is something wrong
NONE,
@@ -37,8 +37,6 @@ enum class SerialPacketType {
// name, player count, version
//-------------------------
FORMAT_SERVER,
//heartbeat
PING,
PONG,
@@ -47,15 +45,11 @@ enum class SerialPacketType {
BROADCAST_REQUEST,
BROADCAST_RESPONSE,
FORMAT_END_SERVER,
//-------------------------
//ClientPacket
// client index, account index, username
//-------------------------
FORMAT_CLIENT,
//Connecting to a server as a client
JOIN_REQUEST,
JOIN_RESPONSE,
@@ -63,7 +57,7 @@ enum class SerialPacketType {
//disconnect from the server
DISCONNECT_REQUEST,
DISCONNECT_RESPONSE,
ADMIN_DISCONNECT_FORCED,
DISCONNECT_FORCED,
//load the account
LOGIN_REQUEST,
@@ -74,36 +68,26 @@ enum class SerialPacketType {
LOGOUT_RESPONSE,
//shut down the server
ADMIN_SHUTDOWN_REQUEST,
FORMAT_END_CLIENT,
SHUTDOWN_REQUEST,
//-------------------------
//RegionPacket
// room index, x, y, raw data
//-------------------------
FORMAT_REGION,
//map data
REGION_REQUEST,
REGION_REQUEST, //NOTE: technically a query
REGION_CONTENT,
FORMAT_END_REGION,
//-------------------------
//CharacterPacket
// character index,
// handle, avatar,
// account index (owner),
// room index, origin, motion
// room index, origin, motion,
// statistics
//-------------------------
FORMAT_CHARACTER,
//full data update
CHARACTER_UPDATE,
//character management
CHARACTER_CREATE,
CHARACTER_DELETE,
@@ -115,56 +99,38 @@ enum class SerialPacketType {
QUERY_CHARACTER_STATS,
QUERY_CHARACTER_LOCATION,
//actions taken
//set the info in the server
CHARACTER_MOVEMENT,
CHARACTER_ATTACK,
CHARACTER_DAMAGE,
//admin control
// ADMIN_SET_CHARACTER_ORIGIN,
FORMAT_END_CHARACTER,
//-------------------------
//MonsterPacket
// monster index,
// handle, avatar
// bounds
// handle, avatar, hitbox
// room index, origin, motion
// TODO: attack data
//-------------------------
FORMAT_MONSTER,
//full data update
MONSTER_UPDATE,
//character management
MONSTER_CREATE,
MONSTER_DELETE,
//find out info from the server
QUERY_MONSTER_EXISTS,
QUERY_MONSTER_STATS,
QUERY_MONSTER_LOCATION,
QUERY_MONSTER_EXISTS, //a list of monsters in a room
QUERY_MONSTER_STATS, //statistics of a specific monster type or instance
QUERY_MONSTER_LOCATION, //umm...
//actions taken
MONSTER_MOVEMENT,
MONSTER_MOVEMENT, //monster movement
MONSTER_ATTACK,
MONSTER_DAMAGE,
FORMAT_END_MONSTER,
//-------------------------
//TextPacket
// name, text
//-------------------------
FORMAT_TEXT,
//general speech
TEXT_BROADCAST,
TEXT_SPEECH,
TEXT_WHISPER,
//rejection/error messages
JOIN_REJECTION,
@@ -173,9 +139,6 @@ enum class SerialPacketType {
CHARACTER_REJECTION,
MONSTER_REJECTION,
SHUTDOWN_REJECTION,
QUERY_REJECTION,
FORMAT_END_TEXT,
//-------------------------
//not used
+104 -47
View File
@@ -31,9 +31,6 @@
#include <cstring>
//macros
#define BOUNDS(type, lower, upper) ((type) > (lower) && (type) < (upper))
//raw memory copy
void serialCopy(void** buffer, void* data, int size) {
memcpy(*buffer, data, size);
@@ -49,28 +46,58 @@ void deserialCopy(void** buffer, void* data, int size) {
//main switch functions
void serializePacket(void* buffer, SerialPacketBase* packet) {
if (BOUNDS(packet->type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) {
serializeServer(buffer, static_cast<ServerPacket*>(packet));
}
if (BOUNDS(packet->type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) {
serializeClient(buffer, static_cast<ClientPacket*>(packet));
}
if (BOUNDS(packet->type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) {
serializeRegion(buffer, static_cast<RegionPacket*>(packet));
}
if (BOUNDS(packet->type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) {
serializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
}
if (BOUNDS(packet->type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) {
serializeMonster(buffer, static_cast<MonsterPacket*>(packet));
}
if (BOUNDS(packet->type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) {
serializeText(buffer, static_cast<TextPacket*>(packet));
switch(packet->type) {
case SerialPacketType::PING:
case SerialPacketType::PONG:
case SerialPacketType::BROADCAST_REQUEST:
case SerialPacketType::BROADCAST_RESPONSE:
serializeServer(buffer, static_cast<ServerPacket*>(packet));
break;
case SerialPacketType::JOIN_REQUEST:
case SerialPacketType::JOIN_RESPONSE:
case SerialPacketType::DISCONNECT_REQUEST:
case SerialPacketType::DISCONNECT_RESPONSE:
case SerialPacketType::DISCONNECT_FORCED:
case SerialPacketType::LOGIN_REQUEST:
case SerialPacketType::LOGIN_RESPONSE:
case SerialPacketType::LOGOUT_REQUEST:
case SerialPacketType::LOGOUT_RESPONSE:
case SerialPacketType::SHUTDOWN_REQUEST:
serializeClient(buffer, static_cast<ClientPacket*>(packet));
break;
case SerialPacketType::REGION_REQUEST:
case SerialPacketType::REGION_CONTENT:
serializeRegion(buffer, static_cast<RegionPacket*>(packet));
break;
case SerialPacketType::CHARACTER_CREATE:
case SerialPacketType::CHARACTER_DELETE:
case SerialPacketType::CHARACTER_LOAD:
case SerialPacketType::CHARACTER_UNLOAD:
case SerialPacketType::QUERY_CHARACTER_EXISTS:
case SerialPacketType::QUERY_CHARACTER_STATS:
case SerialPacketType::QUERY_CHARACTER_LOCATION:
case SerialPacketType::CHARACTER_MOVEMENT:
case SerialPacketType::CHARACTER_ATTACK:
serializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
break;
case SerialPacketType::MONSTER_CREATE:
case SerialPacketType::MONSTER_DELETE:
case SerialPacketType::QUERY_MONSTER_EXISTS:
case SerialPacketType::QUERY_MONSTER_STATS:
case SerialPacketType::QUERY_MONSTER_LOCATION:
case SerialPacketType::MONSTER_MOVEMENT:
case SerialPacketType::MONSTER_ATTACK:
serializeMonster(buffer, static_cast<MonsterPacket*>(packet));
break;
case SerialPacketType::TEXT_BROADCAST:
case SerialPacketType::JOIN_REJECTION:
case SerialPacketType::LOGIN_REJECTION:
case SerialPacketType::REGION_REJECTION:
case SerialPacketType::CHARACTER_REJECTION:
case SerialPacketType::MONSTER_REJECTION:
case SerialPacketType::SHUTDOWN_REJECTION:
serializeText(buffer, static_cast<TextPacket*>(packet));
break;
}
}
@@ -79,27 +106,57 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) {
SerialPacketType type;
memcpy(&type, buffer, sizeof(SerialPacketType));
if (BOUNDS(type, SerialPacketType::FORMAT_SERVER, SerialPacketType::FORMAT_END_SERVER)) {
deserializeServer(buffer, static_cast<ServerPacket*>(packet));
}
if (BOUNDS(type, SerialPacketType::FORMAT_CLIENT, SerialPacketType::FORMAT_END_CLIENT)) {
deserializeClient(buffer, static_cast<ClientPacket*>(packet));
}
if (BOUNDS(type, SerialPacketType::FORMAT_REGION, SerialPacketType::FORMAT_END_REGION)) {
deserializeRegion(buffer, static_cast<RegionPacket*>(packet));
}
if (BOUNDS(type, SerialPacketType::FORMAT_CHARACTER, SerialPacketType::FORMAT_END_CHARACTER)) {
deserializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
}
if (BOUNDS(type, SerialPacketType::FORMAT_MONSTER, SerialPacketType::FORMAT_END_MONSTER)) {
deserializeMonster(buffer, static_cast<MonsterPacket*>(packet));
}
if (BOUNDS(type, SerialPacketType::FORMAT_TEXT, SerialPacketType::FORMAT_END_TEXT)) {
deserializeText(buffer, static_cast<TextPacket*>(packet));
switch(type) {
case SerialPacketType::PING:
case SerialPacketType::PONG:
case SerialPacketType::BROADCAST_REQUEST:
case SerialPacketType::BROADCAST_RESPONSE:
deserializeServer(buffer, static_cast<ServerPacket*>(packet));
break;
case SerialPacketType::JOIN_REQUEST:
case SerialPacketType::JOIN_RESPONSE:
case SerialPacketType::DISCONNECT_REQUEST:
case SerialPacketType::DISCONNECT_RESPONSE:
case SerialPacketType::DISCONNECT_FORCED:
case SerialPacketType::LOGIN_REQUEST:
case SerialPacketType::LOGIN_RESPONSE:
case SerialPacketType::LOGOUT_REQUEST:
case SerialPacketType::LOGOUT_RESPONSE:
case SerialPacketType::SHUTDOWN_REQUEST:
deserializeClient(buffer, static_cast<ClientPacket*>(packet));
break;
case SerialPacketType::REGION_REQUEST:
case SerialPacketType::REGION_CONTENT:
deserializeRegion(buffer, static_cast<RegionPacket*>(packet));
break;
case SerialPacketType::CHARACTER_CREATE:
case SerialPacketType::CHARACTER_DELETE:
case SerialPacketType::CHARACTER_LOAD:
case SerialPacketType::CHARACTER_UNLOAD:
case SerialPacketType::QUERY_CHARACTER_EXISTS:
case SerialPacketType::QUERY_CHARACTER_STATS:
case SerialPacketType::QUERY_CHARACTER_LOCATION:
case SerialPacketType::CHARACTER_MOVEMENT:
case SerialPacketType::CHARACTER_ATTACK:
deserializeCharacter(buffer, static_cast<CharacterPacket*>(packet));
break;
case SerialPacketType::MONSTER_CREATE:
case SerialPacketType::MONSTER_DELETE:
case SerialPacketType::QUERY_MONSTER_EXISTS:
case SerialPacketType::QUERY_MONSTER_STATS:
case SerialPacketType::QUERY_MONSTER_LOCATION:
case SerialPacketType::MONSTER_MOVEMENT:
case SerialPacketType::MONSTER_ATTACK:
deserializeMonster(buffer, static_cast<MonsterPacket*>(packet));
break;
case SerialPacketType::TEXT_BROADCAST:
case SerialPacketType::JOIN_REJECTION:
case SerialPacketType::LOGIN_REJECTION:
case SerialPacketType::REGION_REJECTION:
case SerialPacketType::CHARACTER_REJECTION:
case SerialPacketType::MONSTER_REJECTION:
case SerialPacketType::SHUTDOWN_REJECTION:
deserializeText(buffer, static_cast<TextPacket*>(packet));
break;
}
}
+1
View File
@@ -140,6 +140,7 @@ int UDPNetworkUtility::SendToAllChannels(void* data, int len) {
return sent;
}
//TODO: put a void* and int* parameter list here
int UDPNetworkUtility::Receive() {
memset(packet->data, 0, packet->maxlen);
int ret = SDLNet_UDP_Recv(socket, packet);
+1 -1
View File
@@ -27,7 +27,7 @@
#include "singleton.hpp"
//APIs
#include "SDL_net.h"
#include "SDL/SDL_net.h"
class UDPNetworkUtility : public Singleton<UDPNetworkUtility> {
public:
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+16 -14
View File
@@ -22,6 +22,8 @@
#ifndef BOUNDINGBOX_HPP_
#define BOUNDINGBOX_HPP_
#include "vector2.hpp"
#include <type_traits>
#include <algorithm>
@@ -32,16 +34,15 @@ public:
int w, h;
BoundingBox() = default;
BoundingBox(int i, int j): x(i), y(j), w(0), h(0) {};
BoundingBox(int i, int j, int k, int l): x(i), y(j), w(k), h(l) {};
~BoundingBox() = default;
BoundingBox& operator=(BoundingBox const&) = default;
int Size() {
return std::max(w*h,0);
return (w-x) * (h-y);
}
bool CheckOverlap(BoundingBox rhs) {
bool CheckCollision(BoundingBox rhs) {
return !(
x >= rhs.x + rhs.w ||
y >= rhs.y + rhs.h ||
@@ -49,24 +50,25 @@ public:
rhs.y >= y + h);
}
BoundingBox CalcOverlap(BoundingBox rhs) {
if (!CheckOverlap(rhs)) {
return {0, 0, 0, 0};
Vector2 CalcShift(BoundingBox rhs) {
if (!CheckCollision(rhs)) {
return {0, 0};
}
BoundingBox ret;
ret.x = std::max(x, rhs.x);
ret.y = std::max(y, rhs.y);
ret.w = std::min(x+w, rhs.x+rhs.w) - ret.x;
ret.h = std::min(y+h, rhs.y+rhs.h) - ret.y;
return ret;
//DOCS: Given two BoundingBox objects, how does the other have to move so that they are no longer colliding?
Vector2 horizontal = {0, 0};
Vector2 vertical = {0, 0};
horizontal.x = std::abs(x + w - rhs.x) < std::abs(rhs.x + rhs.w - x) ? x + w - rhs.x -1 : -(rhs.x + rhs.w - x -1);
vertical.y = std::abs(y + h - rhs.x) < std::abs(rhs.y + rhs.h - y) ? y + h - rhs.y -1 : -(rhs.y + rhs.h - y -1);
return std::abs(vertical.y) < std::abs(horizontal.x) ? vertical : horizontal;
}
};
//This is explicitly a POD
static_assert(std::is_pod<BoundingBox>::value, "BoundingBox is not a POD");
#include "vector2.hpp"
//operators
inline BoundingBox operator+(BoundingBox b, Vector2 v) {
return {b.x + (int)v.x, b.y + (int)v.y, b.w, b.h};
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+2 -12
View File
@@ -19,23 +19,13 @@ release: clean all package
#For use on my machine ONLY
package:
ifeq ($(OS),Windows_NT)
rar a -r -ep Tortuga-win.rar $(OUTDIR)/*.exe $(OUTDIR)/*.dll
rar a -r Tortuga-win.rar rsc/* copyright.txt instructions.txt
else ifeq ($(shell uname), Linux)
tar -C $(OUTDIR) -zcvf Tortuga-linux.tar client server ../rsc ../copyright.txt ../instructions.txt
endif
rar a -r -ep Tortuga.rar $(OUTDIR)/*.exe $(OUTDIR)/*.dll
rar a -r Tortuga.rar rsc/* copyright.txt README.txt
$(OUTDIR):
mkdir $(OUTDIR)
clean:
ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe
else ifeq ($(shell uname), Linux)
find . -type f -name *.o -exec rm -f -r -v {} \;
find . -type f -name *.a -exec rm -f -r -v {} \;
rm -f -v out/client out/server
endif
rebuild: clean all
-89
View File
@@ -1,89 +0,0 @@
--[[
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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.
*/
--]]
--DOCS: a placeholder door utility
local doorUtility = {}
roomAPI = require("room")
regionPagerAPI = require("region_pager")
triggerAPI = require("trigger")
triggerManagerAPI = require("trigger_manager")
entityAPI = require("entity")
characterAPI = require("character")
networkAPI = require("network")
function doorUtility.createTrigger(handle, room, x, y, script)
local pager = roomAPI.GetPager(room)
--place the indicator tile
regionPagerAPI.SetTile(pager, x / 32, y / 32, 0, mapMaker.dirt)
regionPagerAPI.SetTile(pager, x / 32, y / 32, 1, mapMaker.blank)
regionPagerAPI.SetTile(pager, x / 32, y / 32, 2, mapMaker.blank)
--create the trigger object
triggerManagerAPI.Create(
roomAPI.GetTriggerMgr(room), handle, x, y,
0, 0, 32, 32, --size of the tiles
script
)
end
function doorUtility.createDoorPair(handle, roomOne, Xone, Yone, roomTwo, Xtwo, Ytwo)
--create the scripts
local function scriptOne(entity)
if entityAPI.GetType(entity) ~= "character" then return end
--move the character
characterAPI.SetRoom(entity, roomTwo)
characterAPI.SetOrigin(entity, Xtwo, Ytwo-16)
networkAPI.PumpCharacterUpdate(entity)
--disable the other trigger
local triggerTwo = triggerManagerAPI.GetTrigger(roomAPI.GetTriggerMgr(roomTwo), handle)
triggerAPI.PushExclusionEntity(triggerTwo, entity)
end
local function scriptTwo(entity)
if entityAPI.GetType(entity) ~= "character" then return end
--move the character
characterAPI.SetRoom(entity, roomOne)
characterAPI.SetOrigin(entity, Xone, Yone-16) --NOTE: the 16 pixel margin for presentation
networkAPI.PumpCharacterUpdate(entity)
--disable the other trigger
local triggerOne = triggerManagerAPI.GetTrigger(roomAPI.GetTriggerMgr(roomOne), handle)
triggerAPI.PushExclusionEntity(triggerOne, entity)
end
--create the triggers proper
doorUtility.createTrigger(handle, roomOne, Xone, Yone, scriptOne)
doorUtility.createTrigger(handle, roomTwo, Xtwo, Ytwo, scriptTwo)
end
return doorUtility
+25 -71
View File
@@ -1,30 +1,4 @@
--[[
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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.
*/
--]]
--DOCS: a placeholder map generator
local regionAPI = require("region")
local Region = require("map_system").Region
local mapMaker = {}
@@ -33,7 +7,6 @@ function mapMaker.Sqr(x) return x*x end
function mapMaker.Dist(x, y, i, j) return math.sqrt(mapMaker.Sqr(x - i) + mapMaker.Sqr(y - j)) end
--tile macros, mapped to the tilesheet "overworld.bmp"
mapMaker.blank = 0
mapMaker.water = 18 + 3 * 0
mapMaker.sand = 18 + 3 * 1
mapMaker.plains = 18 + 3 * 2
@@ -47,43 +20,42 @@ mapMaker.edges.south = 16
mapMaker.edges.east = 1
mapMaker.edges.west = -1
--TODO: (1) path system
--use these macros (mapped to "overworld.bmp" for now) to smooth the region's edges
function mapMaker.SmoothEdgesSimple(r)
--make and pad an array to use
local shiftArray = {}
for i = 1, regionAPI.GetWidth(r) do
for i = 1, Region.GetWidth(r) do
shiftArray[i] = {}
for j = 1, regionAPI.GetHeight(r) do
for j = 1, Region.GetHeight(r) do
shiftArray[i][j] = 0
end
end
--build the array
for i = 1, regionAPI.GetWidth(r) do
for j = 1, regionAPI.GetHeight(r) do
--if (not regionAPI edge) and (west tile < this tile), etc.
if i > 1 and regionAPI.GetTile(r, i - 1, j, 1) < regionAPI.GetTile(r, i, j, 1) then
for i = 1, Region.GetWidth(r) do
for j = 1, Region.GetHeight(r) do
--if (not region edge) and (west tile < this tile), etc.
if i > 1 and Region.GetTile(r, i - 1, j, 1) < Region.GetTile(r, i, j, 1) then
shiftArray[i][j] = shiftArray[i][j] + mapMaker.edges.west
end
if j > 1 and regionAPI.GetTile(r, i, j - 1, 1) < regionAPI.GetTile(r, i, j, 1) then
if j > 1 and Region.GetTile(r, i, j - 1, 1) < Region.GetTile(r, i, j, 1) then
shiftArray[i][j] = shiftArray[i][j] + mapMaker.edges.north
end
if i < regionAPI.GetWidth(r) and regionAPI.GetTile(r, i + 1, j, 1) < regionAPI.GetTile(r, i, j, 1) then
if i < Region.GetWidth(r) and Region.GetTile(r, i + 1, j, 1) < Region.GetTile(r, i, j, 1) then
shiftArray[i][j] = shiftArray[i][j] + mapMaker.edges.east
end
if j < regionAPI.GetHeight(r) and regionAPI.GetTile(r, i, j + 1, 1) < regionAPI.GetTile(r, i, j, 1) then
if j < Region.GetHeight(r) and Region.GetTile(r, i, j + 1, 1) < Region.GetTile(r, i, j, 1) then
shiftArray[i][j] = shiftArray[i][j] + mapMaker.edges.south
end
end
end
--finally apply this
for i = 1, regionAPI.GetWidth(r) do
for j = 1, regionAPI.GetHeight(r) do
for i = 1, Region.GetWidth(r) do
for j = 1, Region.GetHeight(r) do
if shiftArray[i][j] ~= 0 then
regionAPI.SetTile(r, i, j, 2, regionAPI.GetTile(r, i, j, 1) + shiftArray[i][j])
regionAPI.SetTile(r, i, j, 1, regionAPI.GetTile(r, i, j, 1) - 3)
Region.SetTile(r, i, j, 2, Region.GetTile(r, i, j, 1) + shiftArray[i][j])
Region.SetTile(r, i, j, 1, Region.GetTile(r, i, j, 1) - 3)
end
end
end
@@ -91,31 +63,28 @@ end
--custom generation systems here
function mapMaker.DebugIsland(r)
--debug
io.write("map_maker:DebugIsland(", regionAPI.GetX(r), ", ", regionAPI.GetY(r), ")\n")
--basic distance check for each tile, placing an island around the world origin
for i = 1, regionAPI.GetWidth(r) do
for j = 1, regionAPI.GetHeight(r) do
local dist = mapMaker.Dist(0, 0, i + regionAPI.GetX(r) -1, j + regionAPI.GetY(r) -1)
for i = 1, Region.GetWidth(r) do
for j = 1, Region.GetHeight(r) do
local dist = mapMaker.Dist(0, 0, i + Region.GetX(r) -1, j + Region.GetY(r) -1)
if dist < 10 then
regionAPI.SetTile(r, i, j, 1, mapMaker.plains)
Region.SetTile(r, i, j, 1, mapMaker.plains)
elseif dist < 12 then
regionAPI.SetTile(r, i, j, 1, mapMaker.sand)
Region.SetTile(r, i, j, 1, mapMaker.sand)
else
regionAPI.SetTile(r, i, j, 1, mapMaker.water)
regionAPI.SetSolid(r, i, j, true)
Region.SetTile(r, i, j, 1, mapMaker.water)
Region.SetSolid(r, i, j, true)
end
end
end
--examples of the smoothing function NOT working correctly
--[[
for j = 1, regionAPI.GetHeight(r) do
regionAPI.SetTile(r, 3, j, 1, mapMaker.dirt)
regionAPI.SetTile(r, 4, j, 1, mapMaker.dirt)
for j = 1, Region.GetHeight(r) do
Region.SetTile(r, 3, j, 1, mapMaker.dirt)
Region.SetTile(r, 4, j, 1, mapMaker.dirt)
regionAPI.SetTile(r, 10, j, 1, mapMaker.dirt)
Region.SetTile(r, 10, j, 1, mapMaker.dirt)
end
--]]
@@ -123,19 +92,4 @@ function mapMaker.DebugIsland(r)
mapMaker.SmoothEdgesSimple(r)
end
function mapMaker.DebugGrassland(r)
--debug
io.write("map_maker:DebugGrassland(", regionAPI.GetX(r), ", ", regionAPI.GetY(r), ")\n")
--all dirt
for i = 1, regionAPI.GetWidth(r) do
for j = 1, regionAPI.GetHeight(r) do
regionAPI.SetTile(r, i, j, 1, mapMaker.grass)
end
end
--A generic edge system
-- mapMaker.SmoothEdgesSimple(r)
end
return mapMaker
+4 -28
View File
@@ -1,39 +1,15 @@
--[[
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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.
*/
--]]
local region = require("region")
local Region = require("map_system").Region
local mapSaver = {}
function mapSaver.Load(r)
--empty
-- io.write("map_saver:Load(", region.GetX(r), ", ", region.GetY(r), ")\n")
io.write("map_saver:Load(", Region.GetX(r), ", ", Region.GetY(r), ")\n")
end
function mapSaver.Save(r)
--empty
-- io.write("map_saver:Save(", region.GetX(r), ", ", region.GetY(r), ")\n")
io.write("map_saver:Save(", Region.GetX(r), ", ", Region.GetY(r), ")\n")
end
--TODO: (3) create a flexible saving & loading system
--TODO: create a flexible saving & loading system
return mapSaver
+10 -53
View File
@@ -1,61 +1,18 @@
--[[
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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.
*/
--]]
--DOCS: This script is run by the server on startup
print("Lua script check")
--requirements
roomManagerAPI = require("room_manager")
roomAPI = require("room")
mapMaker = require("map_maker")
mapSaver = require("map_saver")
roomSystem = require("room_system")
doorUtility = require("door_utility")
--test the room hooks
roomManagerAPI.SetOnCreate(function(room, index)
print("", "Creating room: ", roomAPI.GetName(room), index)
roomAPI.SetOnTick(room, function(room)
roomAPI.ForEachCharacter(room, function(character)
--
end)
end)
end)
roomManagerAPI.SetOnUnload(function(room, index)
print("", "Unloading room: ", roomAPI.GetName(room), index)
end)
local function dumpTable(t)
print(t)
for k, v in pairs(t) do
print("",k,v)
end
end
--NOTE: room 0 is the first that the client asks for, therefore it must exist
local overworld, uidOne = roomManagerAPI.CreateRoom("overworld", "overworld.bmp")
roomAPI.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save)
local overworld, uid = roomSystem.RoomManager.CreateRoom("overworld", "overworld.bmp")
roomSystem.Room.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save)
local underworld, uidTwo = roomManagerAPI.CreateRoom("underworld", "overworld.bmp")
roomAPI.Initialize(underworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugGrassland, mapSaver.Save)
--call the monstrosity
doorUtility.createDoorPair("pair 1", overworld, -64, -64, underworld, 64, 64)
print("Finished the lua script")
+14 -67
View File
@@ -1,30 +1,10 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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.
*/
--TODO: An archive table of all dead characters
CREATE TABLE IF NOT EXISTS UserAccounts (
CREATE TABLE IF NOT EXISTS Accounts (
uid INTEGER PRIMARY KEY AUTOINCREMENT,
username varchar(100) UNIQUE, --TODO: (3) Swap username for email address
username varchar(100) UNIQUE,
--server-client security
--TODO: server-client security
-- passhash varchar(100),
-- passsalt varchar(100),
@@ -35,7 +15,7 @@ CREATE TABLE IF NOT EXISTS UserAccounts (
admin BIT DEFAULT 0
);
CREATE TABLE IF NOT EXISTS LiveCharacters (
CREATE TABLE IF NOT EXISTS Characters (
uid INTEGER PRIMARY KEY AUTOINCREMENT,
--metadata
@@ -44,52 +24,19 @@ CREATE TABLE IF NOT EXISTS LiveCharacters (
avatar varchar(100),
birth timestamp NOT NULL DEFAULT (datetime()),
--physically exists in the world
--position in the world
roomIndex INTEGER DEFAULT 0,
originX INTEGER DEFAULT 0,
originY INTEGER DEFAULT 0,
boundsX INTEGER DEFAULT 0,
boundsY INTEGER DEFAULT 0,
boundsW INTEGER DEFAULT 0,
boundsH INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS DeadCharacters (
uid INTEGER PRIMARY KEY,
--statistics
baseStats INTEGER REFERENCES StatisticSets(uid),
--metadata
owner INTEGER REFERENCES Accounts(uid),
handle varchar(100),
avatar varchar(100),
birth timestamp NOT NULL
);
CREATE TABLE IF NOT EXISTS LiveMonsters (
uid INTEGER PRIMARY KEY AUTOINCREMENT,
--metadata
handle varchar(100) UNIQUE,
avatar varchar(100),
--actions
-- script
--physically exists in the world
roomIndex INTEGER DEFAULT 0,
originX INTEGER DEFAULT 0,
originY INTEGER DEFAULT 0,
boundsX INTEGER DEFAULT 0,
boundsY INTEGER DEFAULT 0,
boundsW INTEGER DEFAULT 0,
boundsH INTEGER DEFAULT 0
);
CREATE TABLE IF NOT EXISTS DeadMonsters (
uid INTEGER PRIMARY KEY,
--metadata
handle varchar(100) UNIQUE,
avatar varchar(100)
--equipment
weapon INTEGER REFERENCES WornEquipment(uid),
helmet INTEGER REFERENCES WornEquipment(uid),
armour INTEGER REFERENCES WornEquipment(uid)
--etc.
);
-------------------------
@@ -154,5 +101,5 @@ CREATE TABLE IF NOT EXISTS WornEquipment (
--unique information
durability INTEGER DEFAULT 0,
stats INTEGER REFERENCES StatisticSets(uid)
--attached script?
--TODO: attached script?
);
+1 -1
View File
@@ -47,7 +47,7 @@ private:
int clientIndex;
std::string username;
//password/auth token
//TODO: password
//bit fields?
bool blackListed = false;
+10 -26
View File
@@ -27,32 +27,16 @@
//Define the queries
//-------------------------
static const char* CREATE_USER_ACCOUNT = "INSERT INTO UserAccounts (username) VALUES (?);";
static const char* LOAD_USER_ACCOUNT = "SELECT "
"uid, "
"blacklisted, "
"whitelisted, "
"mod, "
"admin "
" FROM UserAccounts WHERE username = ?;";
static const char* SAVE_USER_ACCOUNT = "UPDATE OR FAIL UserAccounts SET "
"blacklisted = ?2, "
"whitelisted = ?3, "
"mod = ?4, "
"admin = ?5 "
"WHERE uid = ?1;";
static const char* DELETE_USER_ACCOUNT = "DELETE FROM UserAccounts WHERE uid = ?;";
static const char* COUNT_USER_ACCOUNT_RECORDS = "SELECT COUNT(*) FROM UserAccounts;";
static const char* CREATE_USER_ACCOUNT = "INSERT INTO Accounts (username) VALUES (?);";
static const char* LOAD_USER_ACCOUNT = "SELECT * FROM Accounts WHERE username = ?;";
static const char* SAVE_USER_ACCOUNT = "UPDATE OR FAIL Accounts SET blacklisted = ?2, whitelisted = ?3, mod = ?4, admin = ?5 WHERE uid = ?1;";
static const char* DELETE_USER_ACCOUNT = "DELETE FROM Accounts WHERE uid = ?;";
static const char* COUNT_USER_ACCOUNT_RECORDS = "SELECT COUNT(*) FROM Accounts;";
//-------------------------
//Define the public methods
//-------------------------
//TODO: (1) block blacklisted accounts
int AccountManager::Create(std::string username, int clientIndex) {
//create this user account, failing if it exists, leave this account in memory
sqlite3_stmt* statement = nullptr;
@@ -110,11 +94,11 @@ int AccountManager::Load(std::string username, int clientIndex) {
//extract the data into memory
AccountData& newAccount = elementMap[uid];
newAccount.username = username;
newAccount.blackListed = sqlite3_column_int(statement, 1);
newAccount.whiteListed = sqlite3_column_int(statement, 2);
newAccount.mod = sqlite3_column_int(statement, 3);
newAccount.admin = sqlite3_column_int(statement, 4);
newAccount.username = reinterpret_cast<const char*>(sqlite3_column_text(statement, 1));
newAccount.blackListed = sqlite3_column_int(statement, 2);
newAccount.whiteListed = sqlite3_column_int(statement, 3);
newAccount.mod = sqlite3_column_int(statement, 4);
newAccount.admin = sqlite3_column_int(statement, 5);
newAccount.clientIndex = clientIndex;
//finish the routine
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
-96
View File
@@ -22,108 +22,12 @@
#include "character_api.hpp"
#include "character_data.hpp"
#include "character_manager.hpp"
#include "entity_api.hpp"
#include "room_manager.hpp"
#include "server_utilities.hpp"
#include <stdexcept>
static int setRoom(lua_State* L) {
//reverse engineer the character index
int characterIndex = -1;
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
CharacterManager& characterMgr = CharacterManager::GetSingleton();
for (auto& it : *characterMgr.GetContainer()) {
if (character == &it.second) {
characterIndex = it.first;
break;
}
}
//error checking
if (characterIndex == -1) {
throw(std::runtime_error("Lua Error: Failed to find character index by reference"));
}
//get the room index, depending on the parameter type
int roomIndex = -1;
RoomManager& roomMgr = RoomManager::GetSingleton();
switch(lua_type(L, 2)) {
case LUA_TNUMBER:
roomIndex = lua_tointeger(L, 2);
break;
case LUA_TLIGHTUSERDATA:
//reverse engineer the room index
for (auto& it : *roomMgr.GetContainer()) {
if (lua_touserdata(L, 2) == &it.second) {
roomIndex = it.first;
break;
}
}
break;
}
//error checking
if (roomIndex == -1) {
throw(std::runtime_error("Lua Error: Failed to find room index by reference"));
}
//send the delete & create messages
pumpAndChangeRooms(character, roomIndex, characterIndex);
return 0;
}
static int getOwner(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushinteger(L, character->GetOwner());
return 1;
}
static int getHandle(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushstring(L, character->GetHandle().c_str());
return 1;
}
static int getAvatar(lua_State* L) {
CharacterData* character = static_cast<CharacterData*>(lua_touserdata(L, 1));
lua_pushstring(L, character->GetAvatar().c_str());
return 1;
}
static const luaL_Reg characterLib[] = {
{"SetRoom", setRoom},
// {"GetOwner", getOwner}, //unusable without account API
{"GetHandle", getHandle},
{"GetAvatar", getAvatar},
{nullptr, nullptr}
};
LUAMOD_API int openCharacterAPI(lua_State* L) {
//get the parent table
luaL_requiref(L, TORTUGA_ENTITY_API, openEntityAPI, false);
//the local table
luaL_newlib(L, characterLib);
//merge the local table into the parent table
lua_pushnil(L); //first key
while(lua_next(L, -2)) {
//copy the key-value pair
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
//push the copy to the parent table
lua_settable(L, -6);
//pop the original value before continuing
lua_pop(L, 1);
}
//remove the local table, leaving the expanded parent table
lua_pop(L, 1);
return 1;
}
-4
View File
@@ -21,10 +21,6 @@
*/
#include "character_data.hpp"
CharacterData::CharacterData(): Entity("character") {
//EMPTY
}
int CharacterData::GetOwner() {
return owner;
}
+5 -2
View File
@@ -32,9 +32,12 @@
class CharacterData: public Entity {
public:
CharacterData();
CharacterData() = default;
~CharacterData() = default;
//accessors and mutators
//...
//database stuff
int GetOwner();
std::string GetHandle();
@@ -43,7 +46,7 @@ public:
private:
friend class CharacterManager;
int owner = -1;
int owner;
std::string handle;
std::string avatar;
};
+8 -69
View File
@@ -23,56 +23,18 @@
#include "sqlite3.h"
#include "character_defines.hpp"
#include <algorithm>
#include <cstring>
#include <stdexcept>
//-------------------------
//Define the queries
//-------------------------
//NOTE: Programmer set variables are NOT zero-indexed
//NOTE: SQLite3 returned variables (i.e. loading) ARE zero-indexed
static const char* CREATE_CHARACTER = "INSERT INTO LiveCharacters ("
"owner, "
"handle, "
"avatar, "
"boundsX, "
"boundsY, "
"boundsW, "
"boundsH"
") VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);";
static const char* LOAD_CHARACTER = "SELECT "
"uid, "
"owner, "
"handle, "
"avatar, "
"roomIndex, "
"originX, "
"originY, "
"boundsX, "
"boundsY, "
"boundsW, "
"boundsH "
"FROM LiveCharacters WHERE handle = ?;";
static const char* SAVE_CHARACTER = "UPDATE OR FAIL LiveCharacters SET "
"roomIndex = ?2, "
"originX = ?3, "
"originY = ?4, "
"boundsX = ?5, "
"boundsY = ?6, "
"boundsW = ?7, "
"boundsH = ?8 "
"WHERE uid = ?1;";
static const char* DELETE_CHARACTER = "DELETE FROM LiveCharacters WHERE uid = ?;";
static const char* COUNT_CHARACTER_RECORDS = "SELECT COUNT(*) FROM LiveCharacters;";
static const char* CREATE_CHARACTER = "INSERT INTO Characters (owner, handle, avatar) VALUES (?, ?, ?);";
static const char* LOAD_CHARACTER = "SELECT * FROM Characters WHERE handle = ?;";
static const char* SAVE_CHARACTER = "UPDATE OR FAIL Characters SET roomIndex = ?2, originX = ?3, originY = ?4 WHERE uid = ?1;";
static const char* DELETE_CHARACTER = "DELETE FROM Characters WHERE uid = ?;";
static const char* COUNT_CHARACTER_RECORDS = "SELECT COUNT(*) FROM Characters;";
//-------------------------
//Define the methods
@@ -93,10 +55,6 @@ int CharacterManager::Create(int owner, std::string handle, std::string avatar)
ret |= sqlite3_bind_int(statement, 1, owner);
ret |= sqlite3_bind_text(statement, 2, handle.c_str(), handle.size() + 1, SQLITE_STATIC);
ret |= sqlite3_bind_text(statement, 3, avatar.c_str(), avatar.size() + 1, SQLITE_STATIC);
ret |= sqlite3_bind_int(statement, 4, CHARACTER_BOUNDS_X);
ret |= sqlite3_bind_int(statement, 5, CHARACTER_BOUNDS_Y);
ret |= sqlite3_bind_int(statement, 6, CHARACTER_BOUNDS_WIDTH);
ret |= sqlite3_bind_int(statement, 7, CHARACTER_BOUNDS_HEIGHT);
//check for binding errors
if (ret) {
@@ -163,14 +121,9 @@ int CharacterManager::Load(int owner, std::string handle, std::string avatar) {
//Don't cache the birth
//world origin
newChar.roomIndex = sqlite3_column_int(statement, 4);
newChar.origin.x = (double)sqlite3_column_int(statement, 5);
newChar.origin.y = (double)sqlite3_column_int(statement, 6);
//bounds
newChar.bounds.x = (int)sqlite3_column_int(statement, 7);
newChar.bounds.y = (int)sqlite3_column_int(statement, 8);
newChar.bounds.w = (int)sqlite3_column_int(statement, 9);
newChar.bounds.h = (int)sqlite3_column_int(statement, 10);
newChar.roomIndex = sqlite3_column_int(statement, 5);
newChar.origin.x = (double)sqlite3_column_int(statement, 6);
newChar.origin.y = (double)sqlite3_column_int(statement, 7);
//gameplay components: equipment, items, buffs, debuffs...
@@ -212,10 +165,6 @@ int CharacterManager::Save(int uid) {
ret |= sqlite3_bind_int(statement, 2, character.roomIndex) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 3, (int)character.origin.x) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 4, (int)character.origin.y) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 5, character.bounds.x) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 6, character.bounds.y) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 7, character.bounds.w) != SQLITE_OK;
ret |= sqlite3_bind_int(statement, 8, character.bounds.h) != SQLITE_OK;
//gameplay components: equipment, items, buffs, debuffs...
@@ -244,7 +193,6 @@ void CharacterManager::Unload(int uid) {
}
void CharacterManager::Delete(int uid) {
//TODO: when deleting a character, move it to an archive table
//delete this character from the database, then remove it from memory
sqlite3_stmt* statement = nullptr;
@@ -304,15 +252,6 @@ CharacterData* CharacterManager::Get(int uid) {
return &it->second;
}
CharacterData* CharacterManager::Get(std::string handle) {
for (std::map<int, CharacterData>::iterator it = elementMap.begin(); it != elementMap.end(); ++it) {
if (it->second.GetHandle() == handle) {
return &it->second;
}
}
return nullptr;
}
int CharacterManager::GetLoadedCount() {
return elementMap.size();
}
-5
View File
@@ -44,18 +44,13 @@ public:
//accessors and mutators
CharacterData* Get(int uid);
CharacterData* Get(std::string handle);
int GetLoadedCount();
int GetTotalCount();
std::map<int, CharacterData>* GetContainer();
//API interface
sqlite3* SetDatabase(sqlite3* db);
sqlite3* GetDatabase();
//hooks
//TODO: character hooks
private:
friend Singleton<CharacterManager>;
@@ -23,88 +23,7 @@
#include "character_manager.hpp"
#include <sstream>
#include <stdexcept>
//TODO: (1) character hooks?
static int setOnCreate(lua_State* L) {
//TODO: (9) setOnCreate()
}
static int setOnLoad(lua_State* L) {
//TODO: (9) setOnLoad()
}
static int setOnSave(lua_State* L) {
//TODO: (9) setOnSave()
}
static int setOnUnload(lua_State* L) {
//TODO: (9) setOnUnload()
}
static int setOnDelete(lua_State* L) {
//TODO: (9) setOnDelete()
}
static int getCharacter(lua_State* L) {
//integer vs name
CharacterManager& characterMgr = CharacterManager::GetSingleton();
CharacterData* characterData = nullptr;
switch(lua_type(L, 1)) {
case LUA_TNUMBER:
characterData = characterMgr.Get(lua_tointeger(L, 1));
break;
case LUA_TSTRING:
//access characters via their handles
characterData = characterMgr.Get(lua_tostring(L, 1));
break;
}
if (characterData) {
lua_pushlightuserdata(L, static_cast<void*>(characterData));
}
else {
lua_pushnil(L);
}
return 1;
}
static int getLoadedCount(lua_State* L) {
CharacterManager& characterMgr = CharacterManager::GetSingleton();
lua_pushinteger(L, characterMgr.GetLoadedCount());
return 1;
}
static int forEach(lua_State* L) {
CharacterManager& characterMgr = CharacterManager::GetSingleton();
//pass each character to the given function
for (auto& it : *characterMgr.GetContainer()) {
lua_pushvalue(L, -1);
lua_pushlightuserdata(L, static_cast<void*>(&it.second));
//call each iteration, throwing an exception if something happened
if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
std::ostringstream os;
os << "Lua error: ";
os << lua_tostring(L, -1);
throw(std::runtime_error(os.str()));
}
}
return 0;
}
static const luaL_Reg characterManagerLib[] = {
// {"SetOnCreate", setOnCreate},
// {"SetOnLoad", setOnLoad},
// {"SetOnSave", setOnSave},
// {"SetOnUnload", setOnUnload},
// {"SetOnDelete", setOnDelete},
{"GetCharacter", getCharacter},
{"GetLoadedCount", getLoadedCount},
{"ForEach", forEach},
{nullptr, nullptr}
};
+6 -1
View File
@@ -1,5 +1,5 @@
#config
INCLUDES+=. ../entities ../monsters ../rooms ../server_utilities ../triggers ../../common/gameplay ../../common/map ../../common/network ../../common/network/packet_types ../../common/utilities
INCLUDES+=. ../entities ../server_utilities ../../common/gameplay ../../common/utilities
LIBS+=
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+1 -1
View File
@@ -22,7 +22,7 @@
#ifndef CLIENTDATA_HPP_
#define CLIENTDATA_HPP_
#include "SDL_net.h"
#include "SDL/SDL_net.h"
#include <chrono>
+8 -8
View File
@@ -21,15 +21,11 @@
*/
#include "client_manager.hpp"
#include "ip_operators.hpp"
#include "udp_network_utility.hpp"
#include <chrono>
std::list<int> ClientManager::CheckConnections() {
//list of clients to disconnect
std::list<int> returnList;
int ClientManager::CheckConnections() {
for (auto& it : elementMap) {
//3 seconds between beats
if (ClientData::Clock::now() - it.second.GetLastBeat() > std::chrono::seconds(3)) {
@@ -42,17 +38,21 @@ std::list<int> ClientManager::CheckConnections() {
for (auto& it : elementMap) {
if (it.second.GetAttempts() > 2) {
returnList.push_back(it.first);
int ret = it.first;
// elementMap.erase(it.first);
return ret;
}
}
return returnList;
return -1;
}
void ClientManager::HandlePong(ServerPacket* const argPacket) {
//find and update the specified client
for (auto& it : elementMap) {
if (it.second.GetAddress() == argPacket->srcAddress) {
if (it.second.GetAddress().host == argPacket->srcAddress.host &&
it.second.GetAddress().port == argPacket->srcAddress.port
) {
it.second.ResetAttempts();
return;
}
+2 -3
View File
@@ -26,16 +26,15 @@
#include "server_packet.hpp"
#include "singleton.hpp"
#include "SDL_net.h"
#include "SDL/SDL_net.h"
#include <functional>
#include <list>
#include <map>
class ClientManager: public Singleton<ClientManager> {
public:
//methods
std::list<int> CheckConnections();
int CheckConnections();
void HandlePong(ServerPacket* const argPacket);
//common public methods
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
-20
View File
@@ -21,14 +21,6 @@
*/
#include "entity.hpp"
Entity::Entity(const char* _type): type(_type) {
//EMPTY
}
void Entity::Update() {
origin += motion;
}
int Entity::SetRoomIndex(int i) {
return roomIndex = i;
}
@@ -41,10 +33,6 @@ Vector2 Entity::SetMotion(Vector2 v) {
return motion = v;
}
BoundingBox Entity::SetBounds(BoundingBox b) {
return bounds = b;
}
int Entity::GetRoomIndex() const {
return roomIndex;
}
@@ -55,12 +43,4 @@ Vector2 Entity::GetOrigin() const {
Vector2 Entity::GetMotion() const {
return motion;
}
BoundingBox Entity::GetBounds() const {
return bounds;
}
const char* Entity::GetType() const {
return type;
}
+3 -14
View File
@@ -22,38 +22,27 @@
#ifndef ENTITY_HPP_
#define ENTITY_HPP_
#include "bounding_box.hpp"
#include "vector2.hpp"
#include <string>
//The base class for all objects in the world
class Entity {
public:
virtual void Update();
//accessors & mutators
int SetRoomIndex(int i);
Vector2 SetOrigin(Vector2 v);
Vector2 SetMotion(Vector2 v);
BoundingBox SetBounds(BoundingBox b);
int GetRoomIndex() const;
Vector2 GetOrigin() const;
Vector2 GetMotion() const;
BoundingBox GetBounds() const;
const char* GetType() const;
protected:
Entity(const char* type);
Entity() = default;
virtual ~Entity() = default;
int roomIndex = -1;
Vector2 origin = {0, 0};
Vector2 motion = {0, 0};
BoundingBox bounds = {0, 0, 0, 0};
const char* type;
Vector2 origin;
Vector2 motion;
};
#endif
+2 -31
View File
@@ -41,17 +41,6 @@ static int setMotion(lua_State* L) {
return 0;
}
static int setBounds(lua_State* L) {
Entity* entity = static_cast<Entity*>(lua_touserdata(L, 1));
entity->SetBounds({
lua_tointeger(L, 2),
lua_tointeger(L, 3),
lua_tointeger(L, 4),
lua_tointeger(L, 5)
});
return 0;
}
static int getRoomIndex(lua_State* L) {
Entity* entity = static_cast<Entity*>(lua_touserdata(L, 1));
lua_pushinteger(L, entity->GetRoomIndex());
@@ -67,36 +56,18 @@ static int getOrigin(lua_State* L) {
static int getMotion(lua_State* L) {
Entity* entity = static_cast<Entity*>(lua_touserdata(L, 1));
lua_pushnumber(L, entity->GetMotion().x);
lua_pushnumber(L, entity->GetMotion().y);
lua_pushnumber(L, entity->GetOrigin().x);
lua_pushnumber(L, entity->GetOrigin().y);
return 2;
}
static int getBounds(lua_State* L) {
Entity* entity = static_cast<Entity*>(lua_touserdata(L, 1));
lua_pushinteger(L, entity->GetBounds().x);
lua_pushinteger(L, entity->GetBounds().y);
lua_pushinteger(L, entity->GetBounds().w);
lua_pushinteger(L, entity->GetBounds().h);
return 4;
}
static int getType(lua_State* L) {
Entity* entity = static_cast<Entity*>(lua_touserdata(L, 1));
lua_pushstring(L, entity->GetType());
return 1;
}
static const luaL_Reg entityLib[] = {
{"SetRoomIndex", setRoomIndex},
{"SetOrigin", setOrigin},
{"SetMotion", setMotion},
{"SetBounds", setBounds},
{"GetRoomIndex", getRoomIndex},
{"GetOrigin", getOrigin},
{"GetMotion", getMotion},
{"GetBounds", getBounds},
{"GetType", getType},
{nullptr, nullptr}
};
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+10 -23
View File
@@ -37,17 +37,10 @@
#include "lua.hpp"
#include "entity_api.hpp"
#include "character_api.hpp"
#include "character_manager_api.hpp"
#include "region_api.hpp"
#include "region_pager_api.hpp"
//#include "monster_api.hpp"
//#include "monster_manager_api.hpp"
#include "network_api.hpp"
#include "room_api.hpp"
#include "room_manager_api.hpp"
#include "trigger_api.hpp"
#include "trigger_manager_api.hpp"
#include "map_system_api.hpp"
#include "monster_system_api.hpp"
#include "room_system_api.hpp"
#include "waypoint_system_api.hpp"
//these libs are loaded by lua.c and are readily available to any Lua program
static const luaL_Reg loadedlibs[] = {
@@ -65,20 +58,14 @@ static const luaL_Reg loadedlibs[] = {
{NULL, NULL}
};
//these libs are preloaded and must be required before used
static const luaL_Reg preloadedlibs[] = {
{TORTUGA_ENTITY_API, openEntityAPI}, //required by derived classes
{TORTUGA_CHARACTER_API, openCharacterAPI},
{TORTUGA_CHARACTER_MANAGER_API, openCharacterManagerAPI},
// {TORTUGA_MONSTER_API, openMonsterAPI},
// {TORTUGA_MONSTER_MANAGER_API, openMonsterManagerAPI},
{TORTUGA_NETWORK_API, openNetworkAPI},
{TORTUGA_REGION_API, openRegionAPI},
{TORTUGA_REGION_PAGER_API, openRegionPagerAPI},
{TORTUGA_ROOM_API, openRoomAPI},
{TORTUGA_ROOM_MANAGER_API, openRoomManagerAPI},
{TORTUGA_TRIGGER_API, openTriggerAPI},
{TORTUGA_TRIGGER_MANAGER_API, openTriggerManagerAPI},
{TORTUGA_ENTITY_API, openEntityAPI},
{TORTUGA_MAP_SYSTEM_API, openMapSystemAPI},
{TORTUGA_MONSTER_SYSTEM_API, openMonsterSystemAPI},
{TORTUGA_ROOM_SYSTEM_API, openRoomSystemAPI},
{TORTUGA_WAYPOINT_SYSTEM_API, openWaypointSystemAPI},
{NULL, NULL}
};
+1
View File
@@ -28,6 +28,7 @@
#include "config_utility.hpp"
#include "room_manager.hpp"
#include "udp_network_utility.hpp"
#include "waypoint_manager.hpp"
#include <stdexcept>
#include <iostream>
+3 -13
View File
@@ -1,5 +1,5 @@
#include directories
INCLUDES+=SDL . accounts characters clients entities rooms server_utilities triggers ../common/debugging ../common/gameplay ../common/map ../common/network ../common/network/packet_types ../common/utilities
INCLUDES+=. accounts characters clients entities monsters rooms server_utilities waypoints ../common/debugging ../common/gameplay ../common/map ../common/network ../common/network/packet_types ../common/utilities
#libraries
#the order of the $(LIBS) is important, at least for MinGW
@@ -8,10 +8,6 @@ ifeq ($(OS),Windows_NT)
LIBS+=-lwsock32 -liphlpapi -lmingw32
endif
LIBS+=-lSDLmain -lSDL -llua -lsqlite3
ifeq ($(shell uname), Linux)
#I don't know what this does, but Ubuntu needs it
LIBS+=-ldl
endif
#flags
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
@@ -33,10 +29,10 @@ all: $(OBJ) $(OUT)
$(MAKE) -C characters
$(MAKE) -C clients
$(MAKE) -C entities
# $(MAKE) -C monsters
$(MAKE) -C monsters
$(MAKE) -C rooms
$(MAKE) -C server_utilities
$(MAKE) -C triggers
$(MAKE) -C waypoints
$(CXX) $(CXXFLAGS) -o $(OUT) $(OBJ) $(LIBS)
$(OBJ): | $(OBJDIR)
@@ -53,12 +49,6 @@ $(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
ifeq ($(OS),Windows_NT)
$(RM) *.o *.a *.exe
else ifeq ($(shell uname), Linux)
find . -type f -name *.o -exec rm -f -r -v {} \;
find . -type f -name *.a -exec rm -f -r -v {} \;
rm -f -v out/client out/server
endif
rebuild: clean all
+5
View File
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+7 -7
View File
@@ -28,7 +28,7 @@
static int setAvatar(lua_State* L) {
MonsterData* monster = static_cast<MonsterData*>(lua_touserdata(L, 1));
monster->SetAvatar(lua_tostring(L, 2));
//TODO: (1) send an update to the clients?
//TODO: send an update to the clients?
return 0;
}
@@ -61,27 +61,27 @@ static const luaL_Reg monsterLib[] = {
};
LUAMOD_API int openMonsterAPI(lua_State* L) {
//get the parent table
luaL_requiref(L, TORTUGA_ENTITY_API, openEntityAPI, false);
//the local table
luaL_newlib(L, monsterLib);
//merge the local table into the parent table
//get the parent table
luaL_requiref(L, TORTUGA_ENTITY_API, openEntityAPI, false);
//clone the parent table into the local table
lua_pushnil(L); //first key
while(lua_next(L, -2)) {
//copy the key-value pair
lua_pushvalue(L, -2);
lua_pushvalue(L, -2);
//push the copy to the parent table
//push the copy to the local table
lua_settable(L, -6);
//pop the original value before continuing
lua_pop(L, 1);
}
//remove the local table, leaving the expanded parent table
//remove the parent table, leaving the expanded child table
lua_pop(L, 1);
return 1;
-17
View File
@@ -21,23 +21,6 @@
*/
#include "monster_data.hpp"
MonsterData::MonsterData(std::string _avatar, int _scriptRef):
Entity("monster"),
avatar(_avatar),
scriptRef(_scriptRef)
{
//EMPTY
}
void MonsterData::Update() {
Entity::Update();
//TODO: (0) call the script reference
}
//-------------------------
//accessors & mutators
//-------------------------
std::string MonsterData::SetAvatar(std::string s) {
return avatar = s;
}
+2 -15
View File
@@ -24,26 +24,13 @@
#include "entity.hpp"
#include "lua.hpp"
#include <string>
/* DOCS: Monster attributes, read more
* species (avatar, script)
* level
* health/mana
* permadeath/respawn
*/
class MonsterData: public Entity {
public:
MonsterData(std::string avatar, int scriptRef);
MonsterData() = default;
~MonsterData() = default;
virtual void Update();
//accessors & mutators
std::string SetAvatar(std::string);
std::string GetAvatar();
@@ -54,7 +41,7 @@ private:
friend class MonsterManager;
std::string avatar;
int scriptRef = LUA_NOREF;
int scriptRef;
};
#endif
+12 -32
View File
@@ -29,66 +29,46 @@ MonsterManager::~MonsterManager() {
UnloadAll();
}
int MonsterManager::Create(std::string avatar, int scriptRef) {
//implicitly create the new
elementMap.emplace(counter, MonsterData(avatar, scriptRef));
//TODO: do various things like saving to the database
return counter++;
int MonsterManager::Create(std::string) {
//TODO
}
//TODO: (1) monster load, save
void MonsterManager::Unload(int uid) {
elementMap.erase(uid);
//TODO
}
void MonsterManager::UnloadAll() {
elementMap.clear();
//TODO
}
void MonsterManager::UnloadIf(std::function<bool(std::pair<const int, MonsterData const&>)> fn) {
std::map<int, MonsterData>::iterator it = elementMap.begin();
while (it != elementMap.end()) {
if (fn(*it)) {
it = elementMap.erase(it);
}
else {
++it;
}
}
//TODO
}
MonsterData* MonsterManager::Get(int uid) {
std::map<int, MonsterData>::iterator it = elementMap.find(uid);
if (it == elementMap.end()) {
return nullptr;
}
return &it->second;
//TODO
}
int MonsterManager::GetLoadedCount() {
return elementMap.size();
//TODO
}
std::map<int, MonsterData>* MonsterManager::GetContainer() {
return &elementMap;
//TODO
}
lua_State* MonsterManager::SetLuaState(lua_State* L) {
return lua = L;
//TODO
}
lua_State* MonsterManager::GetLuaState() {
return lua;
//TODO
}
sqlite3* MonsterManager::SetDatabase(sqlite3* db) {
return database = db;
//TODO
}
sqlite3* MonsterManager::GetDatabase() {
return database;
//TODO
}
+1 -2
View File
@@ -37,7 +37,7 @@ public:
~MonsterManager();
//common public methods
int Create(std::string avatar, int scriptRef);
int Create(std::string);
void Unload(int uid);
void UnloadAll();
@@ -57,7 +57,6 @@ public:
private:
//members
std::map<int, MonsterData> elementMap;
int counter = 0;
lua_State* lua = nullptr;
sqlite3* database = nullptr;
};
-47
View File
@@ -23,54 +23,7 @@
#include "monster_manager.hpp"
static int create(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
int index = mgr->Create(lua_tostring(L, 2), lua_tointeger(L, 3));
MonsterData* monster = mgr->Get(index);
lua_pushlightuserdata(L, static_cast<void*>(monster));
lua_pushinteger(L, index);
return 2;
}
//TOOD: this needs to take the userdata as a parameter too
static int unload(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
mgr->Unload(lua_tointeger(L, 2));
return 0;
}
static int unloadAll(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
mgr->UnloadAll();
return 0;
}
static int unloadIf(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
//TODO: unloadIf
return 0;
}
static int get(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
MonsterData* monster = mgr->Get(lua_tointeger(L, 2));
lua_pushlightuserdata(L, static_cast<void*>(monster));
return 1;
}
static int getLoadedCount(lua_State* L) {
MonsterManager* mgr = static_cast<MonsterManager* const>(lua_touserdata(L, 1));
lua_pushinteger(L, mgr->GetLoadedCount());
return 1;
}
static const luaL_Reg monsterManagerLib[] = {
{"Create", create},
{"Unload", unload},
{"UnloadAll", unloadAll},
// {"UnloadIf", unloadIf},
{"Get", get},
{"GetLoadedCount", getLoadedCount},
{nullptr, nullptr}
};
+55
View File
@@ -0,0 +1,55 @@
/* Copyright: (c) Kayne Ruse 2013-2015
*
* 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 "monster_system_api.hpp"
//all monster API headers
#include "monster_api.hpp"
#include "monster_manager_api.hpp"
//useful "globals"
//...
//This mimics linit.c to create a nested collection of all monster modules.
static const luaL_Reg funcs[] = {
{nullptr, nullptr}
};
static const luaL_Reg libs[] = {
{"Monster", openMonsterAPI},
{"MonsterManager", openMonsterManagerAPI},
{nullptr, nullptr}
};
int openMonsterSystemAPI(lua_State* L) {
//create the table
luaL_newlibtable(L, libs);
//push the "global" functions
luaL_setfuncs(L, funcs, 0);
//push the substable
for (const luaL_Reg* lib = libs; lib->func; lib++) {
lib->func(L);
lua_setfield(L, -2, lib->name);
}
return 1;
}
@@ -19,12 +19,12 @@
* 3. This notice may not be removed or altered from any source
* distribution.
*/
#ifndef TRIGGERMANAGERAPI_HPP_
#define TRIGGERMANAGERAPI_HPP_
#ifndef MONSTERSYSTEMAPI_HPP_
#define MONSTERSYSTEMAPI_HPP_
#include "lua.hpp"
#define TORTUGA_TRIGGER_MANAGER_API "trigger_manager"
LUAMOD_API int openTriggerManagerAPI(lua_State* L);
#define TORTUGA_MONSTER_SYSTEM_API "monster_system"
LUAMOD_API int openMonsterSystemAPI(lua_State* L);
#endif
+6 -1
View File
@@ -1,5 +1,5 @@
#config
INCLUDES+=. ../characters ../entities ../monsters ../server_utilities ../triggers ../../common/gameplay ../../common/map ../../common/utilities
INCLUDES+=. ../characters ../entities ../monsters ../server_utilities ../waypoints ../../common/gameplay ../../common/map ../../common/utilities
LIBS+=
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
@@ -30,3 +30,8 @@ $(OUTDIR):
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $<
clean:
$(RM) *.o *.a *.exe
rebuild: clean all
+5 -65
View File
@@ -23,9 +23,6 @@
#include "room_data.hpp"
#include <sstream>
#include <stdexcept>
static int setRoomName(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
room->SetName(lua_tostring(L, 2));
@@ -56,71 +53,20 @@ static int getPager(lua_State* L) {
return 1;
}
/*
static int getMonsterMgr(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
lua_pushlightuserdata(L, reinterpret_cast<void*>(room->GetMonsterMgr()) );
return 1;
}
*/
static int getTriggerMgr(lua_State* L) {
static int getWaypointMgr(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
lua_pushlightuserdata(L, reinterpret_cast<void*>(room->GetTriggerMgr()) );
return 1;
}
static int forEachCharacter(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
//pass each character to the given function
for (auto& it : *room->GetCharacterList()) {
lua_pushvalue(L, -1);
lua_pushlightuserdata(L, static_cast<void*>(it));
//call each iteration, throwing an exception if something happened
if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
std::ostringstream os;
os << "Lua error: ";
os << lua_tostring(L, -1);
throw(std::runtime_error(os.str()));
}
}
return 0;
}
/*
static int forEachMonster(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
MonsterManager* monsterMgr = room->GetMonsterMgr();
//pass each monster to the given function
for (auto& it : *monsterMgr->GetContainer()) {
lua_pushvalue(L, -1);
lua_pushlightuserdata(L, static_cast<void*>(&it.second));
//call each iteration, throwing an exception if something happened
if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
std::ostringstream os;
os << "Lua error: ";
os << lua_tostring(L, -1);
throw(std::runtime_error(os.str()));
}
}
return 0;
}
*/
static int setOnTick(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
luaL_unref(L, LUA_REGISTRYINDEX, room->GetTickReference());
room->SetTickReference(luaL_ref(L, LUA_REGISTRYINDEX));
return 0;
}
static int getOnTick(lua_State* L) {
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
lua_rawgeti(L, LUA_REGISTRYINDEX, room->GetTickReference());
lua_pushlightuserdata(L, reinterpret_cast<void*>(room->GetWaypointMgr()) );
return 1;
}
static int initialize(lua_State* L) {
//TODO: This could fit into the room system's globals
RoomData* room = static_cast<RoomData*>(lua_touserdata(L, 1));
//set the refs of these parameters (backwards, since it pops from the top of the stack)
@@ -140,14 +86,8 @@ static const luaL_Reg roomLib[] = {
{"GetTileset", getTilesetName},
{"GetPager",getPager},
// {"GetMonsterMgr",getMonsterMgr},
{"GetTriggerMgr",getTriggerMgr},
{"ForEachCharacter", forEachCharacter},
// {"ForEachMonster", forEachMonster},
{"SetOnTick", setOnTick},
{"GetOnTick", getOnTick},
{"GetMonsterMgr",getMonsterMgr},
{"GetWaypointMgr",getWaypointMgr},
{"Initialize", initialize},
{nullptr, nullptr}
+6 -111
View File
@@ -21,87 +21,6 @@
*/
#include "room_data.hpp"
#include <algorithm>
#include <iostream>
#include <stack>
#include <stdexcept>
//TODO: (9) character collisions should be preformed client-side
void RoomData::RunFrame() {
//get the hook
lua_rawgeti(lua, LUA_REGISTRYINDEX, tickRef);
if (!lua_isnil(lua, -1)) {
//call the tick function, with this as a parameter
lua_pushlightuserdata(lua, this);
if (lua_pcall(lua, 1, 0, 0) != LUA_OK) {
throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) ));
}
}
else {
lua_pop(lua, 1);
}
//update the entities in the room
for (auto& it : characterList) {
it->Update();
}
//TODO: (3) iterate through the monster map
//TODO: (3) trigger script for monsters
//build a list of game entities
std::stack<Entity*> entityStack;
for (auto& it : characterList) {
entityStack.push(it);
}
//TODO: (3) push the monster entities
//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();
//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();
}
}
std::string RoomData::SetName(std::string s) {
return roomName = s;
}
@@ -122,38 +41,14 @@ RegionPagerLua* RoomData::GetPager() {
return &pager;
}
TriggerManager* RoomData::GetTriggerMgr() {
return &triggerMgr;
MonsterManager* RoomData::GetMonsterMgr() {
return &monsterMgr;
}
WaypointManager* RoomData::GetWaypointMgr() {
return &waypointMgr;
}
std::list<CharacterData*>* RoomData::GetCharacterList() {
return &characterList;
}
lua_State* RoomData::SetLuaState(lua_State* L) {
lua = L;
pager.SetLuaState(lua);
triggerMgr.SetLuaState(lua);
return lua;
}
lua_State* RoomData::GetLuaState() {
return lua;
}
sqlite3* RoomData::SetDatabase(sqlite3* db) {
database = db;
return database;
}
sqlite3* RoomData::GetDatabase() {
return database;
}
int RoomData::SetTickReference(int i) {
return tickRef = i;
}
int RoomData::GetTickReference() {
return tickRef;
}
+10 -25
View File
@@ -23,11 +23,11 @@
#define ROOMDATA_HPP_
#include "character_data.hpp"
#include "monster_manager.hpp"
#include "region_pager_lua.hpp"
#include "trigger_manager.hpp"
#include "waypoint_manager.hpp"
#include "lua.hpp"
#include "sqlite3.h"
#include <list>
#include <string>
@@ -37,8 +37,6 @@ public:
RoomData() = default;
~RoomData() = default;
void RunFrame();
//accessors and mutators
std::string SetName(std::string);
std::string GetName();
@@ -47,36 +45,23 @@ public:
std::string GetTileset();
RegionPagerLua* GetPager();
TriggerManager* GetTriggerMgr();
MonsterManager* GetMonsterMgr();
WaypointManager* GetWaypointMgr();
std::list<CharacterData*>* GetCharacterList();
//API interfaces
lua_State* SetLuaState(lua_State* L);
lua_State* GetLuaState();
sqlite3* SetDatabase(sqlite3* db);
sqlite3* GetDatabase();
//hooks
int SetTickReference(int i);
int GetTickReference();
//TODO: other triggers like player entry & exit, etc.
//TODO: triggers for unload, save, per-second, player enter, player exit, etc.
private:
//metadata
friend class RoomManager;
//members
std::string roomName;
std::string tilesetName;
//members
RegionPagerLua pager;
TriggerManager triggerMgr;
MonsterManager monsterMgr;
WaypointManager waypointMgr;
std::list<CharacterData*> characterList;
//API
lua_State* lua = nullptr;
sqlite3* database = nullptr;
//hooks
int tickRef = LUA_NOREF;
};
#endif
+8 -82
View File
@@ -35,79 +35,29 @@ int RoomManager::Create(std::string roomName, std::string tileset) {
newRoom->SetName(roomName);
newRoom->SetTileset(tileset);
newRoom->SetLuaState(lua);
newRoom->SetDatabase(database);
//get the hook
lua_rawgeti(lua, LUA_REGISTRYINDEX, createRef);
if(!lua_isnil(lua, -1)) {
lua_pushlightuserdata(lua, newRoom);
lua_pushinteger(lua, counter);
//call the function, 2 arg, 0 return
if (lua_pcall(lua, 2, 0, 0) != LUA_OK) {
throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) ));
}
}
newRoom->pager.SetLuaState(lua);
newRoom->monsterMgr.SetLuaState(lua);
newRoom->monsterMgr.SetDatabase(database);
newRoom->waypointMgr.SetLuaState(lua);
//finish the routine
return counter++;
}
void RoomManager::UnloadAll() {
//get the hook
lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef);
if(!lua_isnil(lua, -1)) {
//pass each room to the hook
for (auto& it : elementMap) {
lua_pushvalue(lua, -1); //copy the hook
lua_pushlightuserdata(lua, &it.second);
lua_pushinteger(lua, it.first);
//call the function, 2 arg, 0 return
if (lua_pcall(lua, 2, 0, 0) != LUA_OK) {
throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) ));
}
}
}
//pop the hook or nil
lua_pop(lua, 1);
elementMap.clear();
}
void RoomManager::UnloadIf(std::function<bool(std::pair<const int, RoomData const&>)> fn) {
//get the hook
lua_rawgeti(lua, LUA_REGISTRYINDEX, unloadRef);
//get the element
std::map<int, RoomData>::iterator it = elementMap.begin();
//jenky pattern
while (it != elementMap.end()) {
if (fn(*it)) {
if(!lua_isnil(lua, -1)) {
lua_pushvalue(lua, -1); //copy the hook
lua_pushlightuserdata(lua, &it->second);
lua_pushinteger(lua, it->first);
//call the function, 2 arg, 0 return
if (lua_pcall(lua, 2, 0, 0) != LUA_OK) {
throw(std::runtime_error(std::string() + "Lua error: " + lua_tostring(lua, -1) ));
}
}
it = elementMap.erase(it);
}
else {
++it;
}
}
//pop the hook or nil
lua_pop(lua, 1);
}
void RoomManager::PushCharacter(CharacterData* character) {
@@ -121,7 +71,7 @@ void RoomManager::PushCharacter(CharacterData* character) {
throw(std::runtime_error("Failed to push an character to a non-existant room"));
}
room->GetCharacterList()->push_back(character);
room->characterList.push_back(character);
}
void RoomManager::PopCharacter(CharacterData const* character) {
@@ -136,7 +86,7 @@ void RoomManager::PopCharacter(CharacterData const* character) {
throw(std::runtime_error("Failed to pop an character to a non-existant room"));
}
room->GetCharacterList()->remove_if([character](CharacterData* ptr) {
room->characterList.remove_if([character](CharacterData* ptr) {
return character == ptr;
});
}
@@ -169,11 +119,7 @@ std::map<int, RoomData>* RoomManager::GetContainer() {
}
lua_State* RoomManager::SetLuaState(lua_State* L) {
lua = L;
for (auto& it : elementMap) {
it.second.SetLuaState(lua);
}
return lua;
return lua = L;
}
lua_State* RoomManager::GetLuaState() {
@@ -181,29 +127,9 @@ lua_State* RoomManager::GetLuaState() {
}
sqlite3* RoomManager::SetDatabase(sqlite3* db) {
database = db;
for (auto& it : elementMap) {
it.second.SetDatabase(database);
}
return database;
return database = db;
}
sqlite3* RoomManager::GetDatabase() {
return database;
}
int RoomManager::SetCreateReference(int i) {
return createRef = i;
}
int RoomManager::SetUnloadReference(int i) {
return unloadRef = i;
}
int RoomManager::GetCreateReference() {
return createRef;
}
int RoomManager::GetUnloadReference() {
return unloadRef;
}
+2 -15
View File
@@ -49,19 +49,12 @@ public:
int GetLoadedCount();
std::map<int, RoomData>* GetContainer();
//API interfaces
//hooks
lua_State* SetLuaState(lua_State* L);
lua_State* GetLuaState();
sqlite3* SetDatabase(sqlite3* db);
sqlite3* GetDatabase();
//hooks
int SetCreateReference(int i);
int SetUnloadReference(int i);
int GetCreateReference();
int GetUnloadReference();
private:
friend Singleton<RoomManager>;
@@ -70,15 +63,9 @@ private:
//members
std::map<int, RoomData> elementMap;
int counter = 0;
//API
lua_State* lua = nullptr;
sqlite3* database = nullptr;
//hooks
int createRef = LUA_NOREF;
int unloadRef = LUA_NOREF;
int counter = 0;
};
#endif

Some files were not shown because too many files have changed in this diff Show More