Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5757748cf | |||
| f4e4728ce3 | |||
| 65f23bbd1a | |||
| 453a211343 | |||
| 1e8f91a871 | |||
| 2a86a09693 | |||
| bd878e20ce | |||
| 92a02c7f0c | |||
| dacb8df674 | |||
| 7356e8ae77 | |||
| 9b2e78a68e | |||
| be90694234 | |||
| e2757a7628 | |||
| 42662c3f61 | |||
| e752dd7b0f | |||
| 44e24b667e | |||
| de6eb38516 | |||
| 74bf70c44d | |||
| cd06ccc1a5 | |||
| dc40ee64cf | |||
| de1cd8d6a8 | |||
| 1923f90329 | |||
| eeac329c49 | |||
| f13e8479e4 | |||
| cf1008f0d9 | |||
| dfae33cbd1 | |||
| 9f3721247d | |||
| d0b2f8e12f | |||
| 051ed0f14c | |||
| 8ea667a0b5 | |||
| b391dde089 | |||
| be67906218 | |||
| 70d4233a15 | |||
| 31fc5a8fa5 | |||
| 1973cfd061 | |||
| 3322783d95 | |||
| 9895e27d5a | |||
| 877c0f59d3 | |||
| 92eb75af7e | |||
| d815f17442 | |||
| 0344fe0d6d | |||
| a10636e067 | |||
| 4b8f9b4330 | |||
| c6981e6216 | |||
| 4b5011a579 |
@@ -20,6 +20,10 @@ The most recent stable build for Windows can be found [here](https://dl.dropboxu
|
||||
* [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 distribution
|
||||
|
||||
## Copyright
|
||||
|
||||
(Future versions (to be determined) may be released under a modified version of the [Uplink Developer's License](http://www.introversion.co.uk/uplink/developer/license.html).)
|
||||
|
||||
+7
-6
@@ -14,9 +14,10 @@ Both a game server and game client are included in this package.
|
||||
Instructions For Setup
|
||||
-------------------------
|
||||
|
||||
1. To create a server, simply run server.exe.
|
||||
2. To join that server, run client.exe with config settings not already in use.
|
||||
(Note: This process will be streamlined later).
|
||||
1. To create a server, simply run server.exe
|
||||
(a public server is provided by default)
|
||||
2. To join a server, your player information must be input into rsc/config.cfg
|
||||
(NOTE: This process will be streamlined later)
|
||||
3. To change the config settings, open rsc/config.cfg
|
||||
4. These settings must be unique for each player:
|
||||
|
||||
@@ -28,7 +29,7 @@ Instructions For Setup
|
||||
* client.avatar = elliot2.bmp #male
|
||||
* client.avatar = coa2.bmp #female
|
||||
|
||||
6. When you've correctly set these values (good luck), select 'Start' from the
|
||||
main menu; this displays the list of available servers.
|
||||
7. Select the name of your server (default is 'local') and select 'Join'.
|
||||
6. When you've correctly set these values, run client.exe, and select 'Start'
|
||||
from the main menu; this displays the list of available servers.
|
||||
7. Select the name of a server (default is 'Public') and select 'Join'.
|
||||
8. Welcome to Tortuga, enjoy your stay.
|
||||
|
||||
@@ -137,11 +137,17 @@ void ClientApplication::Proc() {
|
||||
realTime = Clock::now();
|
||||
|
||||
//simulate game time
|
||||
while (simTime < realTime) {
|
||||
//call each user defined function
|
||||
activeScene->RunFrame();
|
||||
//~60 FPS
|
||||
simTime += std::chrono::duration<int, std::milli>(16);
|
||||
if (simTime < realTime) {
|
||||
while (simTime < realTime) {
|
||||
//call each user defined function
|
||||
activeScene->RunFrame();
|
||||
//~60 FPS
|
||||
simTime += std::chrono::duration<int, std::milli>(16);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//give the machine a break
|
||||
SDL_Delay(10);
|
||||
}
|
||||
|
||||
//draw the game to the screen
|
||||
|
||||
@@ -19,30 +19,12 @@
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
*/
|
||||
#include "utility.hpp"
|
||||
#include "ip_operators.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
std::string truncatePath(std::string pathname) {
|
||||
return std::string(
|
||||
std::find_if(
|
||||
pathname.rbegin(),
|
||||
pathname.rend(),
|
||||
[](char ch) -> bool {
|
||||
//windows & unix tested
|
||||
return ch == '/' || ch == '\\';
|
||||
}).base(),
|
||||
pathname.end());
|
||||
bool operator==(IPaddress lhs, IPaddress rhs) {
|
||||
return lhs.host == rhs.host && lhs.port == rhs.port;
|
||||
}
|
||||
|
||||
std::string to_string_custom(int i) {
|
||||
char buffer[20];
|
||||
snprintf(buffer, 20, "%d", i);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
int to_integer_custom(std::string s) {
|
||||
int ret = 0;
|
||||
sscanf(s.c_str(), "%d", &ret);
|
||||
return ret;
|
||||
bool operator!=(IPaddress lhs, IPaddress rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
@@ -19,16 +19,13 @@
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
*/
|
||||
#ifndef UTILITY_HPP_
|
||||
#define UTILITY_HPP_
|
||||
#ifndef IPOPERATORS_HPP_
|
||||
#define IPOPERATORS_HPP_
|
||||
|
||||
#include <string>
|
||||
#include "SDL/SDL_net.h"
|
||||
|
||||
std::string truncatePath(std::string pathname);
|
||||
//these should've come standard
|
||||
bool operator==(IPaddress lhs, IPaddress rhs);
|
||||
bool operator!=(IPaddress lhs, IPaddress rhs);
|
||||
|
||||
//fixing known bugs in g++
|
||||
std::string to_string_custom(int i);
|
||||
|
||||
int to_integer_custom(std::string);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -96,12 +96,19 @@ protected:
|
||||
void HandleCharacterCreate(CharacterPacket* const);
|
||||
void HandleCharacterDelete(CharacterPacket* const);
|
||||
void HandleCharacterQueryExists(CharacterPacket* const);
|
||||
void HandleCharacterSetRoom(CharacterPacket* const);
|
||||
void HandleCharacterSetOrigin(CharacterPacket* const);
|
||||
void HandleCharacterSetMotion(CharacterPacket* const);
|
||||
void HandleCharacterMovement(CharacterPacket* const);
|
||||
void HandleCharacterAttack(CharacterPacket* const);
|
||||
|
||||
//monster management
|
||||
void HandleMonsterCreate(MonsterPacket* const);
|
||||
void HandleMonsterDelete(MonsterPacket* const);
|
||||
void HandleMonsterQueryExists(MonsterPacket* const);
|
||||
void HandleMonsterMovement(MonsterPacket* const);
|
||||
void HandleMonsterAttack(MonsterPacket* const);
|
||||
|
||||
//player movement
|
||||
void SendLocalCharacterMotion();
|
||||
void ProcessLocalCharacterMovement();
|
||||
void SendLocalCharacterMovement();
|
||||
std::list<BoundingBox> GenerateCollisionGrid(Entity*, int tileWidth, int tileHeight);
|
||||
|
||||
//indexes
|
||||
@@ -144,6 +151,7 @@ protected:
|
||||
//ugly references; I hate this
|
||||
ConfigUtility& config = ConfigUtility::GetSingleton();
|
||||
UDPNetworkUtility& network = UDPNetworkUtility::GetSingleton();
|
||||
Uint8* keyState = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
/* 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 <iostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
//-------------------------
|
||||
//character management
|
||||
//-------------------------
|
||||
|
||||
//DOCS: preexisting characters will result in query responses
|
||||
//DOCS: new characters will result in create messages
|
||||
//DOCS: this client's character will exist in both (skipped)
|
||||
|
||||
void InWorld::HandleCharacterCreate(CharacterPacket* const argPacket) {
|
||||
//prevent double message
|
||||
if (characterMap.find(argPacket->characterIndex) != characterMap.end()) {
|
||||
std::ostringstream msg;
|
||||
msg << "Double character creation event; ";
|
||||
msg << "Index: " << argPacket->characterIndex << "; ";
|
||||
msg << "Handle: " << argPacket->handle;
|
||||
throw(std::runtime_error(msg.str()));
|
||||
}
|
||||
|
||||
//implicity create and retrieve the entity
|
||||
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->CorrectSprite();
|
||||
|
||||
//check for this player's character
|
||||
if (character->GetOwner() == accountIndex) {
|
||||
localCharacter = static_cast<LocalCharacter*>(character);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//debug
|
||||
std::cout << "Create, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check for this player's character
|
||||
if ((*characterIt).second.GetOwner() == accountIndex) {
|
||||
localCharacter = nullptr;
|
||||
|
||||
//clear the camera
|
||||
camera.marginX = 0;
|
||||
camera.marginY = 0;
|
||||
|
||||
//clear the room
|
||||
roomIndex = -1;
|
||||
}
|
||||
|
||||
//remove this character
|
||||
characterMap.erase(characterIt);
|
||||
|
||||
//debug
|
||||
std::cout << "Delete, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterQueryExists(CharacterPacket* const argPacket) {
|
||||
//prevent a double message about this player's character
|
||||
if (argPacket->accountIndex == accountIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//ignore characters in a different room (sub-optimal)
|
||||
if (argPacket->roomIndex != roomIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//implicitly construct the character if it doesn't exist
|
||||
BaseCharacter* character = &characterMap[argPacket->characterIndex];
|
||||
|
||||
//set/update 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->CorrectSprite();
|
||||
|
||||
//debug
|
||||
std::cout << "Query, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterMovement(CharacterPacket* const argPacket) {
|
||||
//TODO: Authentication
|
||||
if (argPacket->characterIndex == characterIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check that this character exists
|
||||
std::map<int, BaseCharacter>::iterator characterIt = characterMap.find(argPacket->characterIndex);
|
||||
if (characterIt != characterMap.end()) {
|
||||
//set the origin and motion
|
||||
characterIt->second.SetOrigin(argPacket->origin);
|
||||
characterIt->second.SetMotion(argPacket->motion);
|
||||
characterIt->second.CorrectSprite();
|
||||
}
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterAttack(CharacterPacket* const argPacket) {
|
||||
//TODO: attack animation
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
//monster management
|
||||
//-------------------------
|
||||
|
||||
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;
|
||||
|
||||
newPacket.accountIndex = accountIndex;
|
||||
newPacket.characterIndex = characterIndex;
|
||||
newPacket.roomIndex = roomIndex;
|
||||
newPacket.origin = localCharacter->GetOrigin();
|
||||
newPacket.motion = localCharacter->GetMotion();
|
||||
|
||||
network.SendTo(Channels::SERVER, &newPacket);
|
||||
}
|
||||
|
||||
std::list<BoundingBox> InWorld::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) {
|
||||
//prepare for collisions
|
||||
BoundingBox wallBounds = {0, 0, tileWidth, tileHeight};
|
||||
std::list<BoundingBox> boxList;
|
||||
|
||||
//NOTE: for loops were too dense to work with, so I've just used while loops
|
||||
|
||||
//outer loop
|
||||
wallBounds.x = snapToBase((double)wallBounds.w, ptr->GetOrigin().x);
|
||||
while(wallBounds.x < (ptr->GetOrigin() + ptr->GetBounds()).x + ptr->GetBounds().w) {
|
||||
//inner loop
|
||||
wallBounds.y = snapToBase((double)wallBounds.h, ptr->GetOrigin().y);
|
||||
while(wallBounds.y < (ptr->GetOrigin() + ptr->GetBounds()).y + ptr->GetBounds().h) {
|
||||
//check to see if this tile is solid
|
||||
if (regionPager.GetSolid(wallBounds.x / wallBounds.w, wallBounds.y / wallBounds.h)) {
|
||||
//push onto the box set
|
||||
boxList.push_front(wallBounds);
|
||||
}
|
||||
|
||||
//increment
|
||||
wallBounds.y += wallBounds.h;
|
||||
}
|
||||
|
||||
//increment
|
||||
wallBounds.x += wallBounds.w;
|
||||
}
|
||||
|
||||
return std::move(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
#include directories
|
||||
INCLUDES+=. client_utilities entities 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
|
||||
@@ -27,7 +27,8 @@ OUT=$(addprefix $(OUTDIR)/,client)
|
||||
all: $(OBJ) $(OUT)
|
||||
$(MAKE) -C client_utilities
|
||||
$(MAKE) -C entities
|
||||
$(MAKE) -C scenes
|
||||
$(MAKE) -C gameplay_scenes
|
||||
$(MAKE) -C menu_scenes
|
||||
$(CXX) $(CXXFLAGS) -o $(OUT) $(OBJ) $(LIBS)
|
||||
|
||||
$(OBJ): | $(OBJDIR)
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
#include "lobby_menu.hpp"
|
||||
|
||||
#include "channels.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
//-------------------------
|
||||
//Public access members
|
||||
@@ -123,7 +123,9 @@ void LobbyMenu::Render(SDL_Surface* const screen) {
|
||||
font.DrawStringTo(serverInfo[i].name, screen, listBox.x, listBox.y + i*listBox.h);
|
||||
|
||||
//draw the player count
|
||||
font.DrawStringTo(to_string_custom(serverInfo[i].playerCount), screen, listBox.x + listBox.w, listBox.y + i*listBox.h);
|
||||
std::ostringstream msg;
|
||||
msg << serverInfo[i].playerCount;
|
||||
font.DrawStringTo(msg.str(), screen, listBox.x + listBox.w, listBox.y + i*listBox.h);
|
||||
|
||||
//compatible?
|
||||
if (!serverInfo[i].compatible) {
|
||||
@@ -164,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 {
|
||||
@@ -210,8 +212,11 @@ void LobbyMenu::HandlePacket(SerialPacket* const argPacket) {
|
||||
break;
|
||||
|
||||
//handle errors
|
||||
default:
|
||||
throw(std::runtime_error(std::string() + "Unknown SerialPacketType encountered in LobbyMenu: " + to_string_custom(static_cast<int>(argPacket->type)) ));
|
||||
default: {
|
||||
std::ostringstream msg;
|
||||
msg << "Unknown SerialPacketType encountered in LobbyMenu: " << static_cast<int>(argPacket->type);
|
||||
throw(std::runtime_error( msg.str() ));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,12 @@ void MainMenu::Render(SDL_Surface* const screen) {
|
||||
startButton.DrawTo(screen);
|
||||
optionsButton.DrawTo(screen);
|
||||
quitButton.DrawTo(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("https://github.com/Ratstail91/Tortuga", screen, 50, screen->h - 50 - image.GetClipH() * 0);
|
||||
//TODO: replace this with a website address
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
@@ -0,0 +1,37 @@
|
||||
#config
|
||||
INCLUDES+=. .. ../../common/graphics ../../common/map ../../common/network ../../common/network/packet_types ../../common/ui ../../common/utilities
|
||||
LIBS+=
|
||||
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
|
||||
|
||||
#source
|
||||
CXXSRC=$(wildcard *.cpp)
|
||||
|
||||
#objects
|
||||
OBJDIR=obj
|
||||
OBJ+=$(addprefix $(OBJDIR)/,$(CXXSRC:.cpp=.o))
|
||||
|
||||
#output
|
||||
OUTDIR=..
|
||||
OUT=$(addprefix $(OUTDIR)/,client.a)
|
||||
|
||||
#targets
|
||||
all: $(OBJ) $(OUT)
|
||||
ar -crs $(OUT) $(OBJ)
|
||||
|
||||
$(OBJ): | $(OBJDIR)
|
||||
|
||||
$(OUT): | $(OUTDIR)
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir $(OBJDIR)
|
||||
|
||||
$(OUTDIR):
|
||||
mkdir $(OUTDIR)
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
$(RM) *.o *.a *.exe
|
||||
|
||||
rebuild: clean all
|
||||
@@ -1,757 +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 "in_world.hpp"
|
||||
|
||||
#include "channels.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "terminal_error.hpp"
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
//-------------------------
|
||||
//these should've come standard
|
||||
//-------------------------
|
||||
|
||||
bool operator==(IPaddress lhs, IPaddress rhs) {
|
||||
return lhs.host == rhs.host && lhs.port == rhs.port;
|
||||
}
|
||||
|
||||
bool operator!=(IPaddress lhs, IPaddress rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
//Public access members
|
||||
//-------------------------
|
||||
|
||||
InWorld::InWorld(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: 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)) {
|
||||
localCharacter->CorrectSprite();
|
||||
SendLocalCharacterMotion();
|
||||
}
|
||||
|
||||
//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);
|
||||
font.DrawStringTo(to_string_custom(fps.GetFrameRate()), 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
|
||||
switch(key.keysym.sym) {
|
||||
case SDLK_ESCAPE:
|
||||
//TODO: 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();
|
||||
SendLocalCharacterMotion();
|
||||
}
|
||||
|
||||
void InWorld::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();
|
||||
SendLocalCharacterMotion();
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
//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;
|
||||
|
||||
//character movement
|
||||
case SerialPacketType::CHARACTER_SET_ROOM:
|
||||
HandleCharacterSetRoom(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
case SerialPacketType::CHARACTER_SET_ORIGIN:
|
||||
HandleCharacterSetOrigin(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
case SerialPacketType::CHARACTER_SET_MOTION:
|
||||
HandleCharacterSetMotion(static_cast<CharacterPacket*>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
//entity management
|
||||
//-------------------------
|
||||
|
||||
//DOCS: preexisting characters will result in query responses
|
||||
//DOCS: new characters will result in create messages
|
||||
//DOCS: this client's character will exist in both (skipped)
|
||||
|
||||
void InWorld::HandleCharacterCreate(CharacterPacket* const argPacket) {
|
||||
//prevent double message
|
||||
if (characterMap.find(argPacket->characterIndex) != characterMap.end()) {
|
||||
std::ostringstream msg;
|
||||
msg << "Double character creation event; ";
|
||||
msg << "Index: " << argPacket->characterIndex << "; ";
|
||||
msg << "Handle: " << argPacket->handle;
|
||||
throw(std::runtime_error(msg.str()));
|
||||
}
|
||||
|
||||
//implicity create and retrieve the entity
|
||||
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->CorrectSprite();
|
||||
|
||||
//check for this player's character
|
||||
if (character->GetOwner() == accountIndex) {
|
||||
localCharacter = static_cast<LocalCharacter*>(character);
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//debug
|
||||
std::cout << "Create, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
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()) {
|
||||
//debug
|
||||
std::cout << "Ignoring character deletion" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//check for this player's character
|
||||
if ((*characterIt).second.GetOwner() == accountIndex) {
|
||||
localCharacter = nullptr;
|
||||
|
||||
//clear the camera
|
||||
camera.marginX = 0;
|
||||
camera.marginY = 0;
|
||||
|
||||
//clear the room
|
||||
roomIndex = -1;
|
||||
}
|
||||
|
||||
//remove this character
|
||||
characterMap.erase(characterIt);
|
||||
|
||||
//debug
|
||||
std::cout << "Delete, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterQueryExists(CharacterPacket* const argPacket) {
|
||||
//prevent a double message about this player's character
|
||||
if (argPacket->accountIndex == accountIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//ignore characters in a different room (sub-optimal)
|
||||
if (argPacket->roomIndex != roomIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//implicitly construct the character if it doesn't exist
|
||||
BaseCharacter* character = &characterMap[argPacket->characterIndex];
|
||||
|
||||
//set/update 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->CorrectSprite();
|
||||
|
||||
//debug
|
||||
std::cout << "Query, total: " << characterMap.size() << std::endl;
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterSetRoom(CharacterPacket* const argPacket) {
|
||||
//someone else's character
|
||||
if (argPacket->characterIndex != characterIndex) {
|
||||
characterMap.erase(argPacket->characterIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
//this character is moving between rooms
|
||||
roomIndex = argPacket->roomIndex;
|
||||
|
||||
//set the character's info
|
||||
localCharacter->SetOrigin(argPacket->origin);
|
||||
localCharacter->SetMotion(argPacket->motion);
|
||||
localCharacter->CorrectSprite();
|
||||
|
||||
//clear the old room's data
|
||||
regionPager.UnloadAll();
|
||||
monsterMap.clear();
|
||||
|
||||
//use the jenky pattern for std::map to skip this player's character
|
||||
for (std::map<int, BaseCharacter>::iterator it = characterMap.begin(); it != characterMap.end(); /* EMPTY */ ) {
|
||||
if (it->first != characterIndex) {
|
||||
it = characterMap.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
//request the info on characters in this room
|
||||
CharacterPacket newPacket;
|
||||
newPacket.type = SerialPacketType::QUERY_CHARACTER_EXISTS;
|
||||
newPacket.roomIndex = roomIndex;
|
||||
network.SendTo(Channels::SERVER, &newPacket);
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterSetOrigin(CharacterPacket* const argPacket) {
|
||||
//TODO: Authentication
|
||||
if (argPacket->characterIndex == characterIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check that this character exists
|
||||
std::map<int, BaseCharacter>::iterator characterIt = characterMap.find(argPacket->characterIndex);
|
||||
if (characterIt != characterMap.end()) {
|
||||
//set the origin and motion
|
||||
characterIt->second.SetOrigin(argPacket->origin);
|
||||
characterIt->second.SetMotion(argPacket->motion);
|
||||
characterIt->second.CorrectSprite();
|
||||
}
|
||||
}
|
||||
|
||||
void InWorld::HandleCharacterSetMotion(CharacterPacket* const argPacket) {
|
||||
//TODO: Authentication
|
||||
if (argPacket->characterIndex == characterIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
//check that this character exists
|
||||
std::map<int, BaseCharacter>::iterator characterIt = characterMap.find(argPacket->characterIndex);
|
||||
if (characterIt != characterMap.end()) {
|
||||
//set the origin and motion
|
||||
characterIt->second.SetOrigin(argPacket->origin);
|
||||
characterIt->second.SetMotion(argPacket->motion);
|
||||
characterIt->second.CorrectSprite();
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------
|
||||
//player movement
|
||||
//-------------------------
|
||||
|
||||
//TODO: add a "movement" packet type
|
||||
void InWorld::SendLocalCharacterMotion() {
|
||||
CharacterPacket newPacket;
|
||||
newPacket.type = SerialPacketType::CHARACTER_SET_MOTION;
|
||||
|
||||
newPacket.accountIndex = accountIndex;
|
||||
newPacket.characterIndex = characterIndex;
|
||||
newPacket.roomIndex = roomIndex;
|
||||
newPacket.origin = localCharacter->GetOrigin();
|
||||
newPacket.motion = localCharacter->GetMotion();
|
||||
|
||||
network.SendTo(Channels::SERVER, &newPacket);
|
||||
}
|
||||
|
||||
std::list<BoundingBox> InWorld::GenerateCollisionGrid(Entity* ptr, int tileWidth, int tileHeight) {
|
||||
//prepare for collisions
|
||||
BoundingBox wallBounds = {0, 0, tileWidth, tileHeight};
|
||||
std::list<BoundingBox> boxList;
|
||||
|
||||
//NOTE: for loops were too dense to work with, so I've just used while loops
|
||||
|
||||
//outer loop
|
||||
wallBounds.x = snapToBase((double)wallBounds.w, ptr->GetOrigin().x);
|
||||
while(wallBounds.x < (ptr->GetOrigin() + ptr->GetBounds()).x + ptr->GetBounds().w) {
|
||||
//inner loop
|
||||
wallBounds.y = snapToBase((double)wallBounds.h, ptr->GetOrigin().y);
|
||||
while(wallBounds.y < (ptr->GetOrigin() + ptr->GetBounds()).y + ptr->GetBounds().h) {
|
||||
//check to see if this tile is solid
|
||||
if (regionPager.GetSolid(wallBounds.x / wallBounds.w, wallBounds.y / wallBounds.h)) {
|
||||
//push onto the box set
|
||||
boxList.push_front(wallBounds);
|
||||
}
|
||||
|
||||
//increment
|
||||
wallBounds.y += wallBounds.h;
|
||||
}
|
||||
|
||||
//increment
|
||||
wallBounds.x += wallBounds.w;
|
||||
}
|
||||
|
||||
return std::move(boxList);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
#config
|
||||
INCLUDES+=.
|
||||
INCLUDES+=. ../map
|
||||
LIBS+=
|
||||
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#config
|
||||
INCLUDES+=. ../graphics ../utilities
|
||||
INCLUDES+=. ../utilities
|
||||
LIBS+=
|
||||
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
//all map API headers
|
||||
#include "region_api.hpp"
|
||||
#include "region_pager_api.hpp"
|
||||
#include "tile_sheet_api.hpp"
|
||||
|
||||
//useful "globals"
|
||||
//...
|
||||
@@ -37,7 +36,6 @@ static const luaL_Reg funcs[] = {
|
||||
static const luaL_Reg libs[] = {
|
||||
{"Region", openRegionAPI},
|
||||
{"RegionPager", openRegionPagerAPI},
|
||||
// {"TileSheet", openTileSheetAPI},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef MAPSYSTEMAPI_HPP_
|
||||
#define MAPSYSTEMAPI_APP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_MAP_SYSTEM_API "map_system"
|
||||
LUAMOD_API int openMapSystemAPI(lua_State* L);
|
||||
|
||||
@@ -42,18 +42,30 @@ Region::Region(Region const& rhs): x(rhs.x), y(rhs.y) {
|
||||
}
|
||||
|
||||
Region::type_t Region::SetTile(int x, int y, int z, type_t v) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT || z >= REGION_DEPTH) {
|
||||
throw(std::out_of_range("Region::SetTile() argument out of range"));
|
||||
}
|
||||
return tiles[x][y][z] = v;
|
||||
}
|
||||
|
||||
Region::type_t Region::GetTile(int x, int y, int z) {
|
||||
if (x < 0 || y < 0 || z < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT || z >= REGION_DEPTH) {
|
||||
throw(std::out_of_range("Region::GetTile() argument out of range"));
|
||||
}
|
||||
return tiles[x][y][z];
|
||||
}
|
||||
|
||||
bool Region::SetSolid(int x, int y, bool b) {
|
||||
if (x < 0 || y < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT) {
|
||||
throw(std::out_of_range("Region::SetSolid() argument out of range"));
|
||||
}
|
||||
return solid[x * REGION_WIDTH + y] = b;
|
||||
}
|
||||
|
||||
bool Region::GetSolid(int x, int y) {
|
||||
if (x < 0 || y < 0 || x >= REGION_WIDTH || y >= REGION_HEIGHT) {
|
||||
throw(std::out_of_range("Region::GetSolid() argument out of range"));
|
||||
}
|
||||
return solid[x * REGION_WIDTH + y];
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef REGIONAPI_HPP_
|
||||
#define REGIONAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_REGION_NAME "region"
|
||||
LUAMOD_API int openRegionAPI(lua_State* L);
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef REGIONPAGERAPI_HPP_
|
||||
#define REGIONPAGERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_REGION_PAGER_NAME "region_pager"
|
||||
LUAMOD_API int openRegionPagerAPI(lua_State* L);
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
|
||||
#include "region_pager_base.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
@@ -1,75 +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 "tile_sheet_api.hpp"
|
||||
|
||||
#include "tile_sheet.hpp"
|
||||
|
||||
static int load(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
sheet->Load(lua_tostring(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unload(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
sheet->Unload();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getXCount(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, sheet->GetXCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getYCount(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, sheet->GetYCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getTileW(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, sheet->GetTileW());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getTileH(lua_State* L) {
|
||||
TileSheet* sheet = reinterpret_cast<TileSheet*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, sheet->GetTileH());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg tileSheetLib[] = {
|
||||
{"Load",load},
|
||||
{"Unload",unload},
|
||||
{"GetXCount",getXCount},
|
||||
{"GetYCount",getYCount},
|
||||
{"GetTileW",getTileW},
|
||||
{"GetTileH",getTileH},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
LUAMOD_API int openTileSheetAPI(lua_State* L) {
|
||||
luaL_newlib(L, tileSheetLib);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/* 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_packet.hpp"
|
||||
|
||||
#include "serial_utility.hpp"
|
||||
|
||||
void serializeMonster(void* buffer, MonsterPacket* packet) {
|
||||
serialCopy(&buffer, &packet->type, sizeof(SerialPacketType));
|
||||
|
||||
//identify the monster
|
||||
serialCopy(&buffer, &packet->monsterIndex, sizeof(int));
|
||||
serialCopy(&buffer, packet->handle, PACKET_STRING_SIZE);
|
||||
serialCopy(&buffer, packet->avatar, PACKET_STRING_SIZE);
|
||||
|
||||
//bounds
|
||||
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));
|
||||
|
||||
|
||||
//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));
|
||||
|
||||
//attack data
|
||||
//TODO
|
||||
}
|
||||
|
||||
void deserializeMonster(void* buffer, MonsterPacket* packet) {
|
||||
deserialCopy(&buffer, &packet->type, sizeof(SerialPacketType));
|
||||
|
||||
//identify the monster
|
||||
deserialCopy(&buffer, &packet->monsterIndex, sizeof(int));
|
||||
deserialCopy(&buffer, packet->handle, PACKET_STRING_SIZE);
|
||||
deserialCopy(&buffer, packet->avatar, PACKET_STRING_SIZE);
|
||||
|
||||
//bounds
|
||||
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));
|
||||
|
||||
|
||||
//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));
|
||||
|
||||
//attack data
|
||||
//TODO
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/* 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.
|
||||
*/
|
||||
#ifndef MONSTERPACKET_HPP_
|
||||
#define MONSTERPACKET_HPP_
|
||||
|
||||
#include "serial_packet_base.hpp"
|
||||
|
||||
#include "bounding_box.hpp"
|
||||
#include "vector2.hpp"
|
||||
|
||||
struct MonsterPacket : SerialPacketBase {
|
||||
//identify the monster
|
||||
int monsterIndex;
|
||||
char handle[PACKET_STRING_SIZE];
|
||||
char avatar[PACKET_STRING_SIZE];
|
||||
BoundingBox bounds;
|
||||
|
||||
//location
|
||||
int roomIndex;
|
||||
Vector2 origin;
|
||||
Vector2 motion;
|
||||
|
||||
//TODO: attack data
|
||||
};
|
||||
|
||||
void serializeMonster(void* buffer, MonsterPacket* packet);
|
||||
void deserializeMonster(void* buffer, MonsterPacket* packet);
|
||||
|
||||
#endif
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "serial_packet_base.hpp"
|
||||
#include "character_packet.hpp"
|
||||
#include "client_packet.hpp"
|
||||
#include "monster_packet.hpp"
|
||||
#include "region_packet.hpp"
|
||||
#include "server_packet.hpp"
|
||||
#include "text_packet.hpp"
|
||||
@@ -33,14 +34,15 @@
|
||||
typedef SerialPacketBase SerialPacket;
|
||||
|
||||
//DOCS: NETWORK_VERSION is used to discern compatible servers and clients
|
||||
constexpr int NETWORK_VERSION = 20141227;
|
||||
constexpr int NETWORK_VERSION = -1;
|
||||
|
||||
union MaxPacket {
|
||||
CharacterPacket a;
|
||||
ClientPacket b;
|
||||
RegionPacket c;
|
||||
ServerPacket d;
|
||||
TextPacket e;
|
||||
MonsterPacket c;
|
||||
RegionPacket d;
|
||||
ServerPacket e;
|
||||
TextPacket f;
|
||||
};
|
||||
|
||||
constexpr int MAX_PACKET_SIZE = sizeof(MaxPacket);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
//TODO: This needs to be smoothed out
|
||||
enum class SerialPacketType {
|
||||
//default: there is something wrong
|
||||
NONE = 0,
|
||||
NONE,
|
||||
|
||||
//-------------------------
|
||||
//ServerPacket
|
||||
@@ -38,12 +38,12 @@ enum class SerialPacketType {
|
||||
//-------------------------
|
||||
|
||||
//heartbeat
|
||||
PING = 1,
|
||||
PONG = 2,
|
||||
PING,
|
||||
PONG,
|
||||
|
||||
//Used for finding available servers
|
||||
BROADCAST_REQUEST = 3,
|
||||
BROADCAST_RESPONSE = 4,
|
||||
BROADCAST_REQUEST,
|
||||
BROADCAST_RESPONSE,
|
||||
|
||||
//-------------------------
|
||||
//ClientPacket
|
||||
@@ -51,24 +51,24 @@ enum class SerialPacketType {
|
||||
//-------------------------
|
||||
|
||||
//Connecting to a server as a client
|
||||
JOIN_REQUEST = 5,
|
||||
JOIN_RESPONSE = 6,
|
||||
JOIN_REQUEST,
|
||||
JOIN_RESPONSE,
|
||||
|
||||
//disconnect from the server
|
||||
DISCONNECT_REQUEST = 7,
|
||||
DISCONNECT_RESPONSE = 8,
|
||||
DISCONNECT_FORCED = 9,
|
||||
DISCONNECT_REQUEST,
|
||||
DISCONNECT_RESPONSE,
|
||||
DISCONNECT_FORCED,
|
||||
|
||||
//load the account
|
||||
LOGIN_REQUEST = 10,
|
||||
LOGIN_RESPONSE = 11,
|
||||
LOGIN_REQUEST,
|
||||
LOGIN_RESPONSE,
|
||||
|
||||
//unload the account
|
||||
LOGOUT_REQUEST = 12,
|
||||
LOGOUT_RESPONSE = 13,
|
||||
LOGOUT_REQUEST,
|
||||
LOGOUT_RESPONSE,
|
||||
|
||||
//shut down the server
|
||||
SHUTDOWN_REQUEST = 14,
|
||||
SHUTDOWN_REQUEST,
|
||||
|
||||
//-------------------------
|
||||
//RegionPacket
|
||||
@@ -76,8 +76,8 @@ enum class SerialPacketType {
|
||||
//-------------------------
|
||||
|
||||
//map data
|
||||
REGION_REQUEST = 15, //NOTE: technically a query
|
||||
REGION_CONTENT = 16,
|
||||
REGION_REQUEST, //NOTE: technically a query
|
||||
REGION_CONTENT,
|
||||
|
||||
//-------------------------
|
||||
//CharacterPacket
|
||||
@@ -89,22 +89,40 @@ enum class SerialPacketType {
|
||||
//-------------------------
|
||||
|
||||
//character management
|
||||
CHARACTER_CREATE = 17,
|
||||
CHARACTER_DELETE = 18,
|
||||
CHARACTER_LOAD = 19,
|
||||
CHARACTER_UNLOAD = 20,
|
||||
CHARACTER_CREATE,
|
||||
CHARACTER_DELETE,
|
||||
CHARACTER_LOAD,
|
||||
CHARACTER_UNLOAD,
|
||||
|
||||
//find out info from the server
|
||||
QUERY_CHARACTER_EXISTS = 21,
|
||||
QUERY_CHARACTER_STATS = 22,
|
||||
QUERY_CHARACTER_LOCATION = 23,
|
||||
QUERY_CHARACTER_EXISTS,
|
||||
QUERY_CHARACTER_STATS,
|
||||
QUERY_CHARACTER_LOCATION,
|
||||
|
||||
//set the info in the server
|
||||
CHARACTER_SET_ROOM = 24,
|
||||
CHARACTER_SET_ORIGIN = 25,
|
||||
CHARACTER_SET_MOTION = 26,
|
||||
CHARACTER_MOVEMENT,
|
||||
CHARACTER_ATTACK,
|
||||
|
||||
//TODO: enemy management
|
||||
//admin control
|
||||
// ADMIN_SET_CHARACTER_ORIGIN,
|
||||
|
||||
//-------------------------
|
||||
//MonsterPacket
|
||||
// monster index,
|
||||
// handle, avatar, hitbox
|
||||
// room index, origin, motion
|
||||
// TODO: attack data
|
||||
//-------------------------
|
||||
|
||||
MONSTER_CREATE,
|
||||
MONSTER_DELETE,
|
||||
|
||||
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...
|
||||
|
||||
MONSTER_MOVEMENT, //monster movement
|
||||
MONSTER_ATTACK,
|
||||
|
||||
//-------------------------
|
||||
//TextPacket
|
||||
@@ -112,20 +130,21 @@ enum class SerialPacketType {
|
||||
//-------------------------
|
||||
|
||||
//general speech
|
||||
TEXT_BROADCAST = 27,
|
||||
TEXT_BROADCAST,
|
||||
|
||||
//rejection/error messages
|
||||
JOIN_REJECTION = 28,
|
||||
LOGIN_REJECTION = 29,
|
||||
REGION_REJECTION = 30,
|
||||
CHARACTER_REJECTION = 31,
|
||||
SHUTDOWN_REJECTION = 32,
|
||||
JOIN_REJECTION,
|
||||
LOGIN_REJECTION,
|
||||
REGION_REJECTION,
|
||||
CHARACTER_REJECTION,
|
||||
MONSTER_REJECTION,
|
||||
SHUTDOWN_REJECTION,
|
||||
|
||||
//-------------------------
|
||||
//not used
|
||||
//-------------------------
|
||||
|
||||
LAST = 33
|
||||
LAST
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -24,6 +24,7 @@
|
||||
//packet types
|
||||
#include "character_packet.hpp"
|
||||
#include "client_packet.hpp"
|
||||
#include "monster_packet.hpp"
|
||||
#include "region_packet.hpp"
|
||||
#include "server_packet.hpp"
|
||||
#include "text_packet.hpp"
|
||||
@@ -75,16 +76,25 @@ void serializePacket(void* buffer, SerialPacketBase* packet) {
|
||||
case SerialPacketType::QUERY_CHARACTER_EXISTS:
|
||||
case SerialPacketType::QUERY_CHARACTER_STATS:
|
||||
case SerialPacketType::QUERY_CHARACTER_LOCATION:
|
||||
case SerialPacketType::CHARACTER_SET_ROOM:
|
||||
case SerialPacketType::CHARACTER_SET_ORIGIN:
|
||||
case SerialPacketType::CHARACTER_SET_MOTION:
|
||||
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;
|
||||
@@ -126,16 +136,25 @@ void deserializePacket(void* buffer, SerialPacketBase* packet) {
|
||||
case SerialPacketType::QUERY_CHARACTER_EXISTS:
|
||||
case SerialPacketType::QUERY_CHARACTER_STATS:
|
||||
case SerialPacketType::QUERY_CHARACTER_LOCATION:
|
||||
case SerialPacketType::CHARACTER_SET_ROOM:
|
||||
case SerialPacketType::CHARACTER_SET_ORIGIN:
|
||||
case SerialPacketType::CHARACTER_SET_MOTION:
|
||||
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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#for use on Windows:
|
||||
|
||||
#MKDIR=mkdir
|
||||
#Windows 7:
|
||||
#RM=del /y
|
||||
|
||||
#Windows 8.1:
|
||||
#RM=del /S
|
||||
|
||||
OUTDIR=out
|
||||
|
||||
all: $(OUTDIR)
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ server.dbname = database.db
|
||||
#client.screen.h = 600
|
||||
client.screen.f = false
|
||||
|
||||
client.username = Kayne Ruse
|
||||
client.handle = Ratstail91
|
||||
client.username = username
|
||||
client.handle = handle
|
||||
client.avatar = elliot2.bmp
|
||||
|
||||
#directories
|
||||
|
||||
+77
-21
@@ -1,39 +1,95 @@
|
||||
local mapSystem = require "map_system"
|
||||
local Region = require("map_system").Region
|
||||
|
||||
local mapMaker = {}
|
||||
|
||||
--utility functions
|
||||
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
|
||||
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.edges = {}
|
||||
mapMaker.edges.north = -16
|
||||
mapMaker.edges.south = 16
|
||||
mapMaker.edges.east = 1
|
||||
mapMaker.edges.west = -1
|
||||
|
||||
mapMaker.water = 18 + 3 * 0
|
||||
mapMaker.sand = 18 + 3 * 1
|
||||
mapMaker.plains = 18 + 3 * 2
|
||||
mapMaker.grass = 18 + 3 * 3
|
||||
mapMaker.dirt = 18 + 3 * 4
|
||||
|
||||
--custom generation systems here
|
||||
function mapMaker.debugIsland(region)
|
||||
for i = 1, mapSystem.Region.GetWidth(region) do
|
||||
for j = 1, mapSystem.Region.GetHeight(region) do
|
||||
local dist = mapMaker.dist(0, 0, i + mapSystem.Region.GetX(region) -1, j + mapSystem.Region.GetY(region) -1)
|
||||
if dist < 10 then
|
||||
mapSystem.Region.SetTile(region, i, j, 1, mapMaker.plains)
|
||||
elseif dist < 12 then
|
||||
mapSystem.Region.SetTile(region, i, j, 1, mapMaker.sand)
|
||||
else
|
||||
mapSystem.Region.SetTile(region, i, j, 1, mapMaker.water)
|
||||
mapSystem.Region.SetSolid(region, i, j, true)
|
||||
--"edge" macros
|
||||
mapMaker.edges = {}
|
||||
mapMaker.edges.north = -16
|
||||
mapMaker.edges.south = 16
|
||||
mapMaker.edges.east = 1
|
||||
mapMaker.edges.west = -1
|
||||
|
||||
--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, Region.GetWidth(r) do
|
||||
shiftArray[i] = {}
|
||||
for j = 1, Region.GetHeight(r) do
|
||||
shiftArray[i][j] = 0
|
||||
end
|
||||
end
|
||||
|
||||
--build the array
|
||||
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 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 < 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 < 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, Region.GetWidth(r) do
|
||||
for j = 1, Region.GetHeight(r) do
|
||||
if shiftArray[i][j] ~= 0 then
|
||||
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
|
||||
end
|
||||
|
||||
--custom generation systems here
|
||||
function mapMaker.DebugIsland(r)
|
||||
--basic distance check for each tile, placing an island around the world origin
|
||||
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
|
||||
Region.SetTile(r, i, j, 1, mapMaker.plains)
|
||||
elseif dist < 12 then
|
||||
Region.SetTile(r, i, j, 1, mapMaker.sand)
|
||||
else
|
||||
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, Region.GetHeight(r) do
|
||||
Region.SetTile(r, 3, j, 1, mapMaker.dirt)
|
||||
Region.SetTile(r, 4, j, 1, mapMaker.dirt)
|
||||
|
||||
Region.SetTile(r, 10, j, 1, mapMaker.dirt)
|
||||
end
|
||||
--]]
|
||||
|
||||
--A generic edge system
|
||||
mapMaker.SmoothEdgesSimple(r)
|
||||
end
|
||||
|
||||
return mapMaker
|
||||
@@ -1,11 +1,15 @@
|
||||
local Region = require("map_system").Region
|
||||
|
||||
local mapSaver = {}
|
||||
function mapSaver.Load(region)
|
||||
|
||||
function mapSaver.Load(r)
|
||||
--empty
|
||||
print("map_saver.lua:mapSaver.Load(region)")
|
||||
io.write("map_saver:Load(", Region.GetX(r), ", ", Region.GetY(r), ")\n")
|
||||
end
|
||||
function mapSaver.Save(region)
|
||||
function mapSaver.Save(r)
|
||||
--empty
|
||||
print("map_saver.lua:mapSaver.Save(region)")
|
||||
io.write("map_saver:Save(", Region.GetX(r), ", ", Region.GetY(r), ")\n")
|
||||
end
|
||||
|
||||
--TODO: create a flexible saving & loading system
|
||||
return mapSaver
|
||||
@@ -1,10 +1,8 @@
|
||||
print("Lua script check")
|
||||
|
||||
mapSystem = require "map_system"
|
||||
mapMaker = require "map_maker"
|
||||
mapSaver = require "map_saver"
|
||||
roomSystem = require "room_system"
|
||||
waypointSystem = require "waypoint_system"
|
||||
mapMaker = require("map_maker")
|
||||
mapSaver = require("map_saver")
|
||||
roomSystem = require("room_system")
|
||||
|
||||
local function dumpTable(t)
|
||||
print(t)
|
||||
@@ -15,53 +13,6 @@ end
|
||||
|
||||
--NOTE: room 0 is the first that the client asks for, therefore it must exist
|
||||
local overworld, uid = roomSystem.RoomManager.CreateRoom("overworld", "overworld.bmp")
|
||||
|
||||
--NOTE: This is horrible; room initialization is important
|
||||
mapSystem.RegionPager.SetOnLoad(roomSystem.Room.GetPager(overworld), mapSaver.Load)
|
||||
mapSystem.RegionPager.SetOnSave(roomSystem.Room.GetPager(overworld), mapSaver.Save)
|
||||
mapSystem.RegionPager.SetOnCreate(roomSystem.Room.GetPager(overworld), mapMaker.debugIsland)
|
||||
mapSystem.RegionPager.SetOnUnload(roomSystem.Room.GetPager(overworld), mapSaver.Save)
|
||||
roomSystem.Room.Initialize(overworld, mapSaver.Load, mapSaver.Save, mapMaker.DebugIsland, mapSaver.Save)
|
||||
|
||||
print("Finished the lua script")
|
||||
|
||||
--[[
|
||||
debugging test
|
||||
|
||||
Ideal output:
|
||||
|
||||
-------------------------
|
||||
pager: userdata: [memory location]
|
||||
Size 0: 0
|
||||
[debug output from load]
|
||||
Size 1: 1
|
||||
[debug output from save]
|
||||
Size 2: 0
|
||||
[debug output from load]
|
||||
Size 3: 1
|
||||
[debug output from save]
|
||||
Size 4: 0
|
||||
-------------------------
|
||||
|
||||
--]-]
|
||||
|
||||
print("-------------------------")
|
||||
local pager = roomSystem.Room.GetPager(overworld)
|
||||
|
||||
print("pager:", pager)
|
||||
|
||||
print("Size 0:", mapSystem.RegionPager.ContainerSize(pager))
|
||||
|
||||
local regionFoo = mapSystem.RegionPager.GetRegion(pager, 0, 0)
|
||||
print("Size 1:", mapSystem.RegionPager.ContainerSize(pager))
|
||||
|
||||
mapSystem.RegionPager.UnloadRegion(pager, regionFoo)
|
||||
print("Size 2:", mapSystem.RegionPager.ContainerSize(pager))
|
||||
|
||||
local regionFoo = mapSystem.RegionPager.GetRegion(pager, 0, 0)
|
||||
print("Size 3:", mapSystem.RegionPager.ContainerSize(pager))
|
||||
|
||||
mapSystem.RegionPager.UnloadRegion(pager, 0, 0)
|
||||
print("Size 4:", mapSystem.RegionPager.ContainerSize(pager))
|
||||
|
||||
print("-------------------------")
|
||||
--]]
|
||||
@@ -25,11 +25,7 @@
|
||||
#include "account_data.hpp"
|
||||
#include "singleton.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef CHARACTERAPI_HPP_
|
||||
#define CHARACTERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_CHARACTER_API "character"
|
||||
LUAMOD_API int openCharacterAPI(lua_State* L);
|
||||
|
||||
@@ -21,11 +21,7 @@
|
||||
*/
|
||||
#include "character_manager.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -25,11 +25,7 @@
|
||||
#include "character_data.hpp"
|
||||
#include "singleton.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef CHARACTERMANAGERAPI_HPP_
|
||||
#define CHARACTERMANAGERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_CHARACTER_MANAGER_API "character_manager"
|
||||
LUAMOD_API int openCharacterManagerAPI(lua_State* L);
|
||||
|
||||
@@ -33,14 +33,14 @@ Vector2 Entity::SetMotion(Vector2 v) {
|
||||
return motion = v;
|
||||
}
|
||||
|
||||
int Entity::GetRoomIndex() {
|
||||
int Entity::GetRoomIndex() const {
|
||||
return roomIndex;
|
||||
}
|
||||
|
||||
Vector2 Entity::GetOrigin() {
|
||||
Vector2 Entity::GetOrigin() const {
|
||||
return origin;
|
||||
}
|
||||
|
||||
Vector2 Entity::GetMotion() {
|
||||
Vector2 Entity::GetMotion() const {
|
||||
return motion;
|
||||
}
|
||||
@@ -32,9 +32,9 @@ public:
|
||||
Vector2 SetOrigin(Vector2 v);
|
||||
Vector2 SetMotion(Vector2 v);
|
||||
|
||||
int GetRoomIndex();
|
||||
Vector2 GetOrigin();
|
||||
Vector2 GetMotion();
|
||||
int GetRoomIndex() const;
|
||||
Vector2 GetOrigin() const;
|
||||
Vector2 GetMotion() const;
|
||||
|
||||
protected:
|
||||
Entity() = default;
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef ENTITYAPI_HPP_
|
||||
#define ENTITYAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_ENTITY_API "entity"
|
||||
LUAMOD_API int openEntityAPI(lua_State* L);
|
||||
|
||||
+5
-5
@@ -34,13 +34,11 @@
|
||||
#define linit_c
|
||||
#define LUA_LIB
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#include "entity_api.hpp"
|
||||
#include "map_system_api.hpp"
|
||||
#include "monster_system_api.hpp"
|
||||
#include "room_system_api.hpp"
|
||||
#include "waypoint_system_api.hpp"
|
||||
|
||||
@@ -63,7 +61,9 @@ static const luaL_Reg loadedlibs[] = {
|
||||
|
||||
//these libs are preloaded and must be required before used
|
||||
static const luaL_Reg preloadedlibs[] = {
|
||||
{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}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "character_manager.hpp"
|
||||
#include "client_manager.hpp"
|
||||
#include "config_utility.hpp"
|
||||
#include "monster_manager.hpp"
|
||||
#include "room_manager.hpp"
|
||||
#include "udp_network_utility.hpp"
|
||||
#include "waypoint_manager.hpp"
|
||||
@@ -43,10 +42,8 @@ int main(int argc, char* argv[]) {
|
||||
CharacterManager::CreateSingleton();
|
||||
ClientManager::CreateSingleton();
|
||||
ConfigUtility::CreateSingleton();
|
||||
MonsterManager::CreateSingleton();
|
||||
RoomManager::CreateSingleton();
|
||||
UDPNetworkUtility::CreateSingleton();
|
||||
WaypointManager::CreateSingleton();
|
||||
|
||||
//call the server's routines
|
||||
ServerApplication::CreateSingleton();
|
||||
@@ -63,10 +60,8 @@ int main(int argc, char* argv[]) {
|
||||
CharacterManager::DeleteSingleton();
|
||||
ClientManager::DeleteSingleton();
|
||||
ConfigUtility::DeleteSingleton();
|
||||
MonsterManager::DeleteSingleton();
|
||||
RoomManager::DeleteSingleton();
|
||||
UDPNetworkUtility::DeleteSingleton();
|
||||
WaypointManager::DeleteSingleton();
|
||||
}
|
||||
catch(exception& e) {
|
||||
cerr << "Fatal exception thrown: " << e.what() << endl;
|
||||
|
||||
@@ -23,11 +23,66 @@
|
||||
|
||||
#include "monster_data.hpp"
|
||||
|
||||
#include "entity_api.hpp"
|
||||
|
||||
static int setAvatar(lua_State* L) {
|
||||
MonsterData* monster = static_cast<MonsterData*>(lua_touserdata(L, 1));
|
||||
monster->SetAvatar(lua_tostring(L, 2));
|
||||
//TODO: send an update to the clients?
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getAvatar(lua_State* L) {
|
||||
MonsterData* monster = static_cast<MonsterData*>(lua_touserdata(L, 1));
|
||||
lua_pushstring(L, monster->GetAvatar().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setScript(lua_State* L) {
|
||||
MonsterData* monster = static_cast<MonsterData*>(lua_touserdata(L, 1));
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, monster->GetScriptReference());
|
||||
monster->SetScriptReference(luaL_ref(L, LUA_REGISTRYINDEX));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getScript(lua_State* L) {
|
||||
MonsterData* monster = static_cast<MonsterData*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, monster->GetScriptReference());
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg monsterLib[] = {
|
||||
{"SetAvatar", setAvatar},
|
||||
{"GetAvatar", getAvatar},
|
||||
{"SetScript", setScript},
|
||||
{"GetScript", getScript},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
LUAMOD_API int openMonsterAPI(lua_State* L) {
|
||||
//the local table
|
||||
luaL_newlib(L, monsterLib);
|
||||
|
||||
//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 local table
|
||||
lua_settable(L, -6);
|
||||
|
||||
//pop the original value before continuing
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
//remove the parent table, leaving the expanded child table
|
||||
lua_pop(L, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef MONSTERAPI_HPP_
|
||||
#define MONSTERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_MONSTER_API "monster"
|
||||
LUAMOD_API int openMonsterAPI(lua_State* L);
|
||||
|
||||
@@ -25,14 +25,14 @@ std::string MonsterData::SetAvatar(std::string s) {
|
||||
return avatar = s;
|
||||
}
|
||||
|
||||
int MonsterData::SetScriptReference(int i) {
|
||||
return scriptRef = i;
|
||||
}
|
||||
|
||||
std::string MonsterData::GetAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
int MonsterData::SetScriptReference(int i) {
|
||||
return scriptRef = i;
|
||||
}
|
||||
|
||||
int MonsterData::GetScriptReference() {
|
||||
return scriptRef;
|
||||
}
|
||||
@@ -32,9 +32,9 @@ public:
|
||||
~MonsterData() = default;
|
||||
|
||||
std::string SetAvatar(std::string);
|
||||
int SetScriptReference(int);
|
||||
|
||||
std::string GetAvatar();
|
||||
|
||||
int SetScriptReference(int);
|
||||
int GetScriptReference();
|
||||
|
||||
private:
|
||||
|
||||
@@ -21,26 +21,22 @@
|
||||
*/
|
||||
#include "monster_manager.hpp"
|
||||
|
||||
MonsterManager::MonsterManager() {
|
||||
//EMPTY
|
||||
}
|
||||
|
||||
MonsterManager::~MonsterManager() {
|
||||
UnloadAll();
|
||||
}
|
||||
|
||||
int MonsterManager::Create(std::string) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
int MonsterManager::Load(std::string) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
int MonsterManager::Save(int uid) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
void MonsterManager::Unload(int uid) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
void MonsterManager::Delete(int uid) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
void MonsterManager::UnloadAll() {
|
||||
//TODO
|
||||
}
|
||||
@@ -57,22 +53,10 @@ int MonsterManager::GetLoadedCount() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
int MonsterManager::GetTotalCount() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
std::map<int, MonsterData>* MonsterManager::GetContainer() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
sqlite3* MonsterManager::SetDatabase(sqlite3* db) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
sqlite3* MonsterManager::GetDatabase() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
lua_State* MonsterManager::SetLuaState(lua_State* L) {
|
||||
//TODO
|
||||
}
|
||||
@@ -80,3 +64,11 @@ lua_State* MonsterManager::SetLuaState(lua_State* L) {
|
||||
lua_State* MonsterManager::GetLuaState() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
sqlite3* MonsterManager::SetDatabase(sqlite3* db) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
sqlite3* MonsterManager::GetDatabase() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
@@ -23,28 +23,22 @@
|
||||
#define MONSTERMANAGER_HPP_
|
||||
|
||||
#include "monster_data.hpp"
|
||||
#include "singleton.hpp"
|
||||
|
||||
#ifdef __unix__
|
||||
#include "lua.hpp"
|
||||
#include "sqlite3.h"
|
||||
#else
|
||||
#include "lua/lua.hpp"
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class MonsterManager: public Singleton<MonsterManager> {
|
||||
class MonsterManager {
|
||||
public:
|
||||
MonsterManager();
|
||||
~MonsterManager();
|
||||
|
||||
//common public methods
|
||||
int Create(std::string);
|
||||
int Load(std::string);
|
||||
int Save(int uid);
|
||||
void Unload(int uid);
|
||||
void Delete(int uid);
|
||||
|
||||
void UnloadAll();
|
||||
void UnloadIf(std::function<bool(std::pair<const int, MonsterData const&>)> fn);
|
||||
@@ -52,25 +46,19 @@ public:
|
||||
//accessors & mutators
|
||||
MonsterData* Get(int uid);
|
||||
int GetLoadedCount();
|
||||
int GetTotalCount();
|
||||
std::map<int, MonsterData>* GetContainer();
|
||||
|
||||
//hooks
|
||||
sqlite3* SetDatabase(sqlite3* db);
|
||||
sqlite3* GetDatabase();
|
||||
lua_State* SetLuaState(lua_State* L);
|
||||
lua_State* GetLuaState();
|
||||
sqlite3* SetDatabase(sqlite3* db);
|
||||
sqlite3* GetDatabase();
|
||||
|
||||
private:
|
||||
friend Singleton<MonsterManager>;
|
||||
|
||||
MonsterManager() = default;
|
||||
~MonsterManager() = default;
|
||||
|
||||
//members
|
||||
std::map<int, MonsterData> elementMap;
|
||||
sqlite3* database = nullptr;
|
||||
lua_State* lua = nullptr;
|
||||
sqlite3* database = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef MONSTERMANAGERAPI_HPP_
|
||||
#define MONSTERMANAGERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_MONSTER_MANAGER_API "monster_manager"
|
||||
LUAMOD_API int openMonsterManagerAPI(lua_State* L);
|
||||
|
||||
@@ -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,16 +19,12 @@
|
||||
* 3. This notice may not be removed or altered from any source
|
||||
* distribution.
|
||||
*/
|
||||
#ifndef TILESHEETAPI_HPP_
|
||||
#define TILESHEETAPI_HPP_
|
||||
#ifndef MONSTERSYSTEMAPI_HPP_
|
||||
#define MONSTERSYSTEMAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_TILE_SHEET_NAME "tile_sheet"
|
||||
LUAMOD_API int openTileSheetAPI(lua_State* L);
|
||||
#define TORTUGA_MONSTER_SYSTEM_API "monster_system"
|
||||
LUAMOD_API int openMonsterSystemAPI(lua_State* L);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#config
|
||||
INCLUDES+=. ../entities ../server_utilities ../../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))
|
||||
|
||||
|
||||
@@ -53,11 +53,21 @@ 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 getWaypointMgr(lua_State* L) {
|
||||
RoomData* room = reinterpret_cast<RoomData*>(lua_touserdata(L, 1));
|
||||
lua_pushlightuserdata(L, reinterpret_cast<void*>(room->GetWaypointMgr()) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int initialize(lua_State* L) {
|
||||
//set the members of the given room
|
||||
//TODO: This could fit into the room system's globals
|
||||
RoomData* room = static_cast<RoomData*>(lua_touserdata(L, 1));
|
||||
room->SetName(lua_tostring(L, 2));
|
||||
room->SetTileset(lua_tostring(L, 3));
|
||||
|
||||
//set the refs of these parameters (backwards, since it pops from the top of the stack)
|
||||
room->GetPager()->SetUnloadReference(luaL_ref(L, LUA_REGISTRYINDEX));
|
||||
@@ -70,11 +80,15 @@ static int initialize(lua_State* L) {
|
||||
}
|
||||
|
||||
static const luaL_Reg roomLib[] = {
|
||||
{"GetPager",getPager},
|
||||
{"SetName", setRoomName},
|
||||
{"GetName", getRoomName},
|
||||
{"SetTileset", setTilesetName},
|
||||
{"GetTileset", getTilesetName},
|
||||
|
||||
{"GetPager",getPager},
|
||||
{"GetMonsterMgr",getMonsterMgr},
|
||||
{"GetWaypointMgr",getWaypointMgr},
|
||||
|
||||
{"Initialize", initialize},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef ROOMAPI_HPP_
|
||||
#define ROOMAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_ROOM_API "room"
|
||||
LUAMOD_API int openRoomAPI(lua_State* L);
|
||||
|
||||
@@ -41,6 +41,14 @@ RegionPagerLua* RoomData::GetPager() {
|
||||
return &pager;
|
||||
}
|
||||
|
||||
std::list<Entity*>* RoomData::GetEntityList() {
|
||||
return &entityList;
|
||||
MonsterManager* RoomData::GetMonsterMgr() {
|
||||
return &monsterMgr;
|
||||
}
|
||||
|
||||
WaypointManager* RoomData::GetWaypointMgr() {
|
||||
return &waypointMgr;
|
||||
}
|
||||
|
||||
std::list<CharacterData*>* RoomData::GetCharacterList() {
|
||||
return &characterList;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,12 @@
|
||||
#ifndef ROOMDATA_HPP_
|
||||
#define ROOMDATA_HPP_
|
||||
|
||||
#include "entity.hpp"
|
||||
#include "character_data.hpp"
|
||||
#include "monster_manager.hpp"
|
||||
#include "region_pager_lua.hpp"
|
||||
#include "waypoint_manager.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
@@ -47,7 +45,9 @@ public:
|
||||
std::string GetTileset();
|
||||
|
||||
RegionPagerLua* GetPager();
|
||||
std::list<Entity*>* GetEntityList();
|
||||
MonsterManager* GetMonsterMgr();
|
||||
WaypointManager* GetWaypointMgr();
|
||||
std::list<CharacterData*>* GetCharacterList();
|
||||
|
||||
//TODO: triggers for unload, save, per-second, player enter, player exit, etc.
|
||||
|
||||
@@ -59,7 +59,9 @@ private:
|
||||
std::string tilesetName;
|
||||
|
||||
RegionPagerLua pager;
|
||||
std::list<Entity*> entityList;
|
||||
MonsterManager monsterMgr;
|
||||
WaypointManager waypointMgr;
|
||||
std::list<CharacterData*> characterList;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,9 @@ int RoomManager::Create(std::string roomName, std::string tileset) {
|
||||
newRoom->SetTileset(tileset);
|
||||
|
||||
newRoom->pager.SetLuaState(lua);
|
||||
newRoom->monsterMgr.SetLuaState(lua);
|
||||
newRoom->monsterMgr.SetDatabase(database);
|
||||
newRoom->waypointMgr.SetLuaState(lua);
|
||||
|
||||
//finish the routine
|
||||
return counter++;
|
||||
@@ -57,6 +60,37 @@ void RoomManager::UnloadIf(std::function<bool(std::pair<const int, RoomData cons
|
||||
}
|
||||
}
|
||||
|
||||
void RoomManager::PushCharacter(CharacterData* character) {
|
||||
if (!character) {
|
||||
throw(std::runtime_error("Failed to push a null character to a room"));
|
||||
}
|
||||
|
||||
RoomData* room = Get(character->GetRoomIndex());
|
||||
|
||||
if (!room) {
|
||||
throw(std::runtime_error("Failed to push an character to a non-existant room"));
|
||||
}
|
||||
|
||||
room->characterList.push_back(character);
|
||||
}
|
||||
|
||||
void RoomManager::PopCharacter(CharacterData const* character) {
|
||||
//NOTE: to pop an character from a room, the character must first exist
|
||||
if (!character) {
|
||||
throw(std::runtime_error("Failed to pop a null character to a room"));
|
||||
}
|
||||
|
||||
RoomData* room = Get(character->GetRoomIndex());
|
||||
|
||||
if (!room) {
|
||||
throw(std::runtime_error("Failed to pop an character to a non-existant room"));
|
||||
}
|
||||
|
||||
room->characterList.remove_if([character](CharacterData* ptr) {
|
||||
return character == ptr;
|
||||
});
|
||||
}
|
||||
|
||||
RoomData* RoomManager::Get(int uid) {
|
||||
std::map<int, RoomData>::iterator it = elementMap.find(uid);
|
||||
|
||||
@@ -91,3 +125,11 @@ lua_State* RoomManager::SetLuaState(lua_State* L) {
|
||||
lua_State* RoomManager::GetLuaState() {
|
||||
return lua;
|
||||
}
|
||||
|
||||
sqlite3* RoomManager::SetDatabase(sqlite3* db) {
|
||||
return database = db;
|
||||
}
|
||||
|
||||
sqlite3* RoomManager::GetDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,12 @@
|
||||
#ifndef ROOMMANAGER_HPP_
|
||||
#define ROOMMANAGER_HPP_
|
||||
|
||||
#include "character_data.hpp"
|
||||
#include "room_data.hpp"
|
||||
#include "singleton.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
@@ -42,6 +40,9 @@ public:
|
||||
void UnloadAll();
|
||||
void UnloadIf(std::function<bool(std::pair<const int, RoomData const&>)> fn);
|
||||
|
||||
void PushCharacter(CharacterData* character);
|
||||
void PopCharacter(CharacterData const* character);
|
||||
|
||||
//accessors and mutators
|
||||
RoomData* Get(int uid);
|
||||
RoomData* Get(std::string name);
|
||||
@@ -51,6 +52,8 @@ public:
|
||||
//hooks
|
||||
lua_State* SetLuaState(lua_State* L);
|
||||
lua_State* GetLuaState();
|
||||
sqlite3* SetDatabase(sqlite3* db);
|
||||
sqlite3* GetDatabase();
|
||||
|
||||
private:
|
||||
friend Singleton<RoomManager>;
|
||||
@@ -61,6 +64,7 @@ private:
|
||||
//members
|
||||
std::map<int, RoomData> elementMap;
|
||||
lua_State* lua = nullptr;
|
||||
sqlite3* database = nullptr;
|
||||
int counter = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef ROOMMANAGERAPI_HPP_
|
||||
#define ROOMMANAGERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_ROOM_MANAGER_API "room_manager"
|
||||
LUAMOD_API int openRoomManagerAPI(lua_State* L);
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef ROOMSYSTEMAPI_HPP_
|
||||
#define ROOMSYSTEMAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_ROOM_SYSTEM_API "room_system"
|
||||
LUAMOD_API int openRoomSystemAPI(lua_State* L);
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "client_manager.hpp"
|
||||
#include "monster_manager.hpp"
|
||||
#include "room_manager.hpp"
|
||||
#include "waypoint_manager.hpp"
|
||||
|
||||
//utilities
|
||||
#include "config_utility.hpp"
|
||||
@@ -39,13 +38,8 @@
|
||||
#include "singleton.hpp"
|
||||
|
||||
//APIs
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include "SDL/SDL.h"
|
||||
|
||||
@@ -107,12 +101,12 @@ private:
|
||||
void HandleCharacterUnload(CharacterPacket* const);
|
||||
|
||||
//character movement
|
||||
void HandleCharacterSetRoom(CharacterPacket* const);
|
||||
void HandleCharacterSetOrigin(CharacterPacket* const);
|
||||
void HandleCharacterSetMotion(CharacterPacket* const);
|
||||
void HandleCharacterMovement(CharacterPacket* const);
|
||||
void HandleCharacterAttack(CharacterPacket* const);
|
||||
|
||||
//utility methods
|
||||
void PumpPacket(SerialPacket* const);
|
||||
void PumpPacketProximity(SerialPacket* const argPacket, int roomIndex, Vector2 position, int distance);
|
||||
void CopyCharacterToPacket(CharacterPacket* const packet, int characterIndex);
|
||||
|
||||
//APIs and utilities
|
||||
@@ -120,12 +114,10 @@ private:
|
||||
lua_State* luaState = nullptr;
|
||||
|
||||
//ugly references; I hate this
|
||||
ClientManager& clientMgr = ClientManager::GetSingleton();
|
||||
AccountManager& accountMgr = AccountManager::GetSingleton();
|
||||
CharacterManager& characterMgr = CharacterManager::GetSingleton();
|
||||
ClientManager& clientMgr = ClientManager::GetSingleton();
|
||||
MonsterManager& monsterMgr = MonsterManager::GetSingleton();
|
||||
RoomManager& roomMgr = RoomManager::GetSingleton();
|
||||
WaypointManager& waypointMgr = WaypointManager::GetSingleton();
|
||||
|
||||
ConfigUtility& config = ConfigUtility::GetSingleton();
|
||||
UDPNetworkUtility& network = UDPNetworkUtility::GetSingleton();
|
||||
|
||||
@@ -45,6 +45,9 @@ void ServerApplication::HandleCharacterCreate(CharacterPacket* const argPacket)
|
||||
return;
|
||||
}
|
||||
|
||||
//push to the rooms
|
||||
roomMgr.PushCharacter(characterMgr.Get(characterIndex));
|
||||
|
||||
//pump this character to all clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, characterIndex);
|
||||
@@ -86,6 +89,9 @@ void ServerApplication::HandleCharacterDelete(CharacterPacket* const argPacket)
|
||||
return;
|
||||
}
|
||||
|
||||
//pop from the rooms
|
||||
roomMgr.PopCharacter(characterMgr.Get(characterIndex));
|
||||
|
||||
//delete the character
|
||||
characterMgr.Delete(characterIndex);
|
||||
|
||||
@@ -119,6 +125,9 @@ void ServerApplication::HandleCharacterLoad(CharacterPacket* const argPacket) {
|
||||
return;
|
||||
}
|
||||
|
||||
//push to the rooms
|
||||
roomMgr.PushCharacter(characterMgr.Get(characterIndex));
|
||||
|
||||
//pump this character to all clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, characterIndex);
|
||||
@@ -149,6 +158,9 @@ void ServerApplication::HandleCharacterUnload(CharacterPacket* const argPacket)
|
||||
return;
|
||||
}
|
||||
|
||||
//pop from the rooms
|
||||
roomMgr.PopCharacter(characterData);
|
||||
|
||||
//unload the character
|
||||
characterMgr.Unload(argPacket->characterIndex);
|
||||
|
||||
@@ -163,99 +175,15 @@ void ServerApplication::HandleCharacterUnload(CharacterPacket* const argPacket)
|
||||
//character movement
|
||||
//-------------------------
|
||||
|
||||
//TODO: ? Could replace this verbosity with a "verify" method, taking a client, account and character ptr as arguments
|
||||
//TODO: Could replace this verbosity with a "verify" method, taking a client, account and character ptr as arguments
|
||||
|
||||
void ServerApplication::HandleCharacterSetRoom(CharacterPacket* const argPacket) {
|
||||
void ServerApplication::HandleCharacterMovement(CharacterPacket* const argPacket) {
|
||||
//get the specified objects
|
||||
AccountData* accountData = accountMgr.Get(argPacket->accountIndex);
|
||||
CharacterData* characterData = characterMgr.Get(argPacket->characterIndex);
|
||||
|
||||
if (!accountData || !characterData) {
|
||||
throw(std::runtime_error("Failed to set character room, missing data"));
|
||||
}
|
||||
|
||||
//get this account's client
|
||||
ClientData* clientData = clientMgr.Get(accountData->GetClientIndex());
|
||||
|
||||
//check for fraud
|
||||
if (clientData->GetAddress() != argPacket->srcAddress) {
|
||||
std::cerr << "Falsified set character origin targeting uid(" << argPacket->characterIndex << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//check if allowed
|
||||
if (characterData->GetOwner() != argPacket->accountIndex && !accountData->GetModerator() && !accountData->GetAdministrator()) {
|
||||
//TODO: send to the client?
|
||||
std::cerr << "Failed to set character room due to lack of permissions targeting uid(" << argPacket->characterIndex << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//set the character's room, zero it's origin, zero it's motion
|
||||
characterData->SetRoomIndex(argPacket->roomIndex);
|
||||
characterData->SetOrigin({0, 0});
|
||||
characterData->SetMotion({0, 0});
|
||||
|
||||
//update the clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_SET_ROOM;
|
||||
PumpPacket(&newPacket);
|
||||
}
|
||||
|
||||
void ServerApplication::HandleCharacterSetOrigin(CharacterPacket* const argPacket) {
|
||||
//get the specified objects
|
||||
AccountData* accountData = accountMgr.Get(argPacket->accountIndex);
|
||||
CharacterData* characterData = characterMgr.Get(argPacket->characterIndex);
|
||||
|
||||
if (!accountData || !characterData) {
|
||||
throw(std::runtime_error("Failed to set character origin, missing data"));
|
||||
}
|
||||
|
||||
//get this account's client
|
||||
ClientData* clientData = clientMgr.Get(accountData->GetClientIndex());
|
||||
|
||||
//check for fraud
|
||||
if (clientData->GetAddress() != argPacket->srcAddress) {
|
||||
std::cerr << "Falsified set character origin targeting uid(" << argPacket->characterIndex << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//check if allowed
|
||||
if (characterData->GetOwner() != argPacket->accountIndex && !accountData->GetModerator() && !accountData->GetAdministrator()) {
|
||||
//TODO: send to the client?
|
||||
std::cerr << "Failed to set character origin due to lack of permissions targeting uid(" << argPacket->characterIndex << ")" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
//set the character's origin, zero it's motion
|
||||
characterData->SetOrigin(argPacket->origin);
|
||||
characterData->SetMotion({0, 0});
|
||||
|
||||
//update the clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_SET_ORIGIN;
|
||||
PumpPacket(&newPacket);
|
||||
}
|
||||
|
||||
void ServerApplication::HandleCharacterSetMotion(CharacterPacket* const argPacket) {
|
||||
//get the specified objects
|
||||
AccountData* accountData = accountMgr.Get(argPacket->accountIndex);
|
||||
|
||||
if (!accountData) {
|
||||
std::ostringstream msg;
|
||||
msg << "Failed to set character motion, missing account: Index " << argPacket->accountIndex << "; ";
|
||||
msg << "Number of accounts loaded: " << accountMgr.GetContainer()->size();
|
||||
throw(std::runtime_error(msg.str()));
|
||||
}
|
||||
|
||||
CharacterData* characterData = characterMgr.Get(argPacket->characterIndex);
|
||||
|
||||
if (!characterData) {
|
||||
std::ostringstream msg;
|
||||
msg << "Failed to set character motion, missing character: Index " << argPacket->characterIndex << "; ";
|
||||
msg << "Number of characters loaded: " << characterMgr.GetContainer()->size();
|
||||
throw(std::runtime_error(msg.str()));
|
||||
throw(std::runtime_error("Failed to move a character, missing data"));
|
||||
}
|
||||
|
||||
//get this account's client
|
||||
@@ -274,13 +202,38 @@ void ServerApplication::HandleCharacterSetMotion(CharacterPacket* const argPacke
|
||||
return;
|
||||
}
|
||||
|
||||
//set the character's origin and motion
|
||||
characterData->SetOrigin(argPacket->origin);
|
||||
characterData->SetMotion(argPacket->motion);
|
||||
//check if moving rooms
|
||||
if (characterData->GetRoomIndex() != argPacket->roomIndex) {
|
||||
//delete from the old room
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_DELETE;
|
||||
PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1);
|
||||
|
||||
//update the clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_SET_MOTION;
|
||||
PumpPacket(&newPacket);
|
||||
//move the character between rooms
|
||||
roomMgr.PopCharacter(characterData);
|
||||
characterData->SetRoomIndex(argPacket->roomIndex);
|
||||
roomMgr.PushCharacter(characterData);
|
||||
|
||||
//create in the new room
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_CREATE;
|
||||
PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1);
|
||||
}
|
||||
//if not moving between rooms
|
||||
else {
|
||||
//set the character's origin and motion
|
||||
characterData->SetOrigin(argPacket->origin);
|
||||
characterData->SetMotion(argPacket->motion);
|
||||
|
||||
//update the clients
|
||||
CharacterPacket newPacket;
|
||||
CopyCharacterToPacket(&newPacket, argPacket->characterIndex);
|
||||
newPacket.type = SerialPacketType::CHARACTER_MOVEMENT;
|
||||
PumpPacketProximity(&newPacket, characterData->GetRoomIndex(), characterData->GetOrigin(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void ServerApplication::HandleCharacterAttack(CharacterPacket* const) {
|
||||
//TODO: bounce graphical attack data
|
||||
}
|
||||
+15
-21
@@ -23,7 +23,6 @@
|
||||
|
||||
//utility functions
|
||||
#include "sql_tools.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
//std & STL
|
||||
#include <stdexcept>
|
||||
@@ -105,7 +104,7 @@ void ServerApplication::Init(int argc, char* argv[]) {
|
||||
characterMgr.SetDatabase(database);
|
||||
|
||||
roomMgr.SetLuaState(luaState);
|
||||
waypointMgr.SetLuaState(luaState);
|
||||
roomMgr.SetDatabase(database);
|
||||
|
||||
std::cout << "Internal managers initialized" << std::endl;
|
||||
|
||||
@@ -187,7 +186,7 @@ void ServerApplication::Proc() {
|
||||
std::cerr << "Client dropped: " << disconnected << std::endl;
|
||||
}
|
||||
|
||||
//give the computer a break
|
||||
//give the machine a break
|
||||
SDL_Delay(10);
|
||||
}
|
||||
delete reinterpret_cast<char*>(packetBuffer);
|
||||
@@ -202,9 +201,7 @@ void ServerApplication::Quit() {
|
||||
accountMgr.UnloadAll();
|
||||
characterMgr.UnloadAll();
|
||||
clientMgr.UnloadAll();
|
||||
monsterMgr.UnloadAll();
|
||||
roomMgr.UnloadAll();
|
||||
waypointMgr.UnloadAll();
|
||||
|
||||
//APIs
|
||||
lua_close(luaState);
|
||||
@@ -279,27 +276,24 @@ void ServerApplication::HandlePacket(SerialPacket* const argPacket) {
|
||||
HandleCharacterUnload(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
|
||||
//character movement
|
||||
case SerialPacketType::CHARACTER_SET_ROOM:
|
||||
HandleCharacterSetRoom(static_cast<CharacterPacket*>(argPacket));
|
||||
/* case SerialPacketType::QUERY_CHARACTER_EXISTS:
|
||||
//
|
||||
break;
|
||||
case SerialPacketType::CHARACTER_SET_ORIGIN:
|
||||
HandleCharacterSetOrigin(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
case SerialPacketType::CHARACTER_SET_MOTION:
|
||||
HandleCharacterSetMotion(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
/*
|
||||
case SerialPacketType::QUERY_CHARACTER_STATS:
|
||||
// HandleCharacterStatsRequest(static_cast<RegionPacket*>(argPacket));
|
||||
//
|
||||
break;
|
||||
case SerialPacketType::QUERY_CHARACTER_LOCATION:
|
||||
// HandleCharacterStatsRequest(static_cast<RegionPacket*>(argPacket));
|
||||
//
|
||||
break;
|
||||
case SerialPacketType::TEXT_BROADCAST:
|
||||
// HandleCharacterStatsRequest(static_cast<RegionPacket*>(argPacket));
|
||||
*/
|
||||
//character movement
|
||||
case SerialPacketType::CHARACTER_MOVEMENT:
|
||||
HandleCharacterMovement(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
|
||||
case SerialPacketType::CHARACTER_ATTACK:
|
||||
HandleCharacterAttack(static_cast<CharacterPacket*>(argPacket));
|
||||
break;
|
||||
/*
|
||||
//enemy management
|
||||
//TODO: enemy management
|
||||
|
||||
@@ -309,7 +303,7 @@ void ServerApplication::HandlePacket(SerialPacket* const argPacket) {
|
||||
default: {
|
||||
std::ostringstream msg;
|
||||
msg << "Unknown SerialPacketType encountered in the server: ";
|
||||
msg << to_string_custom(static_cast<int>(argPacket->type));
|
||||
msg << static_cast<int>(argPacket->type);
|
||||
throw(std::runtime_error(msg.str()));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -149,6 +149,9 @@ void ServerApplication::FullCharacterUnload(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//pop from the rooms
|
||||
roomMgr.PopCharacter(&character.second);
|
||||
|
||||
//pump character unload
|
||||
CharacterPacket newPacket;
|
||||
newPacket.type = SerialPacketType::CHARACTER_DELETE;
|
||||
@@ -172,6 +175,22 @@ void ServerApplication::PumpPacket(SerialPacket* const argPacket) {
|
||||
}
|
||||
}
|
||||
|
||||
void ServerApplication::PumpPacketProximity(SerialPacket* const argPacket, int roomIndex, Vector2 position, int distance) {
|
||||
RoomData* room = roomMgr.Get(roomIndex);
|
||||
|
||||
if (!room) {
|
||||
throw(std::runtime_error("Failed to pump to a non-existant room"));
|
||||
}
|
||||
|
||||
for (auto& character : *room->GetCharacterList()) {
|
||||
if (distance == -1 || (character->GetOrigin() - position).Length() <= distance) {
|
||||
AccountData* account = accountMgr.Get(character->GetOwner());
|
||||
ClientData* client = clientMgr.Get(account->GetClientIndex());
|
||||
network.SendTo(client->GetAddress(), argPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServerApplication::CopyCharacterToPacket(CharacterPacket* const packet, int characterIndex) {
|
||||
CharacterData* character = characterMgr.Get(characterIndex);
|
||||
if (!character) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#config
|
||||
INCLUDES+=. ../../common/utilities
|
||||
INCLUDES+=.
|
||||
LIBS+=
|
||||
CXXFLAGS+=-std=c++11 $(addprefix -I,$(INCLUDES))
|
||||
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
*/
|
||||
#include "sql_tools.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
|
||||
int runSQLScript(sqlite3* db, std::string fname, int (*callback)(void*,int,char**,char**), void* argPtr) {
|
||||
@@ -42,9 +41,10 @@ int runSQLScript(sqlite3* db, std::string fname, int (*callback)(void*,int,char*
|
||||
int ret = sqlite3_exec(db, script.c_str(), callback, argPtr, &errmsg);
|
||||
if (ret != SQLITE_OK) {
|
||||
//handle any errors received from the SQL
|
||||
std::runtime_error e(std::string() + "SQL Script Error " + to_string_custom(ret) + ": " + errmsg);
|
||||
std::ostringstream msg;
|
||||
msg << "SQL Script Error " << ret << ": " << errmsg;
|
||||
free(errmsg);
|
||||
throw(e);
|
||||
throw(std::runtime_error( msg.str() ));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef SERVERUTILITY_HPP_
|
||||
#define SERVERUTILITY_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "sqlite3/sqlite3.h"
|
||||
#else
|
||||
#include "sqlite3.h"
|
||||
#endif
|
||||
#include "sqlite3.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -23,8 +23,65 @@
|
||||
|
||||
#include "waypoint_data.hpp"
|
||||
|
||||
//TODO: Can I alias the entity API for this?
|
||||
//origin
|
||||
static int setOrigin(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
waypoint->SetOrigin(Vector2(lua_tonumber(L, 2), lua_tonumber(L, 3)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getOrigin(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
lua_pushnumber(L, waypoint->GetOrigin().x);
|
||||
lua_pushnumber(L, waypoint->GetOrigin().y);
|
||||
return 2;
|
||||
}
|
||||
|
||||
//bounds
|
||||
static int setBoundingBox(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
waypoint->SetBoundingBox(BoundingBox(
|
||||
lua_tonumber(L, 2),
|
||||
lua_tonumber(L, 3),
|
||||
lua_tonumber(L, 4),
|
||||
lua_tonumber(L, 5)
|
||||
));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getBoundingBox(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
lua_pushnumber(L, waypoint->GetBoundingBox().x);
|
||||
lua_pushnumber(L, waypoint->GetBoundingBox().y);
|
||||
lua_pushnumber(L, waypoint->GetBoundingBox().w);
|
||||
lua_pushnumber(L, waypoint->GetBoundingBox().h);
|
||||
return 4;
|
||||
}
|
||||
|
||||
//triggers
|
||||
static int setTriggerReference(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, waypoint->GetTriggerReference());
|
||||
waypoint->SetTriggerReference(luaL_ref(L, LUA_REGISTRYINDEX));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getTriggerReference(lua_State* L) {
|
||||
WaypointData* waypoint = static_cast<WaypointData*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, waypoint->GetTriggerReference());
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg waypointLib[] = {
|
||||
{"SetOrigin",setOrigin},
|
||||
{"GetOrigin",getOrigin},
|
||||
|
||||
{"SetBounds",setBoundingBox},
|
||||
{"GetBounds",getBoundingBox},
|
||||
|
||||
{"SetTrigger",setTriggerReference},
|
||||
{"GetTrigger",getTriggerReference},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef WAYPOINTAPI_HPP_
|
||||
#define WAYPOINTAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_WAYPOINT_API "waypoint"
|
||||
LUAMOD_API int openWaypointAPI(lua_State* L);
|
||||
|
||||
@@ -27,4 +27,20 @@ int WaypointData::SetTriggerReference(int i) {
|
||||
|
||||
int WaypointData::GetTriggerReference() {
|
||||
return triggerRef;
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBox WaypointData::SetBoundingBox(BoundingBox b) {
|
||||
return bounds = b;
|
||||
}
|
||||
|
||||
BoundingBox WaypointData::GetBoundingBox() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Vector2 WaypointData::SetOrigin(Vector2 v) {
|
||||
return origin = v;
|
||||
}
|
||||
|
||||
Vector2 WaypointData::GetOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
@@ -22,27 +22,32 @@
|
||||
#ifndef WAYPOINTDATA_HPP_
|
||||
#define WAYPOINTDATA_HPP_
|
||||
|
||||
#include "entity.hpp"
|
||||
#include "bounding_box.hpp"
|
||||
#include "vector2.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class WaypointData: public Entity {
|
||||
class WaypointData {
|
||||
public:
|
||||
WaypointData() = default;
|
||||
~WaypointData() = default;
|
||||
|
||||
Vector2 SetOrigin(Vector2 v);
|
||||
Vector2 GetOrigin();
|
||||
|
||||
BoundingBox SetBoundingBox(BoundingBox b);
|
||||
BoundingBox GetBoundingBox();
|
||||
|
||||
int SetTriggerReference(int i);
|
||||
int GetTriggerReference();
|
||||
|
||||
private:
|
||||
friend class WaypointManager;
|
||||
|
||||
Vector2 origin;
|
||||
BoundingBox bounds;
|
||||
int triggerRef = LUA_NOREF;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,46 +21,78 @@
|
||||
*/
|
||||
#include "waypoint_manager.hpp"
|
||||
|
||||
WaypointManager::WaypointManager() {
|
||||
//EMPTY
|
||||
}
|
||||
|
||||
WaypointManager::~WaypointManager() {
|
||||
UnloadAll();
|
||||
}
|
||||
|
||||
int WaypointManager::Create() {
|
||||
//TODO
|
||||
//implicitly creates the element
|
||||
WaypointData& waypointData = elementMap[counter];
|
||||
|
||||
//no real values set
|
||||
waypointData.origin = {0, 0};
|
||||
waypointData.bounds = {0, 0, 0, 0};
|
||||
|
||||
return counter++;
|
||||
}
|
||||
|
||||
int WaypointManager::Load() {
|
||||
//TODO
|
||||
}
|
||||
int WaypointManager::Create(Vector2 origin, BoundingBox bounds) {
|
||||
//implicitly creates the element
|
||||
WaypointData& waypointData = elementMap[counter];
|
||||
|
||||
int WaypointManager::Save(int uid) {
|
||||
//TODO
|
||||
waypointData.origin = origin;
|
||||
waypointData.bounds = bounds;
|
||||
|
||||
return counter++;
|
||||
}
|
||||
|
||||
void WaypointManager::Unload(int uid) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
void WaypointManager::Delete(int uid) {
|
||||
//TODO
|
||||
elementMap.erase(uid);
|
||||
}
|
||||
|
||||
void WaypointManager::UnloadAll() {
|
||||
//TODO
|
||||
elementMap.clear();
|
||||
}
|
||||
|
||||
void WaypointManager::UnloadIf(std::function<bool(std::pair<const int, WaypointData const&>)> fn) {
|
||||
//TODO
|
||||
std::map<int, WaypointData>::iterator it = elementMap.begin();
|
||||
while (it != elementMap.end()) {
|
||||
if (fn(*it)) {
|
||||
it = elementMap.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WaypointData* WaypointManager::Get(int uid) {
|
||||
//TODO
|
||||
std::map<int, WaypointData>::iterator it = elementMap.find(uid);
|
||||
|
||||
if (it == elementMap.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
int WaypointManager::GetLoadedCount() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
int WaypointManager::GetTotalCount() {
|
||||
//TODO
|
||||
return elementMap.size();
|
||||
}
|
||||
|
||||
std::map<int, WaypointData>* WaypointManager::GetContainer() {
|
||||
//TODO
|
||||
return &elementMap;
|
||||
}
|
||||
|
||||
//hooks
|
||||
lua_State* WaypointManager::SetLuaState(lua_State* L) {
|
||||
return lua = L;
|
||||
}
|
||||
|
||||
lua_State* WaypointManager::GetLuaState() {
|
||||
return lua;
|
||||
}
|
||||
@@ -22,29 +22,25 @@
|
||||
#ifndef WAYPOINTMANAGER_HPP_
|
||||
#define WAYPOINTMANAGER_HPP_
|
||||
|
||||
#include "waypoint_data.hpp"
|
||||
#include "singleton.hpp"
|
||||
#include "bounding_box.hpp"
|
||||
#include "vector2.hpp"
|
||||
#include "waypoint_data.hpp"
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
//TODO: should waypoints be managed on a per-room basis?
|
||||
class WaypointManager: public Singleton<WaypointManager> {
|
||||
class WaypointManager {
|
||||
public:
|
||||
WaypointManager();
|
||||
~WaypointManager();
|
||||
|
||||
//common public methods
|
||||
int Create();
|
||||
int Load();
|
||||
int Save(int uid);
|
||||
int Create(Vector2 origin, BoundingBox bounds);
|
||||
void Unload(int uid);
|
||||
void Delete(int uid);
|
||||
|
||||
void UnloadAll();
|
||||
void UnloadIf(std::function<bool(std::pair<const int, WaypointData const&>)> fn);
|
||||
@@ -52,19 +48,13 @@ public:
|
||||
//accessors & mutators
|
||||
WaypointData* Get(int uid);
|
||||
int GetLoadedCount();
|
||||
int GetTotalCount();
|
||||
std::map<int, WaypointData>* GetContainer();
|
||||
|
||||
//hooks
|
||||
lua_State* SetLuaState(lua_State* L) { return lua = L; }
|
||||
lua_State* GetLuaState() { return lua; }
|
||||
lua_State* SetLuaState(lua_State* L);
|
||||
lua_State* GetLuaState();
|
||||
|
||||
private:
|
||||
friend Singleton<WaypointManager>;
|
||||
|
||||
WaypointManager() = default;
|
||||
~WaypointManager() = default;
|
||||
|
||||
//members
|
||||
std::map<int, WaypointData> elementMap;
|
||||
lua_State* lua = nullptr;
|
||||
|
||||
@@ -23,7 +23,35 @@
|
||||
|
||||
#include "waypoint_manager.hpp"
|
||||
|
||||
//TODO: figure out a way to iterate through elements of managers from lua
|
||||
|
||||
static int create(lua_State* L) {
|
||||
WaypointManager* mgr = static_cast<WaypointManager*>(lua_touserdata(L, 1));
|
||||
//TODO
|
||||
}
|
||||
|
||||
static int unload(lua_State* L) {
|
||||
WaypointManager* mgr = static_cast<WaypointManager*>(lua_touserdata(L, 1));
|
||||
//TODO
|
||||
}
|
||||
|
||||
static int getWaypoint(lua_State* L) {
|
||||
WaypointManager* mgr = static_cast<WaypointManager*>(lua_touserdata(L, 1));
|
||||
lua_pushlightuserdata(L, mgr->Get(lua_tointeger(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int getLoadedCount(lua_State* L) {
|
||||
WaypointManager* mgr = static_cast<WaypointManager*>(lua_touserdata(L, 1));
|
||||
lua_pushinteger(L, mgr->GetLoadedCount());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const luaL_Reg waypointManagerLib[] = {
|
||||
{"Create",create},
|
||||
{"Unload",unload},
|
||||
{"GetWaypoint",getWaypoint},
|
||||
{"GetCount",getLoadedCount},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef WAYPOINTMANAGERAPI_HPP_
|
||||
#define WAYPOINTMANAGERAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_WAYPOINT_MANAGER_API "waypoint_manager"
|
||||
LUAMOD_API int openWaypointManagerAPI(lua_State* L);
|
||||
|
||||
@@ -22,11 +22,7 @@
|
||||
#ifndef WAYPOINTSYSTEMAPI_HPP_
|
||||
#define WAYPOINTSYSTEMAPI_HPP_
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
#include "lua/lua.hpp"
|
||||
#else
|
||||
#include "lua.hpp"
|
||||
#endif
|
||||
#include "lua.hpp"
|
||||
|
||||
#define TORTUGA_WAYPOINT_SYSTEM_API "waypoint_system"
|
||||
LUAMOD_API int openWaypointSystemAPI(lua_State* L);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
TODO: In need of script APIs (list)
|
||||
* Characters
|
||||
* Monsters
|
||||
* Waypoints
|
||||
|
||||
TODO: Account passwords (list)
|
||||
* backbone account server OR
|
||||
@@ -9,6 +7,8 @@ TODO: Account passwords (list)
|
||||
* ...
|
||||
* salts & hashes
|
||||
|
||||
TODO: Split config.cfg in two, one for the server and the client
|
||||
TODO: Add the "home" parameter to the server's config file
|
||||
TODO: Waypoints, with positions and trigger zones (collision areas) for doors, monster spawns, etc.
|
||||
TODO: Fix shoddy movement
|
||||
TODO: Periodic mass server saves
|
||||
@@ -17,3 +17,5 @@ TODO: Make a way for the server owner to control the server directly
|
||||
TODO: The TileSheet class should implement the surface itself
|
||||
TODO: Time delay for requesting region packets
|
||||
TODO: A proper logging system
|
||||
TODO: Fix the const-ness of accessors
|
||||
TODO: Add a screenshot of the game to README.md
|
||||
Reference in New Issue
Block a user