Multi-protocol

Lots of chagnes to add multi protocol flexibility, not really
completed yet, still have to rework text messages opcodes and other stuff,
so this still a working in progress feature

* Rework dat reader, the dat reader can now
* dinamically detect dat version
* Split game into gamelib and game_interface
* Lots of other minor changes
This commit is contained in:
Eduardo Bart
2012-07-17 20:49:21 -03:00
parent 6fc11d2fa9
commit eb24d6776e
64 changed files with 536 additions and 588 deletions

View File

@@ -141,3 +141,14 @@ void Application::close()
if(!g_lua.callGlobalField<bool>("g_app", "onClose"))
exit();
}
std::string Application::getOs()
{
#if defined(WIN32)
return "windows";
#elif defined(__APPLE__)
return "mac";
#else
return "linux";
#endif
}

View File

@@ -58,6 +58,7 @@ public:
std::string getBuildCommit() { return BUILD_COMMIT; }
std::string getBuildType() { return BUILD_TYPE; }
std::string getBuildArch() { return BUILD_ARCH; }
std::string getOs();
std::string getStartupOptions() { return m_startupOptions; }
protected:

View File

@@ -22,22 +22,25 @@
#include <framework/core/application.h>
#include <framework/luaengine/luainterface.h>
#include <framework/graphics/fontmanager.h>
#include <framework/ui/ui.h>
#include <framework/net/protocol.h>
#include <framework/core/eventdispatcher.h>
#include <framework/core/configmanager.h>
#include <framework/otml/otml.h>
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/core/modulemanager.h>
#include <framework/core/module.h>
#include <framework/sound/soundmanager.h>
#include <framework/util/crypt.h>
#include <framework/core/resourcemanager.h>
#include <framework/graphics/particlemanager.h>
#include <framework/graphics/texturemanager.h>
#ifdef FW_GRAPHICS
#include <framework/graphics/graphics.h>
#include <framework/platform/platformwindow.h>
#include <framework/graphics/particlemanager.h>
#include <framework/graphics/fontmanager.h>
#include <framework/ui/ui.h>
#endif
void Application::registerLuaFunctions()
{
// conversion globals
@@ -67,6 +70,7 @@ void Application::registerLuaFunctions()
g_lua.bindSingletonFunction("g_app", "getBuildCommit", &Application::getBuildCommit, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getBuildType", &Application::getBuildType, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getBuildArch", &Application::getBuildArch, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "getOs", &Application::getOs, static_cast<Application*>(&g_app));
g_lua.bindSingletonFunction("g_app", "exit", &Application::exit, static_cast<Application*>(&g_app));
// Crypt
@@ -644,7 +648,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<InputMessage>("peekU16", &InputMessage::peekU16);
g_lua.bindClassMemberFunction<InputMessage>("peekU32", &InputMessage::peekU32);
g_lua.bindClassMemberFunction<InputMessage>("peekU64", &InputMessage::peekU64);
g_lua.bindClassMemberFunction<InputMessage>("decryptRSA", &InputMessage::decryptRSA);
g_lua.bindClassMemberFunction<InputMessage>("decryptRsa", &InputMessage::decryptRsa);
g_lua.bindClassMemberFunction<InputMessage>("getReadSize", &InputMessage::getReadSize);
g_lua.bindClassMemberFunction<InputMessage>("getUnreadSize", &InputMessage::getUnreadSize);
g_lua.bindClassMemberFunction<InputMessage>("getMessageSize", &InputMessage::getMessageSize);
@@ -661,7 +665,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<OutputMessage>("addU64", &OutputMessage::addU64);
g_lua.bindClassMemberFunction<OutputMessage>("addString", &OutputMessage::addString);
g_lua.bindClassMemberFunction<OutputMessage>("addPaddingBytes", &OutputMessage::addPaddingBytes);
g_lua.bindClassMemberFunction<OutputMessage>("encryptRSA", &OutputMessage::encryptRSA);
g_lua.bindClassMemberFunction<OutputMessage>("encryptRsa", &OutputMessage::encryptRsa);
g_lua.bindClassMemberFunction<OutputMessage>("getMessageSize", &OutputMessage::getMessageSize);
#endif

View File

@@ -86,7 +86,7 @@ std::string InputMessage::getString()
return std::string(v, stringLength);
}
void InputMessage::decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d)
void InputMessage::decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d)
{
checkRead(size);
RSA::decrypt((char*)m_buffer + m_readPos, size, p.c_str(), q.c_str(), d.c_str());

View File

@@ -40,6 +40,7 @@ public:
void setBuffer(const std::string& buffer);
void skipBytes(uint16 bytes) { m_readPos += bytes; }
void setReadPos(uint16 readPos) { m_readPos = readPos; }
uint8 getU8();
uint16 getU16();
uint32 getU32();
@@ -51,9 +52,10 @@ public:
uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; }
uint64 peekU64() { uint64 v = getU64(); m_readPos-=8; return v; }
void decryptRSA(int size, const std::string& p, const std::string& q, const std::string& d);
void decryptRsa(int size, const std::string& p, const std::string& q, const std::string& d);
int getReadSize() { return m_readPos - m_headerPos; }
int getReadPos() { return m_readPos; }
int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); }
uint16 getMessageSize() { return m_messageSize; }

View File

