mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-11-02 12:56:23 +01:00
added mounts, auras, wings and shop. Packet compression does not work extendedOpCodes does not work but shop is because of extra configuration
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/otpch.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/actions.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/auras.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ban.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/bed.cpp
|
||||
@@ -38,6 +39,7 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/map.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/monster.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mounts.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/movement.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/npc.cpp
|
||||
@@ -56,6 +58,7 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/server.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/shaders.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/spawn.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/spells.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/script.cpp
|
||||
@@ -68,6 +71,7 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/vocation.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/wings.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xtea.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/quests.cpp
|
||||
PARENT_SCOPE)
|
||||
|
||||
@@ -324,7 +324,6 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
|
||||
bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
|
||||
{
|
||||
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL));
|
||||
player->stopWalk();
|
||||
|
||||
if (item->getID() == ITEM_MARKET)
|
||||
{
|
||||
@@ -360,7 +359,6 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
|
||||
uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/)
|
||||
{
|
||||
player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL));
|
||||
player->stopWalk();
|
||||
|
||||
Action* action = getAction(item);
|
||||
if (!action) {
|
||||
|
||||
63
src/auras.cpp
Normal file
63
src/auras.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "otpch.h"
|
||||
|
||||
#include "auras.h"
|
||||
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Auras::reload()
|
||||
{
|
||||
auras.clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
bool Auras::loadFromXml()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file("data/XML/auras.xml");
|
||||
if (!result) {
|
||||
printXMLError("Error - Auras::loadFromXml", "data/XML/auras.xml", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto auraNode : doc.child("auras").children()) {
|
||||
auras.emplace_back(
|
||||
static_cast<uint8_t>(pugi::cast<uint16_t>(auraNode.attribute("id").value())),
|
||||
pugi::cast<uint16_t>(auraNode.attribute("clientid").value()),
|
||||
auraNode.attribute("name").as_string(),
|
||||
pugi::cast<int32_t>(auraNode.attribute("speed").value()),
|
||||
auraNode.attribute("premium").as_bool()
|
||||
);
|
||||
}
|
||||
auras.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Aura* Auras::getAuraByID(uint8_t id)
|
||||
{
|
||||
auto it = std::find_if(auras.begin(), auras.end(), [id](const Aura& aura) {
|
||||
return aura.id == id;
|
||||
});
|
||||
|
||||
return it != auras.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Aura* Auras::getAuraByName(const std::string& name) {
|
||||
auto auraName = name.c_str();
|
||||
for (auto& it : auras) {
|
||||
if (strcasecmp(auraName, it.name.c_str()) == 0) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Aura* Auras::getAuraByClientID(uint16_t clientId)
|
||||
{
|
||||
auto it = std::find_if(auras.begin(), auras.end(), [clientId](const Aura& aura) {
|
||||
return aura.clientId == clientId;
|
||||
});
|
||||
|
||||
return it != auras.end() ? &*it : nullptr;
|
||||
}
|
||||
33
src/auras.h
Normal file
33
src/auras.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef FS_AURAS_H
|
||||
#define FS_AURAS_H
|
||||
|
||||
struct Aura
|
||||
{
|
||||
Aura(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||
|
||||
std::string name;
|
||||
int32_t speed;
|
||||
uint16_t clientId;
|
||||
uint8_t id;
|
||||
bool premium;
|
||||
};
|
||||
|
||||
class Auras
|
||||
{
|
||||
public:
|
||||
bool reload();
|
||||
bool loadFromXml();
|
||||
Aura* getAuraByID(uint8_t id);
|
||||
Aura* getAuraByName(const std::string& name);
|
||||
Aura* getAuraByClientID(uint16_t clientId);
|
||||
|
||||
const std::vector<Aura>& getAuras() const {
|
||||
return auras;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Aura> auras;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -87,6 +87,7 @@ bool ConfigManager::load()
|
||||
boolean[ROPE_SPOT_BLOCK] = getGlobalBoolean(L, "ropeSpotBlock", false);
|
||||
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
|
||||
boolean[DISTANCE_WEAPONS_DROP_ON_GROUND] = getGlobalBoolean(L, "distanceWeaponsDropOnGround", true);
|
||||
boolean[PACKET_COMPRESSION] = getGlobalBoolean(L, "packetCompression", true);
|
||||
|
||||
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
|
||||
string[SERVER_NAME] = getGlobalString(L, "serverName", "");
|
||||
|
||||
@@ -53,6 +53,7 @@ class ConfigManager
|
||||
DISTANCE_WEAPONS_DROP_ON_GROUND,
|
||||
CORPSE_OWNER_ENABLED,
|
||||
ROPE_SPOT_BLOCK,
|
||||
PACKET_COMPRESSION,
|
||||
LAST_BOOLEAN_CONFIG /* this must be the last one */
|
||||
};
|
||||
|
||||
|
||||
140
src/const.h
140
src/const.h
@@ -20,7 +20,7 @@
|
||||
#ifndef FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
||||
#define FS_CONST_H_0A49B5996F074465BF44B90F4F780E8B
|
||||
|
||||
static constexpr int32_t NETWORKMESSAGE_MAXSIZE = 24590;
|
||||
static constexpr int32_t NETWORKMESSAGE_MAXSIZE = 65500;
|
||||
static constexpr int32_t MIN_MARKET_FEE = 20;
|
||||
static constexpr int32_t MAX_MARKET_FEE = 100000;
|
||||
|
||||
@@ -378,6 +378,7 @@ enum PlayerFlags : uint64_t {
|
||||
enum ReloadTypes_t : uint8_t {
|
||||
RELOAD_TYPE_ALL,
|
||||
RELOAD_TYPE_ACTIONS,
|
||||
RELOAD_TYPE_AURAS,
|
||||
RELOAD_TYPE_CHAT,
|
||||
RELOAD_TYPE_COMMANDS,
|
||||
RELOAD_TYPE_CONFIG,
|
||||
@@ -393,8 +394,129 @@ enum ReloadTypes_t : uint8_t {
|
||||
RELOAD_TYPE_QUESTS,
|
||||
RELOAD_TYPE_RAIDS,
|
||||
RELOAD_TYPE_SPELLS,
|
||||
RELOAD_TYPE_SHADERS,
|
||||
RELOAD_TYPE_TALKACTIONS,
|
||||
RELOAD_TYPE_WEAPONS,
|
||||
RELOAD_TYPE_WINGS,
|
||||
};
|
||||
|
||||
// OTCv8 features (from src/client/const.h)
|
||||
enum GameFeature {
|
||||
GameProtocolChecksum = 1,
|
||||
GameAccountNames = 2,
|
||||
GameChallengeOnLogin = 3,
|
||||
GamePenalityOnDeath = 4,
|
||||
GameNameOnNpcTrade = 5,
|
||||
GameDoubleFreeCapacity = 6,
|
||||
GameDoubleExperience = 7,
|
||||
GameTotalCapacity = 8,
|
||||
GameSkillsBase = 9,
|
||||
GamePlayerRegenerationTime = 10,
|
||||
GameChannelPlayerList = 11,
|
||||
GamePlayerMounts = 12,
|
||||
GameEnvironmentEffect = 13,
|
||||
GameCreatureEmblems = 14,
|
||||
GameItemAnimationPhase = 15,
|
||||
GameMagicEffectU16 = 16,
|
||||
GamePlayerMarket = 17,
|
||||
GameSpritesU32 = 18,
|
||||
GameTileAddThingWithStackpos = 19,
|
||||
GameOfflineTrainingTime = 20,
|
||||
GamePurseSlot = 21,
|
||||
GameFormatCreatureName = 22,
|
||||
GameSpellList = 23,
|
||||
GameClientPing = 24,
|
||||
GameExtendedClientPing = 25,
|
||||
GameDoubleHealth = 28,
|
||||
GameDoubleSkills = 29,
|
||||
GameChangeMapAwareRange = 30,
|
||||
GameMapMovePosition = 31,
|
||||
GameAttackSeq = 32,
|
||||
GameBlueNpcNameColor = 33,
|
||||
GameDiagonalAnimatedText = 34,
|
||||
GameLoginPending = 35,
|
||||
GameNewSpeedLaw = 36,
|
||||
GameForceFirstAutoWalkStep = 37,
|
||||
GameMinimapRemove = 38,
|
||||
GameDoubleShopSellAmount = 39,
|
||||
GameContainerPagination = 40,
|
||||
GameThingMarks = 41,
|
||||
GameLooktypeU16 = 42,
|
||||
GamePlayerStamina = 43,
|
||||
GamePlayerAddons = 44,
|
||||
GameMessageStatements = 45,
|
||||
GameMessageLevel = 46,
|
||||
GameNewFluids = 47,
|
||||
GamePlayerStateU16 = 48,
|
||||
GameNewOutfitProtocol = 49,
|
||||
GamePVPMode = 50,
|
||||
GameWritableDate = 51,
|
||||
GameAdditionalVipInfo = 52,
|
||||
GameBaseSkillU16 = 53,
|
||||
GameCreatureIcons = 54,
|
||||
GameHideNpcNames = 55,
|
||||
GameSpritesAlphaChannel = 56,
|
||||
GamePremiumExpiration = 57,
|
||||
GameBrowseField = 58,
|
||||
GameEnhancedAnimations = 59,
|
||||
GameOGLInformation = 60,
|
||||
GameMessageSizeCheck = 61,
|
||||
GamePreviewState = 62,
|
||||
GameLoginPacketEncryption = 63,
|
||||
GameClientVersion = 64,
|
||||
GameContentRevision = 65,
|
||||
GameExperienceBonus = 66,
|
||||
GameAuthenticator = 67,
|
||||
GameUnjustifiedPoints = 68,
|
||||
GameSessionKey = 69,
|
||||
GameDeathType = 70,
|
||||
GameIdleAnimations = 71,
|
||||
GameKeepUnawareTiles = 72,
|
||||
GameIngameStore = 73,
|
||||
GameIngameStoreHighlights = 74,
|
||||
GameIngameStoreServiceType = 75,
|
||||
GameAdditionalSkills = 76,
|
||||
GameDistanceEffectU16 = 77,
|
||||
GamePrey = 78,
|
||||
GameDoubleMagicLevel = 79,
|
||||
|
||||
GameExtendedOpcode = 80,
|
||||
GameMinimapLimitedToSingleFloor = 81,
|
||||
GameSendWorldName = 82,
|
||||
|
||||
GameDoubleLevel = 83,
|
||||
GameDoubleSoul = 84,
|
||||
GameDoublePlayerGoodsMoney = 85,
|
||||
GameCreatureWalkthrough = 86,
|
||||
GameDoubleTradeMoney = 87,
|
||||
GameSequencedPackets = 88,
|
||||
GameTibia12Protocol = 89,
|
||||
|
||||
// 90-99 otclientv8 features
|
||||
GameNewWalking = 90,
|
||||
GameSlowerManualWalking = 91,
|
||||
|
||||
GameItemTooltip = 93,
|
||||
|
||||
GameBot = 95,
|
||||
GameBiggerMapCache = 96,
|
||||
GameForceLight = 97,
|
||||
GameNoDebug = 98,
|
||||
GameBotProtection = 99,
|
||||
|
||||
// Custom features for customer
|
||||
GameFasterAnimations = 101,
|
||||
GameCenteredOutfits = 102,
|
||||
GameSendIdentifiers = 103,
|
||||
GameWingsAndAura = 104,
|
||||
GamePlayerStateU32 = 105,
|
||||
GameOutfitShaders = 106,
|
||||
|
||||
// advanced features
|
||||
GamePacketSizeU32 = 110,
|
||||
GamePacketCompression = 111,
|
||||
|
||||
LastGameFeature = 120
|
||||
};
|
||||
|
||||
enum ClientVersion_t : uint16_t {
|
||||
@@ -417,6 +539,22 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000;
|
||||
//[1000 - 1500];
|
||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000);
|
||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500;
|
||||
//[2001 - 2011];
|
||||
static constexpr int32_t PSTRG_MOUNTS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2001);
|
||||
static constexpr int32_t PSTRG_MOUNTS_RANGE_SIZE = 10;
|
||||
static constexpr int32_t PSTRG_MOUNTS_CURRENTMOUNT = (PSTRG_MOUNTS_RANGE_START + 10);
|
||||
//[2012 - 2022];
|
||||
static constexpr int32_t PSTRG_WINGS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2012);
|
||||
static constexpr int32_t PSTRG_WINGS_RANGE_SIZE = 10;
|
||||
static constexpr int32_t PSTRG_WINGS_CURRENTWINGS = (PSTRG_WINGS_RANGE_START + 10);
|
||||
//[2023 - 2033];
|
||||
static constexpr int32_t PSTRG_AURAS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2023);
|
||||
static constexpr int32_t PSTRG_AURAS_RANGE_SIZE = 10;
|
||||
static constexpr int32_t PSTRG_AURAS_CURRENTAURA = (PSTRG_AURAS_RANGE_START + 10);
|
||||
//[2034 - 2044];
|
||||
static constexpr int32_t PSTRG_SHADERS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2034);
|
||||
static constexpr int32_t PSTRG_SHADERS_RANGE_SIZE = 10;
|
||||
static constexpr int32_t PSTRG_SHADERS_CURRENTSHADER = (PSTRG_SHADERS_RANGE_START + 10);
|
||||
|
||||
#define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE))
|
||||
|
||||
|
||||
15
src/enums.h
15
src/enums.h
@@ -56,6 +56,7 @@ enum itemAttrTypes : uint32_t {
|
||||
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
|
||||
ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25,
|
||||
ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26,
|
||||
ITEM_ATTRIBUTE_AUTOOPEN = 1 << 27,
|
||||
};
|
||||
|
||||
enum VipStatus_t : uint8_t {
|
||||
@@ -93,6 +94,16 @@ enum OperatingSystem_t : uint8_t {
|
||||
CLIENTOS_OTCLIENT_LINUX = 10,
|
||||
CLIENTOS_OTCLIENT_WINDOWS = 11,
|
||||
CLIENTOS_OTCLIENT_MAC = 12,
|
||||
|
||||
// by default OTCv8 uses CLIENTOS_WINDOWS for backward compatibility
|
||||
// for correct value enable g_game.enableFeature(GameExtendedOpcode)
|
||||
// in modules/game_features/features.lua
|
||||
CLIENTOS_OTCLIENTV8_LINUX = 20,
|
||||
CLIENTOS_OTCLIENTV8_WINDOWS = 21,
|
||||
CLIENTOS_OTCLIENTV8_MAC = 22,
|
||||
CLIENTOS_OTCLIENTV8_ANDROID = 23,
|
||||
CLIENTOS_OTCLIENTV8_IOS = 24,
|
||||
CLIENTOS_OTCLIENTV8_WEB = 25
|
||||
};
|
||||
|
||||
enum AccountType_t : uint8_t {
|
||||
@@ -379,6 +390,10 @@ enum ReturnValue {
|
||||
struct Outfit_t {
|
||||
uint16_t lookType = 0;
|
||||
uint16_t lookTypeEx = 0;
|
||||
uint16_t lookMount = 0;
|
||||
uint16_t lookWings = 0;
|
||||
uint16_t lookAura = 0;
|
||||
uint16_t lookShader = 0;
|
||||
uint8_t lookHead = 0;
|
||||
uint8_t lookBody = 0;
|
||||
uint8_t lookLegs = 0;
|
||||
|
||||
115
src/game.cpp
115
src/game.cpp
@@ -106,6 +106,10 @@ void Game::setGameState(GameState_t newState)
|
||||
raids.startup();
|
||||
|
||||
quests.loadFromXml();
|
||||
mounts.loadFromXml();
|
||||
auras.loadFromXml();
|
||||
wings.loadFromXml();
|
||||
shaders.loadFromXml();
|
||||
|
||||
loadMotdNum();
|
||||
loadPlayersRecord();
|
||||
@@ -1866,6 +1870,18 @@ void Game::playerReceivePingBack(uint32_t playerId)
|
||||
player->sendPingBack();
|
||||
}
|
||||
|
||||
void Game::playerReceiveNewPing(uint32_t playerId, uint16_t ping, uint16_t fps)
|
||||
{
|
||||
Player* player = getPlayerByID(playerId);
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
player->receivePing();
|
||||
player->setLocalPing(ping);
|
||||
player->setFPS(fps);
|
||||
}
|
||||
|
||||
void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
|
||||
{
|
||||
Player* player = getPlayerByID(playerId);
|
||||
@@ -2906,6 +2922,17 @@ void Game::playerRequestOutfit(uint32_t playerId)
|
||||
player->sendOutfitWindow();
|
||||
}
|
||||
|
||||
void Game::playerToggleOutfitExtension(uint32_t playerId, int mount, int wings, int aura, int shader)
|
||||
{
|
||||
Player* player = getPlayerByID(playerId);
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mount != -1)
|
||||
player->toggleMount(mount == 1);
|
||||
}
|
||||
|
||||
void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
|
||||
{
|
||||
if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
|
||||
@@ -2917,6 +2944,74 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
|
||||
return;
|
||||
}
|
||||
|
||||
const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType);
|
||||
if (!playerOutfit) {
|
||||
outfit.lookMount = 0;
|
||||
outfit.lookWings = 0;
|
||||
outfit.lookAura = 0;
|
||||
outfit.lookShader = 0;
|
||||
}
|
||||
|
||||
if (outfit.lookMount != 0) {
|
||||
Mount* mount = mounts.getMountByClientID(outfit.lookMount);
|
||||
if (!mount) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->hasMount(mount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->isMounted()) {
|
||||
Mount* prevMount = mounts.getMountByID(player->getCurrentMount());
|
||||
if (prevMount) {
|
||||
changeSpeed(player, mount->speed - prevMount->speed);
|
||||
}
|
||||
|
||||
player->setCurrentMount(mount->id);
|
||||
}
|
||||
else {
|
||||
player->setCurrentMount(mount->id);
|
||||
outfit.lookMount = 0;
|
||||
}
|
||||
}
|
||||
else if (player->isMounted()) {
|
||||
player->dismount();
|
||||
}
|
||||
|
||||
if (outfit.lookWings != 0) {
|
||||
Wing* wing = wings.getWingByID(outfit.lookWings);
|
||||
if (!wing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->hasWing(wing)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (outfit.lookAura != 0) {
|
||||
Aura* aura = auras.getAuraByID(outfit.lookAura);
|
||||
if (!aura) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->hasAura(aura)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (outfit.lookShader) {
|
||||
Shader* shader = shaders.getShaderByID(outfit.lookShader);
|
||||
if (!shader) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!player->hasShader(shader)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->canWear(outfit.lookType, outfit.lookAddons)) {
|
||||
player->defaultOutfit = outfit;
|
||||
|
||||
@@ -5099,6 +5194,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
{
|
||||
switch (reloadType) {
|
||||
case RELOAD_TYPE_ACTIONS: return g_actions->reload();
|
||||
case RELOAD_TYPE_AURAS: return auras.reload();
|
||||
case RELOAD_TYPE_CHAT: return g_chat->load();
|
||||
case RELOAD_TYPE_CONFIG: return g_config.reload();
|
||||
case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload();
|
||||
@@ -5106,6 +5202,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload();
|
||||
case RELOAD_TYPE_ITEMS: return Item::items.reload();
|
||||
case RELOAD_TYPE_MONSTERS: return g_monsters.reload();
|
||||
case RELOAD_TYPE_MOUNTS: return mounts.reload();
|
||||
case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload();
|
||||
case RELOAD_TYPE_NPCS: {
|
||||
Npcs::reload();
|
||||
@@ -5114,7 +5211,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
|
||||
case RELOAD_TYPE_QUESTS: return quests.reload();
|
||||
case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup();
|
||||
|
||||
case RELOAD_TYPE_SHADERS: return shaders.reload();
|
||||
case RELOAD_TYPE_SPELLS: {
|
||||
if (!g_spells->reload()) {
|
||||
std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl;
|
||||
@@ -5128,6 +5225,7 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
}
|
||||
|
||||
case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload();
|
||||
case RELOAD_TYPE_WINGS: return wings.reload();
|
||||
|
||||
default: {
|
||||
if (!g_spells->reload()) {
|
||||
@@ -5151,6 +5249,10 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
g_talkActions->reload();
|
||||
Item::items.reload();
|
||||
quests.reload();
|
||||
auras.reload();
|
||||
mounts.reload();
|
||||
wings.reload();
|
||||
shaders.reload();
|
||||
g_globalEvents->reload();
|
||||
g_events->load();
|
||||
g_chat->load();
|
||||
@@ -5158,3 +5260,14 @@ bool Game::reload(ReloadTypes_t reloadType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::startProgressbar(Creature* creature, uint32_t duration, bool ltr)
|
||||
{
|
||||
SpectatorVec spectators;
|
||||
map.getSpectators(spectators, creature->getPosition(), false, true);
|
||||
for (Creature* spectator : spectators) {
|
||||
if (Player* tmpPlayer = spectator->getPlayer()) {
|
||||
tmpPlayer->sendProgressbar(creature->getID(), duration, ltr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "npc.h"
|
||||
#include "wildcardtree.h"
|
||||
#include "quests.h"
|
||||
#include "shaders.h"
|
||||
|
||||
class ServiceManager;
|
||||
class Creature;
|
||||
@@ -360,6 +361,7 @@ class Game
|
||||
void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver);
|
||||
void playerReceivePing(uint32_t playerId);
|
||||
void playerReceivePingBack(uint32_t playerId);
|
||||
void playerReceiveNewPing(uint32_t playerId, uint16_t ping, uint16_t fps);
|
||||
void playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir);
|
||||
void playerStopAutoWalk(uint32_t playerId);
|
||||
void playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos,
|
||||
@@ -399,6 +401,7 @@ class Game
|
||||
void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId);
|
||||
void playerLeaveParty(uint32_t playerId);
|
||||
void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive);
|
||||
void playerToggleOutfitExtension(uint32_t playerId, int mount, int wings, int aura, int shader);
|
||||
void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||
void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||
void playerCancelRuleViolationReport(uint32_t playerId);
|
||||
@@ -503,10 +506,16 @@ class Game
|
||||
void setBedSleeper(BedItem* bed, uint32_t guid);
|
||||
void removeBedSleeper(uint32_t guid);
|
||||
bool reload(ReloadTypes_t reloadType);
|
||||
void startProgressbar(Creature* creature, uint32_t duration, bool ltr = true);
|
||||
|
||||
Auras auras;
|
||||
Groups groups;
|
||||
Map map;
|
||||
Mounts mounts;
|
||||
Raids raids;
|
||||
Quests quests;
|
||||
Wings wings;
|
||||
Shaders shaders;
|
||||
|
||||
protected:
|
||||
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
|
||||
|
||||
@@ -525,7 +525,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream)
|
||||
bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream, std::map<Container*, int>& openContainers)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
@@ -540,6 +540,17 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
Item* item = it.second;
|
||||
++runningId;
|
||||
|
||||
if (Container* container = item->getContainer()) {
|
||||
auto it = openContainers.find(container);
|
||||
if (it == openContainers.end()) {
|
||||
container->resetAutoOpen();
|
||||
}
|
||||
else {
|
||||
container->setAutoOpen(it->second);
|
||||
}
|
||||
queue.emplace_back(container, runningId);
|
||||
}
|
||||
|
||||
propWriteStream.clear();
|
||||
item->serializeAttr(propWriteStream);
|
||||
|
||||
@@ -550,10 +561,6 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
if (!query_insert.addRow(ss)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Container* container = item->getContainer()) {
|
||||
queue.emplace_back(container, runningId);
|
||||
}
|
||||
}
|
||||
|
||||
while (!queue.empty()) {
|
||||
@@ -567,6 +574,14 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
|
||||
Container* subContainer = item->getContainer();
|
||||
if (subContainer) {
|
||||
auto it = openContainers.find(subContainer);
|
||||
if (it == openContainers.end()) {
|
||||
subContainer->resetAutoOpen();
|
||||
}
|
||||
else {
|
||||
subContainer->setAutoOpen(it->second);
|
||||
}
|
||||
|
||||
queue.emplace_back(subContainer, runningId);
|
||||
}
|
||||
|
||||
@@ -749,6 +764,11 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
|
||||
//item saving
|
||||
query.str(std::string());
|
||||
std::map<Container*, int> openContainers;
|
||||
for (auto container : player->getOpenContainers()) {
|
||||
if (!container.second.container) continue;
|
||||
openContainers[container.second.container] = container.first;
|
||||
}
|
||||
query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
@@ -764,7 +784,7 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveItems(player, itemList, itemsQuery, propWriteStream)) {
|
||||
if (!saveItems(player, itemList, itemsQuery, propWriteStream, openContainers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -783,7 +803,7 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
itemList.emplace_back(it.first, it.second);
|
||||
}
|
||||
|
||||
if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
|
||||
if (!saveItems(player, itemList, depotQuery, propWriteStream, openContainers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class IOLoginData
|
||||
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap;
|
||||
|
||||
static void loadItems(ItemMap& itemMap, DBResult_ptr result);
|
||||
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream);
|
||||
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream, std::map<Container*, int>& openContainers);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
16
src/item.cpp
16
src/item.cpp
@@ -551,6 +551,17 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream)
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_AUTOOPEN:
|
||||
{
|
||||
int8_t autoOpen;
|
||||
if (!propStream.read<int8_t>(autoOpen)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_AUTOOPEN, autoOpen);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_KEYNUMBER: {
|
||||
uint16_t keyNumber;
|
||||
if (!propStream.read<uint16_t>(keyNumber)) {
|
||||
@@ -788,6 +799,11 @@ void Item::serializeAttr(PropWriteStream& propWriteStream) const
|
||||
propWriteStream.write<uint8_t>(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE));
|
||||
}
|
||||
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_AUTOOPEN)) {
|
||||
propWriteStream.write<uint8_t>(ATTR_AUTOOPEN);
|
||||
propWriteStream.write<int8_t>(getIntAttr(ITEM_ATTRIBUTE_AUTOOPEN));
|
||||
}
|
||||
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_KEYNUMBER)) {
|
||||
propWriteStream.write<uint8_t>(ATTR_KEYNUMBER);
|
||||
propWriteStream.write<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER));
|
||||
|
||||
17
src/item.h
17
src/item.h
@@ -103,6 +103,7 @@ enum AttrTypes_t {
|
||||
ATTR_DEFENSE = 35,
|
||||
ATTR_ARMOR = 36,
|
||||
ATTR_SHOOTRANGE = 37,
|
||||
ATTR_AUTOOPEN = 38
|
||||
};
|
||||
|
||||
enum Attr_ReadValue {
|
||||
@@ -818,6 +819,22 @@ class Item : virtual public Thing
|
||||
return !parent || parent->isRemoved();
|
||||
}
|
||||
|
||||
int8_t getAutoOpen()
|
||||
{
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_AUTOOPEN)) {
|
||||
return getIntAttr(ITEM_ATTRIBUTE_AUTOOPEN);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
void setAutoOpen(int8_t value)
|
||||
{
|
||||
setIntAttr(ITEM_ATTRIBUTE_AUTOOPEN, value);
|
||||
}
|
||||
void resetAutoOpen()
|
||||
{
|
||||
removeAttribute(ITEM_ATTRIBUTE_AUTOOPEN);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string getWeightDescription(uint32_t weight) const;
|
||||
|
||||
|
||||
@@ -731,6 +731,9 @@ Position LuaScriptInterface::getPosition(lua_State* L, int32_t arg)
|
||||
Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg)
|
||||
{
|
||||
Outfit_t outfit;
|
||||
outfit.lookMount = getField<uint16_t>(L, arg, "lookMount");
|
||||
outfit.lookWings = getField<uint16_t>(L, arg, "lookWings");
|
||||
outfit.lookAura = getField<uint16_t>(L, arg, "lookAura");
|
||||
outfit.lookAddons = getField<uint8_t>(L, arg, "lookAddons");
|
||||
|
||||
outfit.lookFeet = getField<uint8_t>(L, arg, "lookFeet");
|
||||
@@ -741,7 +744,9 @@ Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg)
|
||||
outfit.lookTypeEx = getField<uint8_t>(L, arg, "lookTypeEx");
|
||||
outfit.lookType = getField<uint8_t>(L, arg, "lookType");
|
||||
|
||||
lua_pop(L, 6);
|
||||
outfit.lookShader = getField<uint16_t>(L, arg, "lookShader");
|
||||
|
||||
lua_pop(L, 8);
|
||||
return outfit;
|
||||
}
|
||||
|
||||
@@ -878,7 +883,7 @@ void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, in
|
||||
|
||||
void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit)
|
||||
{
|
||||
lua_createtable(L, 0, 6);
|
||||
lua_createtable(L, 0, 8);
|
||||
setField(L, "lookType", outfit.lookType);
|
||||
setField(L, "lookTypeEx", outfit.lookTypeEx);
|
||||
setField(L, "lookHead", outfit.lookHead);
|
||||
@@ -886,6 +891,10 @@ void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit)
|
||||
setField(L, "lookLegs", outfit.lookLegs);
|
||||
setField(L, "lookFeet", outfit.lookFeet);
|
||||
setField(L, "lookAddons", outfit.lookAddons);
|
||||
setField(L, "lookMount", outfit.lookMount);
|
||||
setField(L, "lookWings", outfit.lookWings);
|
||||
setField(L, "lookAura", outfit.lookAura);
|
||||
setField(L, "lookShader", outfit.lookShader);
|
||||
}
|
||||
|
||||
#define registerEnum(value) { std::string enumName = #value; registerGlobalVariable(enumName.substr(enumName.find_last_of(':') + 1), value); }
|
||||
@@ -1636,6 +1645,7 @@ void LuaScriptInterface::registerFunctions()
|
||||
|
||||
registerEnum(RELOAD_TYPE_ALL)
|
||||
registerEnum(RELOAD_TYPE_ACTIONS)
|
||||
registerEnum(RELOAD_TYPE_AURAS)
|
||||
registerEnum(RELOAD_TYPE_CHAT)
|
||||
registerEnum(RELOAD_TYPE_COMMANDS)
|
||||
registerEnum(RELOAD_TYPE_CONFIG)
|
||||
@@ -1651,8 +1661,10 @@ void LuaScriptInterface::registerFunctions()
|
||||
registerEnum(RELOAD_TYPE_QUESTS)
|
||||
registerEnum(RELOAD_TYPE_RAIDS)
|
||||
registerEnum(RELOAD_TYPE_SPELLS)
|
||||
registerEnum(RELOAD_TYPE_SHADERS)
|
||||
registerEnum(RELOAD_TYPE_TALKACTIONS)
|
||||
registerEnum(RELOAD_TYPE_WEAPONS)
|
||||
registerEnum(RELOAD_TYPE_WINGS)
|
||||
|
||||
// _G
|
||||
registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER);
|
||||
@@ -2015,6 +2027,8 @@ void LuaScriptInterface::registerFunctions()
|
||||
|
||||
registerMethod("Creature", "getPathTo", LuaScriptInterface::luaCreatureGetPathTo);
|
||||
|
||||
registerMethod("Creature", "sendProgressbar", LuaScriptInterface::luaCreatureSetProgressbar);
|
||||
|
||||
// Player
|
||||
registerClass("Player", "Creature", LuaScriptInterface::luaPlayerCreate);
|
||||
registerMetaMethod("Player", "__eq", LuaScriptInterface::luaUserdataCompare);
|
||||
@@ -2138,6 +2152,10 @@ void LuaScriptInterface::registerFunctions()
|
||||
registerMethod("Player", "hasOutfit", LuaScriptInterface::luaPlayerHasOutfit);
|
||||
registerMethod("Player", "sendOutfitWindow", LuaScriptInterface::luaPlayerSendOutfitWindow);
|
||||
|
||||
registerMethod("Player", "addMount", LuaScriptInterface::luaPlayerAddMount);
|
||||
registerMethod("Player", "removeMount", LuaScriptInterface::luaPlayerRemoveMount);
|
||||
registerMethod("Player", "hasMount", LuaScriptInterface::luaPlayerHasMount);
|
||||
|
||||
registerMethod("Player", "getPremiumDays", LuaScriptInterface::luaPlayerGetPremiumDays);
|
||||
registerMethod("Player", "addPremiumDays", LuaScriptInterface::luaPlayerAddPremiumDays);
|
||||
registerMethod("Player", "removePremiumDays", LuaScriptInterface::luaPlayerRemovePremiumDays);
|
||||
@@ -8597,6 +8615,79 @@ int LuaScriptInterface::luaPlayerSendOutfitWindow(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerAddMount(lua_State* L) {
|
||||
// player:addMount(mountId or mountName)
|
||||
Player* player = getUserdata<Player>(L, 1);
|
||||
if (!player) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t mountId;
|
||||
if (isNumber(L, 2)) {
|
||||
mountId = getNumber<uint8_t>(L, 2);
|
||||
}
|
||||
else {
|
||||
Mount* mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||
if (!mount) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
mountId = mount->id;
|
||||
}
|
||||
pushBoolean(L, player->tameMount(mountId));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerRemoveMount(lua_State* L) {
|
||||
// player:removeMount(mountId or mountName)
|
||||
Player* player = getUserdata<Player>(L, 1);
|
||||
if (!player) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t mountId;
|
||||
if (isNumber(L, 2)) {
|
||||
mountId = getNumber<uint8_t>(L, 2);
|
||||
}
|
||||
else {
|
||||
Mount* mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||
if (!mount) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
mountId = mount->id;
|
||||
}
|
||||
pushBoolean(L, player->untameMount(mountId));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerHasMount(lua_State* L) {
|
||||
// player:hasMount(mountId or mountName)
|
||||
const Player* player = getUserdata<const Player>(L, 1);
|
||||
if (!player) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Mount* mount = nullptr;
|
||||
if (isNumber(L, 2)) {
|
||||
mount = g_game.mounts.getMountByID(getNumber<uint8_t>(L, 2));
|
||||
}
|
||||
else {
|
||||
mount = g_game.mounts.getMountByName(getString(L, 2));
|
||||
}
|
||||
|
||||
if (mount) {
|
||||
pushBoolean(L, player->hasMount(mount));
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerGetPremiumDays(lua_State* L)
|
||||
{
|
||||
// player:getPremiumDays()
|
||||
@@ -8823,10 +8914,14 @@ int LuaScriptInterface::luaPlayerGetClient(lua_State* L)
|
||||
// player:getClient()
|
||||
Player* player = getUserdata<Player>(L, 1);
|
||||
if (player) {
|
||||
lua_createtable(L, 0, 2);
|
||||
lua_createtable(L, 0, 5);
|
||||
setField(L, "version", player->getProtocolVersion());
|
||||
setField(L, "os", player->getOperatingSystem());
|
||||
} else {
|
||||
setField(L, "otcv8", player->getOTCv8Version());
|
||||
setField(L, "ping", player->getLocalPing());
|
||||
setField(L, "fps", player->getFPS());
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
return 1;
|
||||
@@ -11294,11 +11389,15 @@ int LuaScriptInterface::luaConditionSetSpeedDelta(lua_State* L)
|
||||
int LuaScriptInterface::luaConditionSetOutfit(lua_State* L)
|
||||
{
|
||||
// condition:setOutfit(outfit)
|
||||
// condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons])
|
||||
// condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons[, lookMount, lookWings, lookAura, lookShader]])
|
||||
Outfit_t outfit;
|
||||
if (isTable(L, 2)) {
|
||||
outfit = getOutfit(L, 2);
|
||||
} else {
|
||||
outfit.lookShader = getNumber<uint16_t>(L, 12, outfit.lookShader);
|
||||
outfit.lookAura = getNumber<uint16_t>(L, 11, outfit.lookAura);
|
||||
outfit.lookWings = getNumber<uint16_t>(L, 10, outfit.lookWings);
|
||||
outfit.lookMount = getNumber<uint16_t>(L, 9, outfit.lookMount);
|
||||
outfit.lookAddons = getNumber<uint8_t>(L, 8, outfit.lookAddons);
|
||||
outfit.lookFeet = getNumber<uint8_t>(L, 7);
|
||||
outfit.lookLegs = getNumber<uint8_t>(L, 6);
|
||||
@@ -12277,3 +12376,20 @@ void LuaEnvironment::executeTimerEvent(uint32_t eventIndex)
|
||||
luaL_unref(luaState, LUA_REGISTRYINDEX, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaCreatureSetProgressbar(lua_State* L)
|
||||
{
|
||||
// creature:sendProgressbar(duration, leftToRight)
|
||||
Creature* creature = getUserdata<Creature>(L, 1);
|
||||
uint32_t duration = getNumber<uint32_t>(L, 2);
|
||||
bool ltr = getBoolean(L, 3);
|
||||
if (creature) {
|
||||
g_game.startProgressbar(creature, duration, ltr);
|
||||
pushBoolean(L, true);
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -786,6 +786,8 @@ class LuaScriptInterface
|
||||
|
||||
static int luaCreatureGetPathTo(lua_State* L);
|
||||
|
||||
static int luaCreatureSetProgressbar(lua_State* L);
|
||||
|
||||
// Player
|
||||
static int luaPlayerCreate(lua_State* L);
|
||||
|
||||
@@ -909,6 +911,10 @@ class LuaScriptInterface
|
||||
static int luaPlayerHasOutfit(lua_State* L);
|
||||
static int luaPlayerSendOutfitWindow(lua_State* L);
|
||||
|
||||
static int luaPlayerAddMount(lua_State* L);
|
||||
static int luaPlayerRemoveMount(lua_State* L);
|
||||
static int luaPlayerHasMount(lua_State* L);
|
||||
|
||||
static int luaPlayerGetPremiumDays(lua_State* L);
|
||||
static int luaPlayerAddPremiumDays(lua_State* L);
|
||||
static int luaPlayerRemovePremiumDays(lua_State* L);
|
||||
|
||||
@@ -775,6 +775,23 @@ bool Monsters::loadMonster(const std::string& file, const std::string& monsterNa
|
||||
std::cout << "[Warning - Monsters::loadMonster] Missing look type/typeex. " << file << std::endl;
|
||||
}
|
||||
|
||||
if ((attr = node.attribute("mount"))) {
|
||||
mType->info.outfit.lookMount = pugi::cast<uint16_t>(attr.value());
|
||||
}
|
||||
|
||||
if ((attr = node.attribute("aura"))) {
|
||||
mType->info.outfit.lookAura = pugi::cast<uint16_t>(attr.value());
|
||||
}
|
||||
|
||||
if ((attr = node.attribute("wings"))) {
|
||||
mType->info.outfit.lookWings = pugi::cast<uint16_t>(attr.value());
|
||||
}
|
||||
|
||||
if ((attr = node.attribute("shader"))) {
|
||||
Shader* shader = g_game.shaders.getShaderByName(attr.as_string());
|
||||
mType->info.outfit.lookShader = shader ? shader->id : 0;
|
||||
}
|
||||
|
||||
if ((attr = node.attribute("corpse"))) {
|
||||
mType->info.lookcorpse = pugi::cast<uint16_t>(attr.value());
|
||||
}
|
||||
|
||||
82
src/mounts.cpp
Normal file
82
src/mounts.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "mounts.h"
|
||||
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Mounts::reload()
|
||||
{
|
||||
mounts.clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
bool Mounts::loadFromXml()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file("data/XML/mounts.xml");
|
||||
if (!result) {
|
||||
printXMLError("Error - Mounts::loadFromXml", "data/XML/mounts.xml", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto mountNode : doc.child("mounts").children()) {
|
||||
mounts.emplace_back(
|
||||
static_cast<uint8_t>(pugi::cast<uint16_t>(mountNode.attribute("id").value())),
|
||||
pugi::cast<uint16_t>(mountNode.attribute("clientid").value()),
|
||||
mountNode.attribute("name").as_string(),
|
||||
pugi::cast<int32_t>(mountNode.attribute("speed").value()),
|
||||
mountNode.attribute("premium").as_bool()
|
||||
);
|
||||
}
|
||||
mounts.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Mount* Mounts::getMountByID(uint8_t id)
|
||||
{
|
||||
auto it = std::find_if(mounts.begin(), mounts.end(), [id](const Mount& mount) {
|
||||
return mount.id == id;
|
||||
});
|
||||
|
||||
return it != mounts.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Mount* Mounts::getMountByName(const std::string& name) {
|
||||
auto mountName = name.c_str();
|
||||
for (auto& it : mounts) {
|
||||
if (strcasecmp(mountName, it.name.c_str()) == 0) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mount* Mounts::getMountByClientID(uint16_t clientId)
|
||||
{
|
||||
auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const Mount& mount) {
|
||||
return mount.clientId == clientId;
|
||||
});
|
||||
|
||||
return it != mounts.end() ? &*it : nullptr;
|
||||
}
|
||||
52
src/mounts.h
Normal file
52
src/mounts.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||
#define FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||
|
||||
struct Mount
|
||||
{
|
||||
Mount(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||
|
||||
std::string name;
|
||||
int32_t speed;
|
||||
uint16_t clientId;
|
||||
uint8_t id;
|
||||
bool premium;
|
||||
};
|
||||
|
||||
class Mounts
|
||||
{
|
||||
public:
|
||||
bool reload();
|
||||
bool loadFromXml();
|
||||
Mount* getMountByID(uint8_t id);
|
||||
Mount* getMountByName(const std::string& name);
|
||||
Mount* getMountByClientID(uint16_t clientId);
|
||||
|
||||
const std::vector<Mount>& getMounts() const {
|
||||
return mounts;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Mount> mounts;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -51,7 +51,7 @@ Position NetworkMessage::getPosition()
|
||||
void NetworkMessage::addString(const std::string& value)
|
||||
{
|
||||
size_t stringLen = value.length();
|
||||
if (!canAdd(stringLen + 2) || stringLen > 8192) {
|
||||
if (!canAdd(stringLen + 2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ void NetworkMessage::addDouble(double value, uint8_t precision/* = 2*/)
|
||||
|
||||
void NetworkMessage::addBytes(const char* bytes, size_t size)
|
||||
{
|
||||
if (!canAdd(size) || size > 8192) {
|
||||
if (!canAdd(size)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,14 @@ public:
|
||||
add_header(info.length);
|
||||
}
|
||||
|
||||
void addCryptoHeader() {
|
||||
void addCryptoHeader(bool addChecksum, bool compression = false) {
|
||||
if (compression) {
|
||||
add_header<uint32_t>(0);
|
||||
}
|
||||
else if (addChecksum) {
|
||||
//add_header(adlerChecksum(buffer + outputBufferStart, info.length));
|
||||
}
|
||||
|
||||
writeMessageLength();
|
||||
}
|
||||
|
||||
|
||||
283
src/player.cpp
283
src/player.cpp
@@ -528,6 +528,9 @@ void Player::addStorageValue(const uint32_t key, const int32_t value, const bool
|
||||
);
|
||||
return;
|
||||
}
|
||||
else if (IS_IN_KEYRANGE(key, MOUNTS_RANGE) || IS_IN_KEYRANGE(key, WINGS_RANGE) || IS_IN_KEYRANGE(key, AURAS_RANGE)) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
|
||||
return;
|
||||
@@ -684,6 +687,31 @@ void Player::sendPing()
|
||||
}
|
||||
}
|
||||
|
||||
void Player::autoOpenContainers()
|
||||
{
|
||||
for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
|
||||
Item* item = inventory[i];
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Container* container = item->getContainer()) {
|
||||
if (container->getAutoOpen() >= 0) {
|
||||
addContainer(container->getAutoOpen(), container);
|
||||
onSendContainer(container);
|
||||
}
|
||||
for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
|
||||
if (Container* subContainer = (*it)->getContainer()) {
|
||||
if (subContainer->getAutoOpen() >= 0) {
|
||||
addContainer(subContainer->getAutoOpen(), subContainer);
|
||||
onSendContainer(subContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
|
||||
{
|
||||
windowTextId = this->windowTextId;
|
||||
@@ -3034,6 +3062,11 @@ void Player::updateItemsLight(bool internal /*=false*/)
|
||||
void Player::onAddCondition(ConditionType_t type)
|
||||
{
|
||||
Creature::onAddCondition(type);
|
||||
|
||||
if (type == CONDITION_OUTFIT) {
|
||||
dismount();
|
||||
}
|
||||
|
||||
sendIcons();
|
||||
}
|
||||
|
||||
@@ -3825,6 +3858,256 @@ void Player::sendClosePrivate(uint16_t channelId)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Player::getCurrentMount() const
|
||||
{
|
||||
int32_t value;
|
||||
if (getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, value)) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Player::setCurrentMount(uint8_t mountId)
|
||||
{
|
||||
addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mountId);
|
||||
}
|
||||
|
||||
bool Player::toggleMount(bool mount)
|
||||
{
|
||||
if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) {
|
||||
sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mount) {
|
||||
if (isMounted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
|
||||
sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType);
|
||||
if (!playerOutfit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t currentMountId = getCurrentMount();
|
||||
if (currentMountId == 0) {
|
||||
sendOutfitWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
Mount* currentMount = g_game.mounts.getMountByID(currentMountId);
|
||||
if (!currentMount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasMount(currentMount)) {
|
||||
setCurrentMount(0);
|
||||
sendOutfitWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentMount->premium && !isPremium()) {
|
||||
sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasCondition(CONDITION_OUTFIT)) {
|
||||
sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
defaultOutfit.lookMount = currentMount->clientId;
|
||||
|
||||
if (currentMount->speed != 0) {
|
||||
g_game.changeSpeed(this, currentMount->speed);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!isMounted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dismount();
|
||||
}
|
||||
|
||||
g_game.internalCreatureChangeOutfit(this, defaultOutfit);
|
||||
lastToggleMount = OTSYS_TIME();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::tameMount(uint8_t mountId)
|
||||
{
|
||||
if (!g_game.mounts.getMountByID(mountId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpMountId = mountId - 1;
|
||||
const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
|
||||
|
||||
int32_t value;
|
||||
if (getStorageValue(key, value)) {
|
||||
value |= (1 << (tmpMountId % 31));
|
||||
}
|
||||
else {
|
||||
value = (1 << (tmpMountId % 31));
|
||||
}
|
||||
|
||||
addStorageValue(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::untameMount(uint8_t mountId)
|
||||
{
|
||||
if (!g_game.mounts.getMountByID(mountId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpMountId = mountId - 1;
|
||||
const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
|
||||
|
||||
int32_t value;
|
||||
if (!getStorageValue(key, value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
value &= ~(1 << (tmpMountId % 31));
|
||||
addStorageValue(key, value);
|
||||
|
||||
if (getCurrentMount() == mountId) {
|
||||
if (isMounted()) {
|
||||
dismount();
|
||||
g_game.internalCreatureChangeOutfit(this, defaultOutfit);
|
||||
}
|
||||
|
||||
setCurrentMount(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Player::hasMount(const Mount* mount) const
|
||||
{
|
||||
if (isAccessPlayer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mount->premium && !isPremium()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpMountId = mount->id - 1;
|
||||
|
||||
int32_t value;
|
||||
if (!getStorageValue(PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31), value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((1 << (tmpMountId % 31)) & value) != 0;
|
||||
}
|
||||
|
||||
void Player::dismount()
|
||||
{
|
||||
Mount* mount = g_game.mounts.getMountByID(getCurrentMount());
|
||||
if (mount && mount->speed > 0) {
|
||||
g_game.changeSpeed(this, -mount->speed);
|
||||
}
|
||||
|
||||
defaultOutfit.lookMount = 0;
|
||||
}
|
||||
|
||||
bool Player::hasWing(const Wing* wing) const
|
||||
{
|
||||
if (isAccessPlayer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (wing->premium && !isPremium()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpWingId = wing->id - 1;
|
||||
|
||||
int32_t value;
|
||||
if (!getStorageValue(PSTRG_WINGS_RANGE_START + (tmpWingId / 31), value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((1 << (tmpWingId % 31)) & value) != 0;
|
||||
}
|
||||
|
||||
uint8_t Player::getCurrentWing() const
|
||||
{
|
||||
int32_t value;
|
||||
if (getStorageValue(PSTRG_WINGS_CURRENTWINGS, value)) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Player::setCurrentWing(uint8_t wingId)
|
||||
{
|
||||
addStorageValue(PSTRG_WINGS_CURRENTWINGS, wingId);
|
||||
}
|
||||
|
||||
bool Player::hasAura(const Aura* aura) const
|
||||
{
|
||||
if (isAccessPlayer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aura->premium && !isPremium()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpAuraId = aura->id - 1;
|
||||
|
||||
int32_t value;
|
||||
if (!getStorageValue(PSTRG_AURAS_RANGE_START + (tmpAuraId / 31), value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((1 << (tmpAuraId % 31)) & value) != 0;
|
||||
}
|
||||
|
||||
uint8_t Player::getCurrentAura() const
|
||||
{
|
||||
int32_t value;
|
||||
if (getStorageValue(PSTRG_AURAS_CURRENTAURA, value)) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Player::setCurrentAura(uint8_t auraId)
|
||||
{
|
||||
addStorageValue(PSTRG_AURAS_CURRENTAURA, auraId);
|
||||
}
|
||||
|
||||
bool Player::hasShader(const Shader* shader) const
|
||||
{
|
||||
if (isAccessPlayer()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shader->premium && !isPremium()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t tmpShaderId = shader->id - 1;
|
||||
|
||||
int32_t value;
|
||||
if (!getStorageValue(PSTRG_SHADERS_RANGE_START + (tmpShaderId / 31), value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((1 << (tmpShaderId % 31)) & value) != 0;
|
||||
}
|
||||
|
||||
bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
|
||||
{
|
||||
if (tries == 0 || skill == SKILL_LEVEL) {
|
||||
|
||||
78
src/player.h
78
src/player.h
@@ -33,6 +33,10 @@
|
||||
#include "guild.h"
|
||||
#include "groups.h"
|
||||
#include "town.h"
|
||||
#include "mounts.h"
|
||||
#include "auras.h"
|
||||
#include "wings.h"
|
||||
#include "shaders.h"
|
||||
|
||||
class BehaviourDatabase;
|
||||
class House;
|
||||
@@ -131,6 +135,42 @@ class Player final : public Creature, public Cylinder
|
||||
}
|
||||
std::string getDescription(int32_t lookDistance) const final;
|
||||
|
||||
uint8_t getCurrentMount() const;
|
||||
void setCurrentMount(uint8_t mountId);
|
||||
bool isMounted() const
|
||||
{
|
||||
return defaultOutfit.lookMount != 0;
|
||||
}
|
||||
bool hasMount() const
|
||||
{
|
||||
return defaultOutfit.lookMount != 0;
|
||||
}
|
||||
bool hasAura() const
|
||||
{
|
||||
return defaultOutfit.lookAura != 0;
|
||||
}
|
||||
bool hasWings() const
|
||||
{
|
||||
return defaultOutfit.lookWings != 0;
|
||||
}
|
||||
bool hasShader() const
|
||||
{
|
||||
return defaultOutfit.lookShader != 0;
|
||||
}
|
||||
bool toggleMount(bool mount);
|
||||
bool tameMount(uint8_t mountId);
|
||||
bool untameMount(uint8_t mountId);
|
||||
bool hasMount(const Mount* mount) const;
|
||||
void dismount();
|
||||
|
||||
bool hasWing(const Wing* wing) const;
|
||||
uint8_t getCurrentAura() const;
|
||||
void setCurrentAura(uint8_t auraId);
|
||||
bool hasAura(const Aura* aura) const;
|
||||
uint8_t getCurrentWing() const;
|
||||
void setCurrentWing(uint8_t wingId);
|
||||
bool hasShader(const Shader* shader) const;
|
||||
|
||||
void setGUID(uint32_t guid) {
|
||||
this->guid = guid;
|
||||
}
|
||||
@@ -761,6 +801,7 @@ class Player final : public Creature, public Cylinder
|
||||
client->sendInventoryItem(slot, item);
|
||||
}
|
||||
}
|
||||
void autoOpenContainers();
|
||||
|
||||
//event methods
|
||||
void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
|
||||
@@ -997,10 +1038,39 @@ class Player final : public Creature, public Cylinder
|
||||
}
|
||||
}
|
||||
|
||||
void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true) {
|
||||
if (client) {
|
||||
client->sendProgressbar(id, duration, ltr);
|
||||
}
|
||||
}
|
||||
|
||||
void receivePing() {
|
||||
lastPong = OTSYS_TIME();
|
||||
}
|
||||
|
||||
void setFPS(uint16_t value)
|
||||
{
|
||||
fps = value;
|
||||
}
|
||||
void setLocalPing(uint16_t value)
|
||||
{
|
||||
localPing = value;
|
||||
}
|
||||
uint16_t getFPS() const
|
||||
{
|
||||
return fps;
|
||||
}
|
||||
uint16_t getLocalPing() const
|
||||
{
|
||||
return localPing;
|
||||
}
|
||||
uint16_t getOTCv8Version() const
|
||||
{
|
||||
if (client)
|
||||
return client->otclientV8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void onThink(uint32_t interval) final;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||
@@ -1025,6 +1095,10 @@ class Player final : public Creature, public Cylinder
|
||||
void learnInstantSpell(const std::string& spellName);
|
||||
void forgetInstantSpell(const std::string& spellName);
|
||||
bool hasLearnedInstantSpell(const std::string& spellName) const;
|
||||
const std::map<uint8_t, OpenContainer>& getOpenContainers() const
|
||||
{
|
||||
return openContainers;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::forward_list<Condition*> getMuteConditions() const;
|
||||
@@ -1115,6 +1189,7 @@ class Player final : public Creature, public Cylinder
|
||||
int64_t lastPong;
|
||||
int64_t nextAction = 0;
|
||||
int64_t earliestAttackTime = 0;
|
||||
int64_t lastToggleMount = 0;
|
||||
|
||||
BedItem* bedItem = nullptr;
|
||||
Guild* guild = nullptr;
|
||||
@@ -1162,6 +1237,8 @@ class Player final : public Creature, public Cylinder
|
||||
|
||||
uint16_t staminaMinutes = 3360;
|
||||
uint16_t maxWriteLen = 0;
|
||||
uint16_t localPing = 0;
|
||||
uint16_t fps = 0;
|
||||
int16_t lastDepotId = -1;
|
||||
|
||||
uint8_t soul = 0;
|
||||
@@ -1179,6 +1256,7 @@ class Player final : public Creature, public Cylinder
|
||||
|
||||
bool secureMode = false;
|
||||
bool inMarket = false;
|
||||
bool wasMounted = false;
|
||||
bool ghostMode = false;
|
||||
bool pzLocked = false;
|
||||
bool isConnecting = false;
|
||||
|
||||
@@ -25,14 +25,27 @@
|
||||
|
||||
extern RSA g_RSA;
|
||||
|
||||
Protocol::~Protocol()
|
||||
{
|
||||
if (compression) {
|
||||
deflateEnd(&zstream);
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
|
||||
{
|
||||
if (!rawMessages) {
|
||||
bool compressed = false;
|
||||
if (compression && msg->getLength() > 64) {
|
||||
compress(*msg);
|
||||
compressed = true;
|
||||
}
|
||||
|
||||
msg->writeMessageLength();
|
||||
|
||||
if (encryptionEnabled) {
|
||||
XTEA_encrypt(*msg);
|
||||
msg->addCryptoHeader();
|
||||
msg->addCryptoHeader(checksumEnabled, compressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,3 +166,34 @@ uint32_t Protocol::getIP() const
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Protocol::enableCompression()
|
||||
{
|
||||
if (compression)
|
||||
return;
|
||||
if (deflateInit2(&zstream, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
std::cerr << "ZLIB initialization error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||
}
|
||||
compression = true;
|
||||
}
|
||||
|
||||
void Protocol::compress(OutputMessage& msg) const
|
||||
{
|
||||
static thread_local std::vector<uint8_t> buffer(NETWORKMESSAGE_MAXSIZE);
|
||||
zstream.next_in = msg.getOutputBuffer();
|
||||
zstream.avail_in = msg.getLength();
|
||||
zstream.next_out = buffer.data();
|
||||
zstream.avail_out = buffer.size();
|
||||
if (deflate(&zstream, Z_SYNC_FLUSH) != Z_OK) {
|
||||
std::cerr << "ZLIB deflate error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||
return;
|
||||
}
|
||||
int finalSize = buffer.size() - zstream.avail_out - 4;
|
||||
if (finalSize < 0) {
|
||||
std::cerr << "Packet compression error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
msg.reset();
|
||||
msg.addBytes((const char*)buffer.data(), finalSize);
|
||||
}
|
||||
@@ -21,12 +21,13 @@
|
||||
#define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
|
||||
|
||||
#include "connection.h"
|
||||
#include <zlib.h>
|
||||
|
||||
class Protocol : public std::enable_shared_from_this<Protocol>
|
||||
{
|
||||
public:
|
||||
explicit Protocol(Connection_ptr connection) : connection(connection), key(), encryptionEnabled(false), rawMessages(false) {}
|
||||
virtual ~Protocol() = default;
|
||||
virtual ~Protocol();
|
||||
|
||||
// non-copyable
|
||||
Protocol(const Protocol&) = delete;
|
||||
@@ -74,6 +75,9 @@ protected:
|
||||
void setXTEAKey(const uint32_t* key) {
|
||||
memcpy(this->key, key, sizeof(*key) * 4);
|
||||
}
|
||||
void disableChecksum() {
|
||||
checksumEnabled = false;
|
||||
}
|
||||
|
||||
void XTEA_encrypt(OutputMessage& msg) const;
|
||||
bool XTEA_decrypt(NetworkMessage& msg) const;
|
||||
@@ -82,16 +86,20 @@ protected:
|
||||
void setRawMessages(bool value) {
|
||||
rawMessages = value;
|
||||
}
|
||||
|
||||
void enableCompression();
|
||||
virtual void release() {}
|
||||
friend class Connection;
|
||||
|
||||
OutputMessage_ptr outputBuffer;
|
||||
private:
|
||||
void compress(OutputMessage& msg) const;
|
||||
const ConnectionWeak_ptr connection;
|
||||
uint32_t key[4];
|
||||
bool encryptionEnabled;
|
||||
bool rawMessages;
|
||||
bool encryptionEnabled = false;
|
||||
bool checksumEnabled = true;
|
||||
bool rawMessages = false;
|
||||
bool compression = false;
|
||||
mutable z_stream zstream = { 0 };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,17 @@ void ProtocolGame::release()
|
||||
|
||||
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem, bool isFake)
|
||||
{
|
||||
// OTCv8 features and extended opcodes
|
||||
if (otclientV8 || operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
||||
if (otclientV8)
|
||||
sendFeatures();
|
||||
NetworkMessage opcodeMessage;
|
||||
opcodeMessage.addByte(0x32);
|
||||
opcodeMessage.addByte(0x00);
|
||||
opcodeMessage.add<uint16_t>(0x00);
|
||||
writeToOutputBuffer(opcodeMessage);
|
||||
}
|
||||
|
||||
//dispatcher thread
|
||||
Player* foundPlayer = g_game.getPlayerByName(name);
|
||||
if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
|
||||
@@ -145,6 +156,8 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS
|
||||
}
|
||||
}
|
||||
|
||||
player->autoOpenContainers();
|
||||
|
||||
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
||||
player->registerCreatureEvent("ExtendedOpcode");
|
||||
}
|
||||
@@ -197,6 +210,7 @@ void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
|
||||
|
||||
player->client = getThis();
|
||||
sendAddCreature(player, player->getPosition(), 0, false);
|
||||
player->autoOpenContainers();
|
||||
player->lastIP = player->getIP();
|
||||
player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
|
||||
acceptPackets = true;
|
||||
@@ -263,20 +277,18 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
|
||||
enableXTEAEncryption();
|
||||
setXTEAKey(key);
|
||||
|
||||
if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
|
||||
NetworkMessage opcodeMessage;
|
||||
opcodeMessage.addByte(0x32);
|
||||
opcodeMessage.addByte(0x00);
|
||||
opcodeMessage.add<uint16_t>(0x00);
|
||||
writeToOutputBuffer(opcodeMessage);
|
||||
}
|
||||
|
||||
msg.skipBytes(1); // gamemaster flag
|
||||
|
||||
uint32_t accountNumber = msg.get<uint32_t>();
|
||||
std::string characterName = msg.getString();
|
||||
std::string password = msg.getString();
|
||||
|
||||
// OTCv8 version detection
|
||||
uint16_t otcV8StringLength = msg.get<uint16_t>();
|
||||
if (otcV8StringLength == 5 && msg.getString(5) == "OTCv8") {
|
||||
otclientV8 = msg.get<uint16_t>(); // 253, 260, 261, ...
|
||||
}
|
||||
|
||||
/*if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
|
||||
//sendUpdateRequest();
|
||||
disconnectClient("Use Tibia 7.72 to login!");
|
||||
@@ -338,8 +350,31 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
|
||||
|
||||
void ProtocolGame::onConnect()
|
||||
{
|
||||
// Checksum missimplementation
|
||||
/* auto output = OutputMessagePool::getOutputMessage();
|
||||
static std::random_device rd;
|
||||
static std::ranlux24 generator(rd());
|
||||
static std::uniform_int_distribution<uint16_t> randNumber(0x00, 0xFF);
|
||||
|
||||
// Skip checksum
|
||||
output->skipBytes(sizeof(uint32_t));
|
||||
|
||||
// Packet length & type
|
||||
output->add<uint16_t>(0x0006);
|
||||
output->addByte(0x1F);
|
||||
|
||||
// Add timestamp & random number
|
||||
challengeTimestamp = static_cast<uint32_t>(time(nullptr));
|
||||
output->add<uint32_t>(challengeTimestamp);
|
||||
|
||||
challengeRandom = randNumber(generator);
|
||||
output->addByte(challengeRandom);
|
||||
|
||||
// Go back and write checksum
|
||||
output->skipBytes(-12);
|
||||
output->add<uint32_t>(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8));
|
||||
|
||||
send(output); */
|
||||
}
|
||||
|
||||
void ProtocolGame::disconnectClient(const std::string& message) const
|
||||
@@ -390,6 +425,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg)
|
||||
case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
|
||||
case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
|
||||
case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
|
||||
case 0x40: parseNewPing(msg); break;
|
||||
case 0x64: parseAutoWalk(msg); break;
|
||||
case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
|
||||
case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
|
||||
@@ -445,6 +481,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg)
|
||||
case 0xCC: parseSeekInContainer(msg); break;
|
||||
case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
|
||||
case 0xD3: parseSetOutfit(msg); break;
|
||||
case 0xD4: parseToggleMount(msg); break;
|
||||
case 0xDC: parseAddVip(msg); break;
|
||||
case 0xDD: parseRemoveVip(msg); break;
|
||||
case 0xE6: parseBugReport(msg); break;
|
||||
@@ -735,9 +772,27 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg)
|
||||
newOutfit.lookLegs = msg.getByte();
|
||||
newOutfit.lookFeet = msg.getByte();
|
||||
newOutfit.lookAddons = msg.getByte();
|
||||
newOutfit.lookMount = msg.get<uint16_t>();
|
||||
newOutfit.lookWings = otclientV8 ? msg.get<uint16_t>() : 0;
|
||||
newOutfit.lookAura = otclientV8 ? msg.get<uint16_t>() : 0;
|
||||
std::string shaderName = otclientV8 ? msg.getString() : "";
|
||||
Shader* shader = g_game.shaders.getShaderByName(shaderName);
|
||||
newOutfit.lookShader = shader ? shader->id : 0;
|
||||
addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseToggleMount(NetworkMessage& msg)
|
||||
{
|
||||
int mount = msg.get<int8_t>();
|
||||
int wings = -1, aura = -1, shader = -1;
|
||||
if (otclientV8 >= 254) {
|
||||
wings = msg.get<int8_t>();
|
||||
aura = msg.get<int8_t>();
|
||||
shader = msg.get<int8_t>();
|
||||
}
|
||||
addGameTask(&Game::playerToggleOutfitExtension, player->getID(), mount, wings, aura, shader);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseUseItem(NetworkMessage& msg)
|
||||
{
|
||||
Position pos = msg.getPosition();
|
||||
@@ -2127,9 +2182,13 @@ void ProtocolGame::sendOutfitWindow()
|
||||
msg.addByte(0xC8);
|
||||
|
||||
Outfit_t currentOutfit = player->getDefaultOutfit();
|
||||
Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
|
||||
if (currentMount) {
|
||||
currentOutfit.lookMount = currentMount->clientId;
|
||||
}
|
||||
|
||||
AddOutfit(msg, currentOutfit);
|
||||
|
||||
const ClientVersion_t clientVersion = g_game.getClientVersion();
|
||||
std::vector<ProtocolOutfit> protocolOutfits;
|
||||
if (player->isAccessPlayer()) {
|
||||
static const std::string gamemasterOutfitName = "Gamemaster";
|
||||
@@ -2145,22 +2204,72 @@ void ProtocolGame::sendOutfitWindow()
|
||||
}
|
||||
|
||||
protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
|
||||
if (CLIENT_VERSION_780 <= clientVersion && clientVersion <= CLIENT_VERSION_792) {
|
||||
if (protocolOutfits.size() == 20) { // Game client doesn't allow more than 15 outfits in 780-792
|
||||
break;
|
||||
}
|
||||
if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msg.addByte(protocolOutfits.size());
|
||||
for (const ProtocolOutfit& outfit : protocolOutfits) {
|
||||
msg.add<uint16_t>(outfit.lookType);
|
||||
if (clientVersion > CLIENT_VERSION_781) {
|
||||
msg.addString(outfit.name);
|
||||
}
|
||||
msg.addString(outfit.name);
|
||||
msg.addByte(outfit.addons);
|
||||
}
|
||||
|
||||
std::vector<const Mount*> mounts;
|
||||
for (const Mount& mount : g_game.mounts.getMounts()) {
|
||||
if (player->hasMount(&mount)) {
|
||||
mounts.push_back(&mount);
|
||||
}
|
||||
}
|
||||
|
||||
msg.addByte(mounts.size());
|
||||
for (const Mount* mount : mounts) {
|
||||
msg.add<uint16_t>(mount->clientId);
|
||||
msg.addString(mount->name);
|
||||
}
|
||||
|
||||
if (otclientV8) {
|
||||
std::vector<const Wing*> wings;
|
||||
for (const Wing& wing : g_game.wings.getWings()) {
|
||||
if (player->hasWing(&wing)) {
|
||||
wings.push_back(&wing);
|
||||
}
|
||||
}
|
||||
|
||||
msg.addByte(wings.size());
|
||||
for (const Wing* wing : wings) {
|
||||
msg.add<uint16_t>(wing->clientId);
|
||||
msg.addString(wing->name);
|
||||
}
|
||||
|
||||
std::vector<const Aura*> auras;
|
||||
for (const Aura& aura : g_game.auras.getAuras()) {
|
||||
if (player->hasAura(&aura)) {
|
||||
auras.push_back(&aura);
|
||||
}
|
||||
}
|
||||
|
||||
msg.addByte(auras.size());
|
||||
for (const Aura* aura : auras) {
|
||||
msg.add<uint16_t>(aura->clientId);
|
||||
msg.addString(aura->name);
|
||||
}
|
||||
|
||||
std::vector<const Shader*> shaders;
|
||||
for (const Shader& shader : g_game.shaders.getShaders()) {
|
||||
if (player->hasShader(&shader)) {
|
||||
shaders.push_back(&shader);
|
||||
}
|
||||
}
|
||||
|
||||
msg.addByte(shaders.size());
|
||||
for (const Shader* shader : shaders) {
|
||||
msg.add<uint16_t>(shader->id);
|
||||
msg.addString(shader->name);
|
||||
}
|
||||
}
|
||||
|
||||
writeToOutputBuffer(msg);
|
||||
}
|
||||
|
||||
@@ -2284,9 +2393,18 @@ void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit)
|
||||
msg.addByte(outfit.lookLegs);
|
||||
msg.addByte(outfit.lookFeet);
|
||||
msg.addByte(outfit.lookAddons);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
msg.addItemId(outfit.lookTypeEx);
|
||||
}
|
||||
|
||||
msg.add<uint16_t>(outfit.lookMount);
|
||||
if (otclientV8) {
|
||||
msg.add<uint16_t>(outfit.lookWings);
|
||||
msg.add<uint16_t>(outfit.lookAura);
|
||||
Shader* shader = g_game.shaders.getShaderByID(outfit.lookShader);
|
||||
msg.addString(shader ? shader->name : "");
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo)
|
||||
@@ -2413,4 +2531,76 @@ void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg)
|
||||
|
||||
// process additional opcodes via lua script event
|
||||
addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
|
||||
}
|
||||
|
||||
// OTCv8
|
||||
void ProtocolGame::sendFeatures()
|
||||
{
|
||||
if (!otclientV8)
|
||||
return;
|
||||
|
||||
std::map<GameFeature, bool> features;
|
||||
// place for non-standard OTCv8 features
|
||||
//features[GameExtendedOpcode] = true;
|
||||
//features[GameChangeMapAwareRange] = true;
|
||||
//features[GameNewWalking] = true;
|
||||
//features[GameEnvironmentEffect] = false; // disable it, useless 2 byte with every tile
|
||||
//features[GameExtendedClientPing] = true;
|
||||
//features[GameItemTooltip] = true; // fully available from version 2.6
|
||||
//features[GameWingsAndAura] = true;
|
||||
//features[GameOutfitShaders] = true;
|
||||
|
||||
// packet compression
|
||||
// we don't send feature, because feature assumes all packets are compressed
|
||||
// if adler32 is enabled then compression can be detected automaticly, just adlre32 must be 0
|
||||
if (g_config.getBoolean(ConfigManager::PACKET_COMPRESSION)) {
|
||||
enableCompression();
|
||||
}
|
||||
|
||||
if (features.empty())
|
||||
return;
|
||||
|
||||
auto msg = getOutputBuffer(1024);
|
||||
msg->addByte(0x43);
|
||||
msg->add<uint16_t>(features.size());
|
||||
for (auto& feature : features) {
|
||||
msg->addByte((uint8_t)feature.first);
|
||||
msg->addByte(feature.second ? 1 : 0);
|
||||
}
|
||||
|
||||
send(std::move(getCurrentBuffer())); // send this packet immediately
|
||||
}
|
||||
|
||||
void ProtocolGame::parseNewPing(NetworkMessage& msg)
|
||||
{
|
||||
uint32_t pingId = msg.get<uint32_t>();
|
||||
uint16_t localPing = msg.get<uint16_t>();
|
||||
uint16_t fps = msg.get<uint16_t>();
|
||||
|
||||
addGameTask(&Game::playerReceiveNewPing, player->getID(), localPing, fps);
|
||||
g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::sendNewPing, getThis(), pingId)));
|
||||
}
|
||||
|
||||
void ProtocolGame::sendNewPing(uint32_t pingId)
|
||||
{
|
||||
if (!otclientV8)
|
||||
return;
|
||||
|
||||
NetworkMessage msg;
|
||||
msg.addByte(0x40);
|
||||
msg.add<uint32_t>(pingId);
|
||||
writeToOutputBuffer(msg);
|
||||
}
|
||||
|
||||
void ProtocolGame::sendProgressbar(uint32_t id, uint32_t duration, bool ltr)
|
||||
{
|
||||
if (!otclientV8 || otclientV8 < 260)
|
||||
return;
|
||||
|
||||
NetworkMessage msg;
|
||||
msg.addByte(0x3b);
|
||||
msg.add<uint32_t>(id);
|
||||
msg.add<uint32_t>(duration);
|
||||
msg.addByte(ltr);
|
||||
writeToOutputBuffer(msg);
|
||||
}
|
||||
@@ -52,7 +52,8 @@ class ProtocolGame final : public Protocol
|
||||
// static protocol information
|
||||
enum { server_sends_first = true };
|
||||
enum { protocol_identifier = 0 }; // Not required as we send first
|
||||
|
||||
enum { use_checksum = true };
|
||||
|
||||
static const char* protocol_name() {
|
||||
return "gameworld protocol";
|
||||
}
|
||||
@@ -90,6 +91,7 @@ class ProtocolGame final : public Protocol
|
||||
//Parse methods
|
||||
void parseAutoWalk(NetworkMessage& msg);
|
||||
void parseSetOutfit(NetworkMessage& msg);
|
||||
void parseToggleMount(NetworkMessage& msg);
|
||||
void parseSay(NetworkMessage& msg);
|
||||
void parseLookAt(NetworkMessage& msg);
|
||||
void parseLookInBattleList(NetworkMessage& msg);
|
||||
@@ -265,6 +267,12 @@ class ProtocolGame final : public Protocol
|
||||
//otclient
|
||||
void parseExtendedOpcode(NetworkMessage& msg);
|
||||
|
||||
//OTCv8
|
||||
void sendFeatures();
|
||||
void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true);
|
||||
void parseNewPing(NetworkMessage& msg);
|
||||
void sendNewPing(uint32_t pingId);
|
||||
|
||||
friend class Player;
|
||||
|
||||
// Helpers so we don't need to bind every time
|
||||
@@ -289,6 +297,7 @@ class ProtocolGame final : public Protocol
|
||||
|
||||
bool debugAssertSent = false;
|
||||
bool acceptPackets = false;
|
||||
uint16_t otclientV8 = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -193,6 +193,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
|
||||
return;
|
||||
}
|
||||
|
||||
// OTCv8 version detection
|
||||
uint16_t otclientV8 = 0;
|
||||
uint16_t otcV8StringLength = msg.get<uint16_t>();
|
||||
if (otcV8StringLength == 5 && msg.getString(5) == "OTCv8") {
|
||||
otclientV8 = msg.get<uint16_t>(); // 253, 260, 261, ...
|
||||
}
|
||||
|
||||
auto thisPtr = std::static_pointer_cast<ProtocolLogin>(shared_from_this());
|
||||
g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version)));
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class ProtocolLogin : public Protocol
|
||||
// static protocol information
|
||||
enum {server_sends_first = false};
|
||||
enum {protocol_identifier = 0x01};
|
||||
enum { use_checksum = true };
|
||||
static const char* protocol_name() {
|
||||
return "login protocol";
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class ProtocolStatus final : public Protocol
|
||||
// static protocol information
|
||||
enum {server_sends_first = false};
|
||||
enum {protocol_identifier = 0xFF};
|
||||
enum { use_checksum = false };
|
||||
static const char* protocol_name() {
|
||||
return "status protocol";
|
||||
}
|
||||
|
||||
52
src/shaders.cpp
Normal file
52
src/shaders.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "otpch.h"
|
||||
|
||||
#include "shaders.h"
|
||||
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Shaders::reload()
|
||||
{
|
||||
shaders.clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
bool Shaders::loadFromXml()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file("data/XML/shaders.xml");
|
||||
if (!result) {
|
||||
printXMLError("Error - Shaders::loadFromXml", "data/XML/shaders.xml", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto shaderNode : doc.child("shaders").children()) {
|
||||
shaders.emplace_back(
|
||||
static_cast<uint8_t>(pugi::cast<uint16_t>(shaderNode.attribute("id").value())),
|
||||
shaderNode.attribute("name").as_string(),
|
||||
shaderNode.attribute("premium").as_bool()
|
||||
);
|
||||
}
|
||||
shaders.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Shader* Shaders::getShaderByID(uint8_t id)
|
||||
{
|
||||
auto it = std::find_if(shaders.begin(), shaders.end(), [id](const Shader& shader) {
|
||||
return shader.id == id;
|
||||
});
|
||||
|
||||
return it != shaders.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Shader* Shaders::getShaderByName(const std::string& name) {
|
||||
auto shaderName = name.c_str();
|
||||
for (auto& it : shaders) {
|
||||
if (strcasecmp(shaderName, it.name.c_str()) == 0) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
30
src/shaders.h
Normal file
30
src/shaders.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef FS_SHADERS_H
|
||||
#define FS_SHADERS_H
|
||||
|
||||
struct Shader
|
||||
{
|
||||
Shader(uint8_t id, std::string name, bool premium) :
|
||||
name(std::move(name)), id(id), premium(premium) {}
|
||||
|
||||
uint8_t id;
|
||||
std::string name;
|
||||
bool premium;
|
||||
};
|
||||
|
||||
class Shaders
|
||||
{
|
||||
public:
|
||||
bool reload();
|
||||
bool loadFromXml();
|
||||
Shader* getShaderByID(uint8_t id);
|
||||
Shader* getShaderByName(const std::string& name);
|
||||
|
||||
const std::vector<Shader>& getShaders() const {
|
||||
return shaders;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Shader> shaders;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -890,6 +890,31 @@ std::string getSkillName(uint8_t skillid)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t adlerChecksum(const uint8_t* data, size_t length)
|
||||
{
|
||||
if (length > NETWORKMESSAGE_MAXSIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t adler = 65521;
|
||||
|
||||
uint32_t a = 1, b = 0;
|
||||
|
||||
while (length > 0) {
|
||||
size_t tmp = length > 5552 ? 5552 : length;
|
||||
length -= tmp;
|
||||
|
||||
do {
|
||||
a += *data++;
|
||||
b += a;
|
||||
} while (--tmp);
|
||||
|
||||
a %= adler;
|
||||
b %= adler;
|
||||
}
|
||||
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
std::string ucfirst(std::string str)
|
||||
{
|
||||
|
||||
@@ -85,6 +85,8 @@ std::string getCombatName(CombatType_t combatType);
|
||||
|
||||
std::string getSkillName(uint8_t skillid);
|
||||
|
||||
uint32_t adlerChecksum(const uint8_t* data, size_t length);
|
||||
|
||||
std::string ucfirst(std::string str);
|
||||
std::string ucwords(std::string str);
|
||||
bool booleanString(const std::string& str);
|
||||
|
||||
63
src/wings.cpp
Normal file
63
src/wings.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "otpch.h"
|
||||
|
||||
#include "wings.h"
|
||||
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Wings::reload()
|
||||
{
|
||||
wings.clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
bool Wings::loadFromXml()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file("data/XML/wings.xml");
|
||||
if (!result) {
|
||||
printXMLError("Error - Wings::loadFromXml", "data/XML/wings.xml", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto wingNode : doc.child("wings").children()) {
|
||||
wings.emplace_back(
|
||||
static_cast<uint8_t>(pugi::cast<uint16_t>(wingNode.attribute("id").value())),
|
||||
pugi::cast<uint16_t>(wingNode.attribute("clientid").value()),
|
||||
wingNode.attribute("name").as_string(),
|
||||
pugi::cast<int32_t>(wingNode.attribute("speed").value()),
|
||||
wingNode.attribute("premium").as_bool()
|
||||
);
|
||||
}
|
||||
wings.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Wing* Wings::getWingByID(uint8_t id)
|
||||
{
|
||||
auto it = std::find_if(wings.begin(), wings.end(), [id](const Wing& wing) {
|
||||
return wing.id == id;
|
||||
});
|
||||
|
||||
return it != wings.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Wing* Wings::getWingByName(const std::string& name) {
|
||||
auto wingName = name.c_str();
|
||||
for (auto& it : wings) {
|
||||
if (strcasecmp(wingName, it.name.c_str()) == 0) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Wing* Wings::getWingByClientID(uint16_t clientId)
|
||||
{
|
||||
auto it = std::find_if(wings.begin(), wings.end(), [clientId](const Wing& wing) {
|
||||
return wing.clientId == clientId;
|
||||
});
|
||||
|
||||
return it != wings.end() ? &*it : nullptr;
|
||||
}
|
||||
33
src/wings.h
Normal file
33
src/wings.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef FS_WINGS_H
|
||||
#define FS_WINGS_H
|
||||
|
||||
struct Wing
|
||||
{
|
||||
Wing(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||
|
||||
std::string name;
|
||||
int32_t speed;
|
||||
uint16_t clientId;
|
||||
uint8_t id;
|
||||
bool premium;
|
||||
};
|
||||
|
||||
class Wings
|
||||
{
|
||||
public:
|
||||
bool reload();
|
||||
bool loadFromXml();
|
||||
Wing* getWingByID(uint8_t id);
|
||||
Wing* getWingByName(const std::string& name);
|
||||
Wing* getWingByClientID(uint16_t clientId);
|
||||
|
||||
const std::vector<Wing>& getWings() const {
|
||||
return wings;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Wing> wings;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user