@@ -89,7 +89,7 @@ void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
m_messageSize += bytes;
}
void OutputMessage::encryptRSA(int size, const std::string& key)
void OutputMessage::encryptRsa(int size, const std::string& key)
{
if(m_messageSize < size)
throw stdext::exception("insufficient bytes in buffer to encrypt");

View File

@@ -49,7 +49,7 @@ public:
void addString(const std::string& buffer);
void addPaddingBytes(int bytes, uint8 byte = 0);
void encryptRSA(int size, const std::string& key);
void encryptRsa(int size, const std::string& key);
uint16 getMessageSize() { return m_messageSize; }

View File

@@ -286,31 +286,27 @@ namespace Otc
};
enum GameFeature {
GameExtendedOpcode = 0,
GameProtocolChecksum,
GameAccountNames,
GameChallangeOnLogin,
GameStackposOnTileAddThing,
GamePenalityOnDeath,
GameNameOnNpcTrade,
GameDoubleFreeCapacity,
GameDoubleExperience,
GameTotalCapacity,
GameSkillsBase,
GameAdditionalPlayerStats,
GameIdOnCancelAttack,
GameChannelPlayerList,
GamePlayerMounts,
GameEnvironmentEffect,
GameCreatureType,
GameCreatureAdditionalInfo,
GameCreaturePassableInfo,
GameItemAnimationPhase,
GameTrucatedPingOpcode,
GameReverseCreatureStack,
GameMagicEffectU16,
GamePlayerMarket,
LastGameFeature
// 1-50 defined in c++
GameProtocolChecksum = 1,
GameAccountNames = 2,
GameChallangeOnLogin = 3,
GamePenalityOnDeath = 5,
GameNameOnNpcTrade = 6,
GameDoubleFreeCapacity = 7,
GameDoubleExperience = 8,
GameTotalCapacity = 9,
GameSkillsBase = 10,
GamePlayerRegenerationTime = 11,
GameChannelPlayerList = 13,
GamePlayerMounts = 14,
GameEnvironmentEffect = 15,
GameCreatureEmblems = 17,
GameItemAnimationPhase = 19,
GameMagicEffectU16 = 22,
GamePlayerMarket = 23,
// 23-50 unused yet
// 51-100 reserved to be defined in lua
LastGameFeature = 101
};
enum PathFindResult {

View File

@@ -461,6 +461,8 @@ void Creature::setDirection(Otc::Direction direction)
void Creature::setOutfit(const Outfit& outfit)
{
if(!g_things.isValidDatId(outfit.getId(), DatCreatureCategory))
return;
m_walkAnimationPhase = 0; // might happen when player is walking and outfit is changed.
m_outfit = outfit;
}

View File

@@ -46,6 +46,8 @@ void Effect::startAnimation()
void Effect::setId(uint32 id)
{
if(!g_things.isValidDatId(id, DatEffectCategory))
id = 0;
m_id = id;
}

View File

@@ -38,7 +38,7 @@ Game g_game;
Game::Game()
{
resetGameStates();
setClientVersion(860);
m_protocolVersion = 0;
}
void Game::resetGameStates()
@@ -416,10 +416,11 @@ void Game::processWalkCancel(Otc::Direction direction)
void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName)
{
if(m_protocolGame || isOnline()) {
g_logger.traceError("unable to login into a world while already online or logging");
return;
}
if(m_protocolGame || isOnline())
stdext::throw_exception("Unable to login into a world while already online or logging.");
if(m_protocolVersion == 0)
stdext::throw_exception("Must set a valid game protocol version before logging.");
m_protocolGame = ProtocolGamePtr(new ProtocolGame);
m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName);
@@ -1090,61 +1091,48 @@ bool Game::canPerformGameAction()
return m_localPlayer && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
}
void Game::setClientVersion(int clientVersion)
void Game::setProtocolVersion(int version)
{
if(isOnline()) {
g_logger.error("Unable to change client version while online");
return;
}
if(isOnline())
stdext::throw_exception("Unable to change client version while online");
//TODO: check supported versions
if(version < 810 || version > 960)
stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset();
if(clientVersion >= 854) {
if(version >= 854) {
enableFeature(Otc::GameProtocolChecksum);
enableFeature(Otc::GameAccountNames);
enableFeature(Otc::GameChallangeOnLogin);
enableFeature(Otc::GameStackposOnTileAddThing);
enableFeature(Otc::GameDoubleFreeCapacity);
enableFeature(Otc::GameCreatureAdditionalInfo);
enableFeature(Otc::GameReverseCreatureStack);
enableFeature(Otc::GameCreatureEmblems);
}
if(clientVersion >= 860) {
enableFeature(Otc::GameIdOnCancelAttack);
}
if(clientVersion >= 862) {
if(version >= 862) {
enableFeature(Otc::GamePenalityOnDeath);
}
if(clientVersion >= 870) {
if(version >= 870) {
enableFeature(Otc::GameDoubleExperience);
enableFeature(Otc::GamePlayerMounts);
}
if(clientVersion >= 910) {
if(version >= 910) {
enableFeature(Otc::GameNameOnNpcTrade);
enableFeature(Otc::GameTotalCapacity);
enableFeature(Otc::GameSkillsBase);
enableFeature(Otc::GameAdditionalPlayerStats);
enableFeature(Otc::GamePlayerRegenerationTime);
enableFeature(Otc::GameChannelPlayerList);
enableFeature(Otc::GameEnvironmentEffect);
enableFeature(Otc::GameCreatureType);
enableFeature(Otc::GameItemAnimationPhase);
}
if(clientVersion >= 940) {
if(version >= 940) {
enableFeature(Otc::GamePlayerMarket);
}
if(clientVersion >= 953) {
enableFeature(Otc::GameCreaturePassableInfo);
enableFeature(Otc::GameTrucatedPingOpcode);
}
m_clientVersion = clientVersion;
m_protocolVersion = version;
}
void Game::setAttackingCreature(const CreaturePtr& creature)

View File

@@ -232,8 +232,8 @@ public:
void setFeature(Otc::GameFeature feature) { m_features.set(feature, false); }
bool getFeature(Otc::GameFeature feature) { return m_features.test(feature); }
void setClientVersion(int clientVersion);
int getClientVersion() { return m_clientVersion; }
void setProtocolVersion(int version);
int getProtocolVersion() { return m_protocolVersion; }
void setRSA(const std::string& rsa);
std::string getRSA() { return m_rsa; }
@@ -256,7 +256,6 @@ public:
int getServerBeat() { return m_serverBeat; }
LocalPlayerPtr getLocalPlayer() { return m_localPlayer; }
ProtocolGamePtr getProtocolGame() { return m_protocolGame; }
int getProtocolVersion() { return PROTOCOL; }
std::string getCharacterName() { return m_characterName; }
std::string getWorldName() { return m_worldName; }
std::vector<uint8> getGMActions() { return m_gmActions; }
@@ -288,7 +287,7 @@ private:
std::string m_characterName;
std::string m_worldName;
std::bitset<Otc::LastGameFeature> m_features;
int m_clientVersion;
int m_protocolVersion;
std::string m_rsa;
};

View File

@@ -101,7 +101,7 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
else if(tile->mustHookEast())
xPattern = getNumPatternX() >= 3 ? 2 : 0;
}
} else if(isFluid() || isFluidContainer()) {
} else if(isSplash() || isFluidContainer()) {
int color = Otc::FluidTransparent;
switch(m_countOrSubType) {
case Otc::FluidNone:
@@ -176,15 +176,20 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate)
void Item::setId(uint32 id)
{
m_otbId = g_things.findOtbForClientId(id)->getServerId();
if(!g_things.isValidDatId(id, DatItemCategory))
id = 0;
//m_otbId = g_things.findOtbForClientId(id)->getServerId();
m_id = id;
m_otbId = 0;
}
void Item::setOtbId(uint16 id)
{
if(!g_things.isValidOtbId(id))
id = 0;
auto otbType = g_things.getOtbType(id);
m_otbId = id;
m_id = otbType->getClientId();
m_otbId = id;
}
bool Item::isValid()

View File

@@ -190,10 +190,10 @@ void OTClient::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "getLocalPlayer", &Game::getLocalPlayer, &g_game);
g_lua.bindSingletonFunction("g_game", "getProtocolGame", &Game::getProtocolGame, &g_game);
g_lua.bindSingletonFunction("g_game", "getProtocolVersion", &Game::getProtocolVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "setProtocolVersion", &Game::setProtocolVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "getCharacterName", &Game::getCharacterName, &g_game);
g_lua.bindSingletonFunction("g_game", "getWorldName", &Game::getWorldName, &g_game);
g_lua.bindSingletonFunction("g_game", "getGMActions", &Game::getGMActions, &g_game);
g_lua.bindSingletonFunction("g_game", "getClientVersion", &Game::getClientVersion, &g_game);
g_lua.bindSingletonFunction("g_game", "getFeature", &Game::getFeature, &g_game);
g_lua.registerSingletonClass("g_shaders");

View File

@@ -467,7 +467,7 @@ bool Map::loadOtcm(const std::string& fileName)
uint8 countOrSubType = fin->getU8();
ItemPtr item = Item::create(id);
if(item->isStackable() || item->isFluidContainer() || item->isFluid())
if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
item->setCountOrSubType(countOrSubType);
if(item->isValid())

View File

@@ -83,6 +83,8 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition)
void Missile::setId(uint32 id)
{
if(!g_things.isValidDatId(id, DatMissileCategory))
id = 0;
m_id = id;
}

View File

@@ -25,7 +25,7 @@
Outfit::Outfit()
{
m_category = DatCreatureCategory;
m_id = 1;
m_id = 0;
resetClothes();
}

View File

@@ -25,49 +25,7 @@
#include "global.h"
#if !(PROTOCOL == 810) && \
!(PROTOCOL == 854) && \
!(PROTOCOL >= 860 && PROTOCOL <= 862) && \
!(PROTOCOL >= 870 && PROTOCOL <= 871) && \
!(PROTOCOL >= 910 && PROTOCOL <= 953)
#error "the supplied protocol version is not supported"
#endif
namespace Proto {
#ifdef CIPSOFT_RSA
constexpr const char* RSA = "1321277432058722840622950990822933849527763264961655079678763618"
"4334395343554449668205332383339435179772895415509701210392836078"
"6959821132214473291575712138800495033169914814069637740318278150"
"2907336840325241747827401343576296990629870233111328210165697754"
"88792221429527047321331896351555606801473202394175817";
#else
constexpr const char* RSA = "1091201329673994292788609605089955415282375029027981291234687579"
"3726629149257644633073969600111060390723088861007265581882535850"
"3429057592827629436413108566029093628212635953836686562675849720"
"6207862794310902180176810615217550567108238764764442605581471797"
"07119674283982419152118103759076030616683978566631413";
#endif
constexpr int PicSignature = 0x4F8C231A; // 953 pic signature
constexpr int ClientVersion = PROTOCOL;
enum OsTypes {
OsLinux = 1,
OsWindows = 2,
OsFlash = 3,
OsOtclientLinux = 10,
OsOtclientWindows = 11,
OsOtclientMac = 12
};
#ifdef OSTYPE
constexpr int ClientOs = OSTYPE;
#elif defined WIN32
constexpr int ClientOs = OsOtclientWindows;
#else
constexpr int ClientOs = OsOtclientLinux;
#endif
enum LoginServerOpts {
LoginServerError = 10,
LoginServerMotd = 20,

View File

@@ -43,8 +43,11 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
opcode = msg->getU8();
// try to parse in lua first
int readPos = msg->getReadPos();
if(callLuaField<bool>("onOpcode", opcode, msg))
continue;
else
msg->setReadPos(readPos); // restore read pos
if(!m_gameInitialized && opcode > Proto::GameServerFirstGameOpcode)
g_logger.warning("received a game opcode from the server, but the game is not initialized yet, this is a server side bug");
@@ -66,16 +69,12 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
parseLoginWait(msg);
break;
case Proto::GameServerPing:
if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
parsePingBack(msg);
else
parsePing(msg);
break;
case Proto::GameServerPingBack:
if(g_game.getFeature(Otc::GameTrucatedPingOpcode))
parsePing(msg);
else
if((opcode == Proto::GameServerPing && g_game.getProtocolVersion() >= 953) ||
(opcode == Proto::GameServerPingBack && g_game.getProtocolVersion() < 953))
parsePingBack(msg);
else
parsePing(msg);
break;
case Proto::GameServerChallange:
parseChallange(msg);
@@ -334,9 +333,9 @@ void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
int numViolationReasons;
if(g_game.getClientVersion() >= 860)
if(g_game.getProtocolVersion() >= 860)
numViolationReasons = 20;
else if(g_game.getClientVersion() >= 854)
else if(g_game.getProtocolVersion() >= 854)
numViolationReasons = 19;
else
numViolationReasons = 32;
@@ -454,7 +453,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
Position pos = getPosition(msg);
int stackPos = -1;
if(g_game.getFeature(Otc::GameStackposOnTileAddThing))
if(g_game.getProtocolVersion() >= 854)
stackPos = msg->getU8();
ThingPtr thing = getThing(msg);
@@ -508,7 +507,7 @@ void ProtocolGame::parseCreatureMove(const InputMessagePtr& msg)
int stackPos = -2;
// older protocols stores creatures in reverse order
if(!g_game.getFeature(Otc::GameReverseCreatureStack))
if(!g_game.getProtocolVersion() >= 854)
stackPos = -1;
g_map.addThing(thing, newPos, stackPos);
@@ -884,12 +883,11 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
m_localPlayer->setStamina(stamina);
m_localPlayer->setSoul(soul);
if(g_game.getFeature(Otc::GameAdditionalPlayerStats)) {
int speed = msg->getU16();
msg->getU16(); // regeneration time
if(g_game.getProtocolVersion() >= 910)
m_localPlayer->setSpeed(msg->getU16());
m_localPlayer->setSpeed(speed);
}
if(g_game.getFeature(Otc::GamePlayerRegenerationTime))
msg->getU16(); // regeneration time
}
void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
@@ -913,7 +911,7 @@ void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
{
if(g_game.getFeature(Otc::GameIdOnCancelAttack))
if(g_game.getProtocolVersion() >= 860)
msg->getU32(); // unknown
g_game.processAttackCancel();
@@ -1389,7 +1387,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
uint id = msg->getU32();
int creatureType;
if(g_game.getFeature(Otc::GameCreatureType))
if(g_game.getProtocolVersion() >= 910)
creatureType = msg->getU8();
else {
if(id >= Proto::PlayerStartId && id < Proto::PlayerEndId)
@@ -1441,12 +1439,11 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
int emblem = -1;
bool passable = false;
if(g_game.getFeature(Otc::GameCreatureAdditionalInfo)) {
if(!known)
emblem = msg->getU8();
if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
emblem = msg->getU8();
if(g_game.getProtocolVersion() >= 854)
passable = (msg->getU8() == 0);
}
if(creature) {
creature->setHealthPercent(healthPercent);
@@ -1474,7 +1471,7 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
if(creature)
creature->turn(direction);
if(g_game.getFeature(Otc::GameCreaturePassableInfo)) {
if(g_game.getProtocolVersion() >= 953) {
bool passable = msg->getU8();
if(creature)
@@ -1497,7 +1494,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
if(item->getId() == 0)
stdext::throw_exception("unable to create item with invalid id 0");
if(item->isStackable() || item->isFluidContainer() || item->isFluid())
if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
item->setCountOrSubType(msg->getU8());
if(g_game.getFeature(Otc::GameItemAnimationPhase)) {

View File

@@ -49,8 +49,9 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientEnterGame);
msg->addU16(Proto::ClientOs);
msg->addU16(g_game.getClientVersion());
msg->addU16(g_lua.callGlobalField<int>("g_game", "getOs"));
msg->addU16(g_game.getProtocolVersion());
int paddingBytes = 128;
msg->addU8(0); // first RSA byte must be 0
@@ -90,7 +91,7 @@ void ProtocolGame::sendLoginPacket(uint challangeTimestamp, uint8 challangeRando
msg->addPaddingBytes(paddingBytes);
// encrypt with RSA
msg->encryptRSA(128, Proto::RSA);
msg->encryptRsa(128, g_lua.callGlobalField<std::string>("g_game", "getRsa"));
send(msg);
@@ -570,9 +571,10 @@ void ProtocolGame::sendShareExperience(bool active, int unknown)
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientShareExperience);
msg->addU8(active ? 0x01 : 0x00);
#if PROTOCOL<910
msg->addU8(unknown);
#endif
if(g_game.getProtocolVersion() < 910)
msg->addU8(unknown);
send(msg);
}

View File

@@ -28,12 +28,6 @@
#include "thingtypemanager.h"
#include <framework/luaengine/luaobject.h>
struct Light
{
uint8 intensity;
uint8 color;
};
// @bindclass
#pragma pack(push,1) // disable memory alignment
class Thing : public LuaObject
@@ -95,8 +89,7 @@ public:
int getAnimationPhases() { return rawGetDatType()->getAnimationPhases(); }
int getGroundSpeed() { return rawGetDatType()->getGroundSpeed(); }
int getMaxTextLength() { return rawGetDatType()->getMaxTextLength(); }
int getLightLevel() { return rawGetDatType()->getLightLevel(); }
int getLightColor() { return rawGetDatType()->getLightColor(); }
Light getLight() { return rawGetDatType()->getLight(); }
int getMinimapColor() { return rawGetDatType()->getMinimapColor(); }
int getLensHelp() { return rawGetDatType()->getLensHelp(); }
int getClothSlot() { return rawGetDatType()->getClothSlot(); }
@@ -110,9 +103,10 @@ public:
bool isForceUse() { return rawGetDatType()->isForceUse(); }
bool isMultiUse() { return rawGetDatType()->isMultiUse(); }
bool isWritable() { return rawGetDatType()->isWritable(); }
bool isChargeable() { return rawGetDatType()->isChargeable(); }
bool isWritableOnce() { return rawGetDatType()->isWritableOnce(); }
bool isFluidContainer() { return rawGetDatType()->isFluidContainer(); }
bool isFluid() { return rawGetDatType()->isFluid(); }
bool isSplash() { return rawGetDatType()->isSplash(); }
bool isNotWalkable() { return rawGetDatType()->isNotWalkable(); }
bool isNotMoveable() { return rawGetDatType()->isNotMoveable(); }
bool blockProjectile() { return rawGetDatType()->blockProjectile(); }
@@ -134,6 +128,7 @@ public:
bool isFullGround() { return rawGetDatType()->isFullGround(); }
bool isIgnoreLook() { return rawGetDatType()->isIgnoreLook(); }
bool isCloth() { return rawGetDatType()->isCloth(); }
MarketData getMarketData() { return rawGetDatType()->getMarketData(); }
protected:
Position m_position;

View File

@@ -22,6 +22,7 @@
#include "thingtypedat.h"
#include "spritemanager.h"
#include "game.h"
#include <framework/graphics/graphics.h>
#include <framework/graphics/texture.h>
@@ -33,20 +34,11 @@ ThingTypeDat::ThingTypeDat()
{
m_category = DatInvalidCategory;
m_id = 0;
m_null = true;
m_exactSize = 0;
m_layers = 0;
m_numPatternX = 0;
m_numPatternY = 0;
m_numPatternZ = 0;
m_numPatternX = m_numPatternY = m_numPatternZ = 0;
m_animationPhases = 0;
m_groundSpeed = 0;
m_maxTextLenght = 0;
m_lightLevel = 0;
m_lightColor = 0;
m_miniMapColor = 0;
m_lensHelp = 0;
m_clothSlot = 0;
m_elevation = 0;
m_layers = 0;
}
void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const FileStreamPtr& fin)
@@ -55,136 +47,68 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File
m_id = clientId;
m_category = category;
static int datVersion;
if(clientId == 100 && category == DatItemCategory)
datVersion = 2;
bool done = false;
for(int i = 0 ; i < DatLastAttrib;++i) {
int property = fin->getU8();
if(property == DatLastAttrib) {
int attrib = fin->getU8();
if(attrib == DatLastAttrib) {
done = true;
break;
}
switch(property) {
case DatAttribIsGround:
m_isGround = true;
m_groundSpeed = fin->getU16();
if(m_groundSpeed == 0)
m_groundSpeed = 100;
// hacky way to detect if is older dat version or not
if(clientId == 100 && category == DatItemCategory && datVersion != 1 &&
(attrib == DatAttribNotPathable || attrib == DatAttribDontHide || attrib == DatAttribIgnoreLook)) {
datVersion = 1;
}
if(datVersion <= 1) {
if(attrib == DatAttribWritable) {
m_attribs.set(DatAttribChargeable, true);
continue;
} else if(attrib > DatAttribWritable)
attrib -= 1;
}
switch(attrib) {
case DatAttribDisplacement: {
m_displacement.x = fin->getU16();
m_displacement.y = fin->getU16();
m_attribs.set(attrib, true);
break;
case DatAttribIsGroundBorder:
m_isGroundBorder = true;
}
case DatAttribLight: {
Light light;
light.intensity = fin->getU16();
light.color = fin->getU16();
m_attribs.set(attrib, light);
break;
case DatAttribIsOnBottom:
m_isOnBottom = true;
break;
case DatAttribIsOnTop:
m_isOnTop = true;
break;
case DatAttribIsContainer:
m_isContainer = true;
break;
case DatAttribIsStackable:
m_isStackable = true;
break;
case DatAttribIsForceUse:
m_isForceUse = true;
break;
case DatAttribIsMultiUse:
m_isMultiUse = true;
break;
case DatAttribIsWritable:
m_isWritable = true;
m_maxTextLenght = fin->getU16();
break;
case DatAttribIsWritableOnce:
m_isWritableOnce = true;
m_maxTextLenght = fin->getU16();
break;
case DatAttribIsFluidContainer:
m_isFluidContainer = true;
break;
case DatAttribIsFluid:
m_isFluid = true;
break;
case DatAttribIsNotWalkable:
m_isNotWalkable = true;
break;
case DatAttribIsNotMoveable:
m_isNotMoveable = true;
break;
case DatAttribBlockProjectile:
m_blockProjectile = true;
break;
case DatAttribIsNotPathable:
m_isNotPathable = true;
break;
case DatAttribIsPickupable:
m_isPickupable = true;
break;
case DatAttribIsHangable:
m_isHangable = true;
break;
case DatAttribHookSouth:
m_isHookSouth = true;
break;
case DatAttribHookEast:
m_isHookEast = true;
break;
case DatAttribIsRotateable:
m_isRotateable = true;
break;
case DatAttribHasLight:
m_hasLight = true;
m_lightLevel = fin->getU16();
m_lightColor = fin->getU16();
break;
case DatAttribDontHide:
m_isDontHide = true;
break;
case DatAttribIsTranslucent:
m_isTranslucent = true;
break;
case DatAttribHasDisplacement:
m_hasDisplacement = true;
m_displacement = Point(fin->getU16(), fin->getU16());
break;
case DatAttribHasElevation:
m_hasElevation = true;
m_elevation = fin->getU16();
break;
case DatAttribIsLyingCorpse:
m_isLyingCorpse = true;
break;
case DatAttribAnimateAlways:
m_isAnimateAlways = true;
}
case DatAttribMarket: {
MarketData market;
market.category = fin->getU16();
market.showAs = fin->getU16();
market.tradeAs = fin->getU16();
market.name = fin->getString();
market.restrictProfession = fin->getU16();
market.requiredLevel = fin->getU16();
m_attribs.set(attrib, market);
break;
}
case DatAttribGround:
case DatAttribWritable:
case DatAttribWritableOnce:
case DatAttribElevation:
case DatAttribMiniMapColor:
m_miniMapColor = true;
m_miniMapColor = fin->getU16();
break;
case DatAttribLensHelp:
m_lensHelp = true;
m_lensHelp = fin->getU16();
break;
case DatAttribIsFullGround:
m_isFullGround = true;
break;
case DatAttribIgnoreLook:
m_isIgnoreLook = true;
break;
case DatAttribCloth:
m_isCloth = true;
m_clothSlot = fin->getU16();
break;
case DatAttribMarket:
fin->getU16(); // category
fin->getU16(); // trade as
fin->getU16(); // show as
fin->getString(); // name
fin->getU16(); // restrict profession
fin->getU16(); // level
case DatAttribLensHelp:
m_attribs.set(attrib, fin->getU16());
break;
default:
stdext::throw_exception("corrupt data, invalid type attribute");
m_attribs.set(attrib, true);
break;
};
}
@@ -192,40 +116,18 @@ void ThingTypeDat::unserialize(uint16 clientId, DatCategory category, const File
if(!done)
stdext::throw_exception("corrupt data");
int totalSprites = 1;
for(int i = 0; i < DatLastDimension; ++i) {
switch(i) {
case DatWidth:
m_size.setWidth(fin->getU8());
break;
case DatHeight:
m_size.setHeight(fin->getU8());
break;
case DatExactSize:
if(m_size.width() <= 1 && m_size.height() <= 1)
m_exactSize = 32;
else
m_exactSize = std::min((int)fin->getU8(), std::max(m_size.width() * 32, m_size.height() * 32));
break;
case DatLayers:
m_layers = fin->getU8();
break;
case DatPatternX:
m_numPatternX = fin->getU8();
break;
case DatPatternY:
m_numPatternY = fin->getU8();
break;
case DatPatternZ:
m_numPatternZ = fin->getU8();
break;
case DatAnimationPhases:
m_animationPhases = fin->getU8();
break;
}
}
uint8 width = fin->getU8();
uint8 height = fin->getU8();
m_size = Size(width, height);
m_exactSize = (width > 1 || height > 1) ? std::min((int)fin->getU8(), std::max(width * 32, height * 32)) : 32;
m_layers = fin->getU8();
m_numPatternX = fin->getU8();
m_numPatternY = fin->getU8();
m_numPatternZ = fin->getU8();
m_animationPhases = fin->getU8();
int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
totalSprites = m_size.width() * m_size.height() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
if(totalSprites == 0)
stdext::throw_exception("a thing type has no sprites");
if(totalSprites > 4096)

View File

@@ -29,6 +29,7 @@
#include <framework/graphics/coordsbuffer.h>
#include <framework/luaengine/luaobject.h>
#include <framework/net/server.h>
#include <framework/util/attribstorage.h>
enum DatCategory {
DatItemCategory = 0,
@@ -48,53 +49,59 @@ enum DatSpriteMask {
};
enum DatAttrib {
DatAttribIsGround = 0,
DatAttribIsGroundBorder,
DatAttribIsOnBottom,
DatAttribIsOnTop,
DatAttribIsContainer,
DatAttribIsStackable,
DatAttribIsForceUse,
DatAttribIsMultiUse,
DatAttribIsWritable,
DatAttribIsWritableOnce,
DatAttribIsFluidContainer,
DatAttribIsFluid,
DatAttribIsNotWalkable,
DatAttribIsNotMoveable,
DatAttribGround = 0,
DatAttribGroundBorder,
DatAttribOnBottom,
DatAttribOnTop,
DatAttribContainer,
DatAttribStackable,
DatAttribForceUse,
DatAttribMultiUse,
//DatAttribRune
DatAttribWritable,
DatAttribWritableOnce,
DatAttribFluidContainer,
DatAttribSplash,
DatAttribNotWalkable,
DatAttribNotMoveable,
DatAttribBlockProjectile,
DatAttribIsNotPathable,
DatAttribIsPickupable,
DatAttribIsHangable,
DatAttribNotPathable,
DatAttribPickupable,
DatAttribHangable,
DatAttribHookSouth,
DatAttribHookEast,
DatAttribIsRotateable,
DatAttribHasLight,
DatAttribRotateable,
DatAttribLight,
DatAttribDontHide,
DatAttribIsTranslucent,
DatAttribHasDisplacement,
DatAttribHasElevation,
DatAttribIsLyingCorpse,
DatAttribTranslucent,
DatAttribDisplacement,
DatAttribElevation,
DatAttribLyingCorpse,
DatAttribAnimateAlways,
DatAttribMiniMapColor,
DatAttribLensHelp,
DatAttribIsFullGround,
DatAttribFullGround,
DatAttribIgnoreLook,
DatAttribCloth,
DatAttribMarket,
DatLastAttrib = 255
DatLastAttrib = 255,
// legacy attribs
DatAttribChargeable = 254
};
enum DatDimension {
DatWidth = 0,
DatHeight,
DatExactSize,
DatLayers,
DatPatternX,
DatPatternY,
DatPatternZ,
DatAnimationPhases,
DatLastDimension
struct MarketData {
std::string name;
int category;
uint16 requiredLevel;
uint16 restrictProfession;
uint16 showAs;
uint16 tradeAs;
};
struct Light {
uint8 intensity;
uint8 color;
};
class ThingTypeDat : public LuaObject
@@ -113,56 +120,58 @@ public:
Size getSize() { return m_size; }
int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); }
Point getDisplacement() { return m_displacement; }
int getDisplacementX() { return m_displacement.x; }
int getDisplacementY() { return m_displacement.y; }
int getExactSize() { return m_exactSize; }
int getLayers() { return m_layers; }
int getNumPatternX() { return m_numPatternX; }
int getNumPatternY() { return m_numPatternY; }
int getNumPatternZ() { return m_numPatternZ; }
int getAnimationPhases() { return m_animationPhases; }
int getGroundSpeed() { return m_groundSpeed; }
int getMaxTextLength() { return m_maxTextLenght; }
int getLightLevel() { return m_lightLevel; }
int getLightColor() { return m_lightColor; }
int getMinimapColor() { return m_miniMapColor; }
int getLensHelp() { return m_lensHelp; }
int getClothSlot() { return m_clothSlot; }
int getElevation() { return m_elevation; }
bool isGround() { return m_isGround; }
bool isGroundBorder() { return m_isGroundBorder; }
bool isOnBottom() { return m_isOnBottom; }
bool isOnTop() { return m_isOnTop; }
bool isContainer() { return m_isContainer; }
bool isStackable() { return m_isStackable; }
bool isForceUse() { return m_isForceUse; }
bool isMultiUse() { return m_isMultiUse; }
bool isWritable() { return m_isWritable; }
bool isWritableOnce() { return m_isWritableOnce; }
bool isFluidContainer() { return m_isFluidContainer; }
bool isFluid() { return m_isFluid; }
bool isNotWalkable() { return m_isNotWalkable; }
bool isNotMoveable() { return m_isNotMoveable; }
bool blockProjectile() { return m_blockProjectile; }
bool isNotPathable() { return m_isNotPathable; }
bool isPickupable() { return m_isPickupable; }
bool isHangable() { return m_isHangable; }
bool isHookSouth() { return m_isHookSouth; }
bool isHookEast() { return m_isHookEast; }
bool isRotateable() { return m_isRotateable; }
bool hasLight() { return m_hasLight; }
bool isDontHide() { return m_isDontHide; }
bool isTranslucent() { return m_isTranslucent; }
bool hasDisplacement() { return m_hasDisplacement; }
bool hasElevation() { return m_hasElevation; }
bool isLyingCorpse() { return m_isLyingCorpse; }
bool isAnimateAlways() { return m_isAnimateAlways; }
bool hasMiniMapColor() { return m_hasMiniMapColor; }
bool hasLensHelp() { return m_hasLensHelp; }
bool isFullGround() { return m_isFullGround; }
bool isIgnoreLook() { return m_isIgnoreLook; }
bool isCloth() { return m_isCloth; }
Point getDisplacement() { return m_displacement; }
int getDisplacementX() { return getDisplacement().x; }
int getDisplacementY() { return getDisplacement().y; }
int getGroundSpeed() { return m_attribs.get<uint16>(DatAttribGround); }
int getMaxTextLength() { return m_attribs.has(DatAttribWritableOnce) ? m_attribs.get<uint16>(DatAttribWritableOnce) : m_attribs.get<uint16>(DatAttribWritable); }
Light getLight() { return m_attribs.get<Light>(DatAttribLight); }
int getMinimapColor() { return m_attribs.get<uint16>(DatAttribMiniMapColor); }
int getLensHelp() { return m_attribs.get<uint16>(DatAttribLensHelp); }
int getClothSlot() { return m_attribs.get<uint16>(DatAttribCloth); }
int getElevation() { return m_attribs.get<uint16>(DatAttribElevation); }
MarketData getMarketData() { return m_attribs.get<MarketData>(DatAttribMarket); }
bool isGround() { return m_attribs.has(DatAttribGround); }
bool isGroundBorder() { return m_attribs.has(DatAttribGroundBorder); }
bool isOnBottom() { return m_attribs.has(DatAttribOnBottom); }
bool isOnTop() { return m_attribs.has(DatAttribOnTop); }
bool isContainer() { return m_attribs.has(DatAttribContainer); }
bool isStackable() { return m_attribs.has(DatAttribStackable); }
bool isForceUse() { return m_attribs.has(DatAttribForceUse); }
bool isMultiUse() { return m_attribs.has(DatAttribMultiUse); }
bool isWritable() { return m_attribs.has(DatAttribWritable); }
bool isChargeable() { return m_attribs.has(DatAttribChargeable); }
bool isWritableOnce() { return m_attribs.has(DatAttribWritableOnce); }
bool isFluidContainer() { return m_attribs.has(DatAttribFluidContainer); }
bool isSplash() { return m_attribs.has(DatAttribSplash); }
bool isNotWalkable() { return m_attribs.has(DatAttribNotWalkable); }
bool isNotMoveable() { return m_attribs.has(DatAttribNotMoveable); }
bool blockProjectile() { return m_attribs.has(DatAttribBlockProjectile); }
bool isNotPathable() { return m_attribs.has(DatAttribNotPathable); }
bool isPickupable() { return m_attribs.has(DatAttribPickupable); }
bool isHangable() { return m_attribs.has(DatAttribHangable); }
bool isHookSouth() { return m_attribs.has(DatAttribHookSouth); }
bool isHookEast() { return m_attribs.has(DatAttribHookEast); }
bool isRotateable() { return m_attribs.has(DatAttribRotateable); }
bool hasLight() { return m_attribs.has(DatAttribLight); }
bool isDontHide() { return m_attribs.has(DatAttribDontHide); }
bool isTranslucent() { return m_attribs.has(DatAttribTranslucent); }
bool hasDisplacement() { return m_attribs.has(DatAttribDisplacement); }
bool hasElevation() { return m_attribs.has(DatAttribElevation); }
bool isLyingCorpse() { return m_attribs.has(DatAttribLyingCorpse); }
bool isAnimateAlways() { return m_attribs.has(DatAttribAnimateAlways); }
bool hasMiniMapColor() { return m_attribs.has(DatAttribMiniMapColor); }
bool hasLensHelp() { return m_attribs.has(DatAttribLensHelp); }
bool isFullGround() { return m_attribs.has(DatAttribFullGround); }
bool isIgnoreLook() { return m_attribs.has(DatAttribIgnoreLook); }
bool isCloth() { return m_attribs.has(DatAttribCloth); }
private:
const TexturePtr& getTexture(int animationPhase);
@@ -172,64 +181,21 @@ private:
DatCategory m_category;
uint16 m_id;
Boolean<true> m_null;
bool m_null;
AttribStorage m_attribs;
Size m_size;
Point m_displacement;
int m_exactSize;
int m_numPatternX, m_numPatternY, m_numPatternZ;
int m_animationPhases;
int m_layers;
std::vector<int> m_spritesIndex;
std::vector<TexturePtr> m_textures;
std::vector<std::vector<Rect>> m_texturesFramesRects;
std::vector<std::vector<Rect>> m_texturesFramesOriginRects;
std::vector<std::vector<Point>> m_texturesFramesOffsets;
// dat stuff
Size m_size;
Point m_displacement;
int m_exactSize;
int m_layers;
int m_numPatternX;
int m_numPatternY;
int m_numPatternZ;
int m_animationPhases;
int m_groundSpeed;
int m_maxTextLenght;
int m_lightLevel;
int m_lightColor;
int m_miniMapColor;
int m_lensHelp;
int m_clothSlot;
int m_elevation;
Boolean<false> m_isGround;
Boolean<false> m_isGroundBorder;
Boolean<false> m_isOnBottom;
Boolean<false> m_isOnTop;
Boolean<false> m_isContainer;
Boolean<false> m_isStackable;
Boolean<false> m_isForceUse;
Boolean<false> m_isMultiUse;
Boolean<false> m_isWritable;
Boolean<false> m_isWritableOnce;
Boolean<false> m_isFluidContainer;
Boolean<false> m_isFluid;
Boolean<false> m_isNotWalkable;
Boolean<false> m_isNotMoveable;
Boolean<false> m_blockProjectile;
Boolean<false> m_isNotPathable;
Boolean<false> m_isPickupable;
Boolean<false> m_isHangable;
Boolean<false> m_isHookSouth;
Boolean<false> m_isHookEast;
Boolean<false> m_isRotateable;
Boolean<false> m_hasLight;
Boolean<false> m_isDontHide;
Boolean<false> m_isTranslucent;
Boolean<false> m_hasDisplacement;
Boolean<false> m_hasElevation;
Boolean<false> m_isLyingCorpse;
Boolean<false> m_isAnimateAlways;
Boolean<false> m_hasMiniMapColor;
Boolean<false> m_hasLensHelp;
Boolean<false> m_isFullGround;
Boolean<false> m_isIgnoreLook;
Boolean<false> m_isCloth;
};
#endif

View File

@@ -367,7 +367,7 @@ ThingPtr Tile::getTopMultiUseThing()
for(uint i = 0; i < m_things.size(); ++i) {
ThingPtr thing = m_things[i];
if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) {
if(i > 0 && thing->isFluid())
if(i > 0 && thing->isSplash())
return m_things[i-1];
return thing;
}

View File

@@ -58,7 +58,7 @@ void UIItem::drawSelf(Fw::DrawPane drawPane)
g_painter->setColor(Color::white);
m_item->draw(dest, scaleFactor, true);
if(m_font && m_item->isStackable() && m_item->getCount() > 1) {
if(m_font && (m_item->isStackable() || m_item->isChargeable()) && m_item->getCount() > 1) {
std::string count = stdext::to_string(m_item->getCount());
g_painter->setColor(Color(231, 231, 231));
m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight);