diff --git a/.gitignore b/.gitignore index 27741b4d..73f2b7f0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ CMakeFiles CMakeCache.txt cmake_install.cmake Makefile -otclient +./otclient otclient.exe *.dll .kdev* diff --git a/src/otclient/core/const.h b/src/otclient/core/const.h new file mode 100644 index 00000000..a72494db --- /dev/null +++ b/src/otclient/core/const.h @@ -0,0 +1,22 @@ +#ifndef OTCLIENT_CORE_CONST_H +#define OTCLIENT_CORE_CONST_H + +enum ThingAttributesGroup { + THING_GROUP_NONE = 0, + THING_GROUP_GROUND, + THING_GROUP_CONTAINER, + THING_GROUP_WEAPON, + THING_GROUP_AMMUNITION, + THING_GROUP_ARMOR, + THING_GROUP_RUNE, + THING_GROUP_TELEPORT, + THING_GROUP_MAGICFIELD, + THING_GROUP_WRITEABLE, + THING_GROUP_KEY, + THING_GROUP_SPLASH, + THING_GROUP_FLUID, + THING_GROUP_DOOR, + THING_GROUP_LAST +}; + +#endif diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp new file mode 100644 index 00000000..9bd12fbe --- /dev/null +++ b/src/otclient/core/creature.cpp @@ -0,0 +1,64 @@ +#include "creature.h" +#include "tibiadat.h" +#include +#include +#include "game.h" + +#include +#include +#include + + + + + + +// OW BART TEM COMO USAR 2 FRAMEBUFFER? +// SERIA O IDEAL PARA DESENHAR A COR DOS BONEQUIN. + + + + + + +Creature::Creature() +{ + m_type = Thing::TYPE_CREATURE; +} + +ThingAttributes *Creature::getAttributes() +{ + return g_tibiaDat.getCreatureAttributes(m_outfit.type); +} + +void Creature::draw(int x, int y) +{ + //ThingAttributes *creatureAttributes = getAttributes(); + int anim = 0; + + // draw outfit + internalDraw(x, y, 0, m_direction, 0, 0, anim); + + // draw addons + //for(int a = 0; a < m_outfit.addons; ++a) { + //internalDraw(x, y, 0, m_direction, m_outfit.addons & (1 << a), 0, anim); + //} + //glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT); + + //glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); + + //Color colors[4] = {Color::yellow, Color::red, Color::green, Color::blue}; + + //for(int i = 0; i < 4; ++i) { + + + /*g_graphics.bindColor(colors[i]); + internalDraw(creatureAttributes->width*32 - 32, creatureAttributes->height*32 - 32, 1, m_direction, 0, 0, 0); + framebuffer.unbind(); + + + g_graphics.bindColor(Color::green);*/ + //framebuffer.draw(x, y, creatureAttributes->width*32, creatureAttributes->height*32); + //} + //glPopAttrib(); +} diff --git a/src/otclient/core/creature.h b/src/otclient/core/creature.h new file mode 100644 index 00000000..33363fc9 --- /dev/null +++ b/src/otclient/core/creature.h @@ -0,0 +1,47 @@ +#ifndef CREATURE_H +#define CREATURE_H + +#include "thing.h" + +struct Outfit +{ + uint16 type; + uint8 head; + uint8 body; + uint8 legs; + uint8 feet; + uint8 addons; +}; + +class Creature : public Thing +{ +public: + Creature(); + + virtual ThingAttributes *getAttributes(); + void draw(int x, int y); + + void setName(const std::string& name) { m_name = name; } + std::string getName() { return m_name; } + + void setHealthPercent(uint8 healthPercent) { m_healthPercent = healthPercent; } + uint8 getHealthPercent() { return m_healthPercent; } + + void setDirection(Direction direction) { m_direction = direction; } + Direction getDirection() { return m_direction; } + + void setOutfit(const Outfit& outfit) { m_outfit = outfit; } + Outfit getOutfit() { return m_outfit; } + + virtual Creature *getCreature() { return this; } + virtual const Creature *getCreature() const { return this; } + +private: + std::string m_name; + uint8 m_healthPercent; + Direction m_direction; + Outfit m_outfit; + +}; + +#endif // CREATURE_H diff --git a/src/otclient/core/declarations.h b/src/otclient/core/declarations.h new file mode 100644 index 00000000..6a1c6878 --- /dev/null +++ b/src/otclient/core/declarations.h @@ -0,0 +1,21 @@ +#ifndef OTCLIENT_CORE_DECLARATIONS_H +#define OTCLIENT_CORE_DECLARATIONS_H + +#include +#include "const.h" + +class Thing; +class Item; +class Tile; +class Creature; +class Player; +class Effect; + +typedef std::shared_ptr ThingPtr; +typedef std::shared_ptr ItemPtr; +typedef std::shared_ptr TilePtr; +typedef std::shared_ptr CreaturePtr; +typedef std::shared_ptr PlayerPtr; +typedef std::shared_ptr EffectPtr; + +#endif diff --git a/src/otclient/core/effect.cpp b/src/otclient/core/effect.cpp new file mode 100644 index 00000000..fcdec57f --- /dev/null +++ b/src/otclient/core/effect.cpp @@ -0,0 +1,18 @@ +#include "effect.h" +#include "tibiadat.h" + +Effect::Effect() +{ + m_type = Thing::TYPE_EFFECT; +} + +ThingAttributes *Effect::getAttributes() +{ + return g_tibiaDat.getEffectAttributes(m_id); +} + +void Effect::draw(int x, int y) +{ + int anim = 0; + internalDraw(x, y, 0, 0, 0, 0, anim); +} diff --git a/src/otclient/core/effect.h b/src/otclient/core/effect.h new file mode 100644 index 00000000..e4677650 --- /dev/null +++ b/src/otclient/core/effect.h @@ -0,0 +1,19 @@ +#ifndef EFFECT_H +#define EFFECT_H + +#include +#include "thing.h" + + +class Effect : public Thing +{ +public: + Effect(); + + virtual ThingAttributes *getAttributes(); + void draw(int x, int y); + +private: +}; + +#endif // EFFECT_H diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp new file mode 100644 index 00000000..d3968153 --- /dev/null +++ b/src/otclient/core/game.cpp @@ -0,0 +1,8 @@ +#include "game.h" + +Game g_game; + +Game::Game() +{ + m_online = false; +} diff --git a/src/otclient/core/game.h b/src/otclient/core/game.h new file mode 100644 index 00000000..527ef3c9 --- /dev/null +++ b/src/otclient/core/game.h @@ -0,0 +1,33 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include "map.h" +#include "player.h" +#include + +class Game +{ +public: + Game(); + + void setProtocol(ProtocolGamePtr protocolGame) { m_protocolGame = protocolGame; } + ProtocolGamePtr getProtocol() { return m_protocolGame; } + + Map *getMap() { return &m_map; } + Player *getPlayer() { return &m_player; } + + void setOnline(bool online) { m_online = online; } + bool getOnline() { return m_online; } + +private: + Map m_map; + Player m_player; + ProtocolGamePtr m_protocolGame; + bool m_online; + +}; + +extern Game g_game; + +#endif // GAME_H diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp new file mode 100644 index 00000000..7cdc1d0f --- /dev/null +++ b/src/otclient/core/item.cpp @@ -0,0 +1,35 @@ +#include "item.h" +#include "tibiadat.h" +#include "tibiaspr.h" +#include +#include "thing.h" + +Item::Item() +{ + m_type = Thing::TYPE_ITEM; +} + +ThingAttributes *Item::getAttributes() +{ + return g_tibiaDat.getItemAttributes(m_id); +} + +void Item::draw(int x, int y) +{ + ThingAttributes *itemAttributes = getAttributes(); + + int xdiv = 0, ydiv = 0, zdiv = 0, anim = 0; + + if(itemAttributes->group == THING_GROUP_SPLASH || itemAttributes->group == THING_GROUP_FLUID || itemAttributes->stackable) { + //cDivX = subType % itemAttributes->xdiv; + //cDivY = subType / itemAttributes->xdiv; + } + else if(!itemAttributes->moveable) { + xdiv = m_position.x % itemAttributes->xdiv; + ydiv = m_position.y % itemAttributes->ydiv; + zdiv = m_position.z % itemAttributes->zdiv; + } + + for(int b = 0; b < itemAttributes->blendframes; b++) + internalDraw(x, y, b, xdiv, ydiv, zdiv, anim); +} diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h new file mode 100644 index 00000000..3b826154 --- /dev/null +++ b/src/otclient/core/item.h @@ -0,0 +1,24 @@ +#ifndef ITEM_H +#define ITEM_H + +#include +#include "thing.h" + +class Item : public Thing +{ +public: + Item(); + + virtual ThingAttributes *getAttributes(); + void draw(int x, int y); + + void setCount(uint8 count) { m_count = count; } + + virtual Item* getItem() { return this; } + virtual const Item* getItem() const { return this; } + +private: + uint8 m_count; +}; + +#endif // ITEM_H diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp new file mode 100644 index 00000000..818224ea --- /dev/null +++ b/src/otclient/core/map.cpp @@ -0,0 +1,74 @@ +#include "map.h" +#include "game.h" +#include +#include + +void Map::draw(int x, int y) +{ + if(!m_framebuffer) + m_framebuffer = FrameBufferPtr(new FrameBuffer(15*32, 11*32)); + + g_graphics.bindColor(Color::white); + m_framebuffer->bind(); + + Position *playerPos = g_game.getPlayer()->getPosition(); + + // player is above 7 + if(playerPos->z <= 7) { + + // player pos it 8-6. check if we can draw upper floors. + bool draw = true; + for(int jz = 6; jz >= 0; --jz) { + Position coverPos = Position(playerPos->x-(6-jz), playerPos->y-(6-jz), jz); + if(m_tiles[coverPos]) { + if(m_tiles[coverPos]->getStackSize() > 0 && jz < playerPos->z) { + draw = false; + } + } + } + + for(int iz = 7; iz > 0; --iz) { + + // +1 in draws cause 64x64 items may affect view. + for(int ix = -7; ix < + 8+7; ++ix) { + for(int iy = -5; iy < + 6+7; ++iy) { + Position itemPos = Position(playerPos->x + ix, playerPos->y + iy, iz); + //Position drawPos = Position(ix + 8, iy - playerPos->y + 6, iz); + //logDebug("x: ", relativePos.x, " y: ", relativePos.y, " z: ", (int)relativePos.z); + if(m_tiles[itemPos]) + m_tiles[itemPos]->draw((ix + 7 - (7-iz))*32, (iy + 5 - (7-iz))*32); + } + } + + if(!draw) + break; + } + + } + + // draw effects + for(auto it = m_effects.begin(), end = m_effects.end(); it != end; ++it) { + Position *effectPos = (*it)->getPosition(); + (*it)->draw((effectPos->x - playerPos->x + 7) * 32, (effectPos->y - playerPos->y + 5) * 32); + } + + // debug draws + g_graphics.drawBoundingRect(Rect(7*32, 5*32, 32, 32), Color::red); + + m_framebuffer->unbind(); + m_framebuffer->draw(Rect(x, y, g_graphics.getScreenSize())); +} + +void Map::addThing(ThingPtr thing, uint8 stackpos) +{ + if(thing->getType() == Thing::TYPE_ITEM || thing->getType() == Thing::TYPE_CREATURE) { + if(!m_tiles[*thing->getPosition()]) { + m_tiles[*thing->getPosition()] = TilePtr(new Tile()); + } + + m_tiles[*thing->getPosition()]->addThing(thing, stackpos); + } + else if(thing->getType() == Thing::TYPE_EFFECT) { + m_effects.push_back(thing); + } +} diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h new file mode 100644 index 00000000..6b4dffbc --- /dev/null +++ b/src/otclient/core/map.h @@ -0,0 +1,29 @@ +#ifndef MAP_H +#define MAP_H + +#include "tile.h" +#include "effect.h" +#include +#include + +struct MapPositionHasher : std::unary_function { + std::size_t operator()(const Position& pos) const { + return ((((pos.x * 65536) + pos.y) * 15) + pos.z) % 1000000; + } +}; + +class Map +{ +public: + void addThing(ThingPtr thing, uint8 stackpos = 0); + + void draw(int x, int y); + +private: + std::unordered_map m_tiles; + std::list m_effects; + + FrameBufferPtr m_framebuffer; +}; + +#endif diff --git a/src/otclient/core/player.h b/src/otclient/core/player.h new file mode 100644 index 00000000..b2aff45e --- /dev/null +++ b/src/otclient/core/player.h @@ -0,0 +1,22 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include "creature.h" + +class Player : public Creature +{ +public: + void setDrawSpeed(uint16 drawSpeed) { m_drawSpeed = drawSpeed; } + uint16 getDrawSpeed() { return m_drawSpeed; } + + void setCanReportBugs(uint8 canReportBugs) { m_canReportBugs = (canReportBugs != 0); } + bool getCanReportBugs() { return m_canReportBugs; } + +private: + + uint16 m_drawSpeed; + bool m_canReportBugs; + +}; + +#endif diff --git a/src/otclient/core/thing.cpp b/src/otclient/core/thing.cpp new file mode 100644 index 00000000..70dbb58c --- /dev/null +++ b/src/otclient/core/thing.cpp @@ -0,0 +1,83 @@ +#include "thing.h" +#include "tibiaspr.h" +#include + +ThingAttributes::ThingAttributes() +{ + group = THING_GROUP_NONE; + blockSolid = false; + hasHeight = false; + blockPathFind = false; + blockProjectile = false; + alwaysOnTop = false; + alwaysOnTopOrder = 0; + stackable = false; + useable = false; + moveable = true; + pickupable = false; + rotable = false; + readable = false; + lookThrough = false; + speed = 0; + lightLevel = 0; + lightColor = 0; + isVertical = false; + isHorizontal = false; + isHangable = false; + miniMapColor = 0; + hasMiniMapColor = false; + subParam07 = 0; + subParam08 = 0; + sprites = NULL; + width = 0; + height = 0; + blendframes = 0; + xdiv = 0; + ydiv = 0; + zdiv = 0; + animcount = 0; + xOffset = 0; + yOffset = 0; +} + +ThingAttributes::~ThingAttributes() +{ + if(sprites) + delete []sprites; +} + +Thing::Thing() +{ + m_type = TYPE_NONE; +} + +void Thing::internalDraw(int x, int y, int blendframes, int xdiv, int ydiv, int zdiv, int anim) +{ + ThingAttributes *thingAttributes = getAttributes(); + if(!thingAttributes) + return; + + for(int yi = 0; yi < thingAttributes->height; yi++) { + for(int xi = 0; xi < thingAttributes->width; xi++) { + uint16 sprIndex = xi + + yi * thingAttributes->width + + blendframes * thingAttributes->width * thingAttributes->height + + xdiv * thingAttributes->width * thingAttributes->height * thingAttributes->blendframes + + ydiv * thingAttributes->width * thingAttributes->height * thingAttributes->blendframes * thingAttributes->xdiv + + zdiv * thingAttributes->width * thingAttributes->height * thingAttributes->blendframes * thingAttributes->xdiv * thingAttributes->ydiv + + anim * thingAttributes->width * thingAttributes->height * thingAttributes->blendframes * thingAttributes->xdiv * thingAttributes->ydiv * thingAttributes->zdiv; + uint16 itemId = thingAttributes->sprites[sprIndex]; + if(itemId == 0xFFFF) + continue; + TexturePtr data = g_tibiaSpr.getSprite(itemId); + + int offsetX = 0, offsetY = 0; + if(thingAttributes->hasHeight) { + offsetX = thingAttributes->xOffset; + offsetY = thingAttributes->xOffset; // << look to xoffset + } + + g_graphics.drawTexturedRect(Rect((x - xi*32) - offsetX, (y - yi*32) - offsetY, 32, 32), data, Rect(0, 0, 32, 32)); + } + } +} diff --git a/src/otclient/core/thing.h b/src/otclient/core/thing.h new file mode 100644 index 00000000..b96f8e4a --- /dev/null +++ b/src/otclient/core/thing.h @@ -0,0 +1,65 @@ +#ifndef THING_H +#define THING_H + +#include +#include "declarations.h" + +struct ThingAttributes +{ + ThingAttributes(); + ~ThingAttributes(); + + bool stackable, alwaysOnTop, useable, readable, moveable, blockSolid, blockProjectile, blockPathFind, pickupable, + isHangable, isHorizontal, isVertical, rotable, hasHeight, lookThrough, hasMiniMapColor; + uint8 alwaysOnTopOrder, width, height, blendframes, xdiv, ydiv, zdiv, animcount, xOffset, yOffset; + uint16 id, speed, subParam07, subParam08, lightLevel, lightColor, miniMapColor; + uint16 *sprites; + ThingAttributesGroup group; +}; + +class Creature; +class Item; + +class Thing; +typedef std::shared_ptr ThingPtr; + +class Thing +{ +public: + Thing(); + + enum Type { + TYPE_NONE, + TYPE_ITEM, + TYPE_CREATURE, + TYPE_EFFECT, + TYPE_SHOT + }; + + void setId(uint32 id) { m_id = id; } + uint32 getId() { return m_id; } + + void setType(Type type) { m_type = type; } + Type getType() const { return m_type; } + + void setPosition(const Position& position) { m_position = position; } + Position *getPosition() { return &m_position; } + + virtual void draw(int, int) {} + virtual ThingAttributes *getAttributes() { return NULL; } + + virtual Item* getItem() { return NULL; } + virtual const Item *getItem() const { return NULL; } + virtual Creature *getCreature() { return NULL; } + virtual const Creature *getCreature() const { return NULL; } + +protected: + void internalDraw(int x, int y, int blendframes, int xdiv, int ydiv, int zdiv, int anim); + + uint32 m_id; + Type m_type; + Position m_position; + +}; + +#endif // THING_H diff --git a/src/otclient/core/tibiadat.cpp b/src/otclient/core/tibiadat.cpp new file mode 100644 index 00000000..10034975 --- /dev/null +++ b/src/otclient/core/tibiadat.cpp @@ -0,0 +1,205 @@ +#include "tibiadat.h" +#include "tibiaspr.h" +#include + +TibiaDat g_tibiaDat; + +bool TibiaDat::load(const std::string& filename) +{ + std::stringstream fin; + g_resources.loadFile(filename, fin); + + fin.read((char*)&m_signature, 4); + + fin.read((char*)&m_groupCount[0], 2); + fin.read((char*)&m_groupCount[1], 2); + fin.read((char*)&m_groupCount[2], 2); + fin.read((char*)&m_groupCount[3], 2); + + m_groupCount[0] -= 99; + + m_totalCount = m_groupCount[0] + m_groupCount[1] + m_groupCount[2] + m_groupCount[3]; + + m_thingsAttributes = new ThingAttributes*[m_totalCount+1]; + for(uint16 i = 0; i <= m_totalCount; i++) + m_thingsAttributes[i] = NULL; + + uint8 read_byte; + uint16 read_short; + //uint32 read_long; + + for(uint16 id = 0; (id <= m_totalCount) && !fin.eof(); id++) { + m_thingsAttributes[id] = new ThingAttributes(); + m_thingsAttributes[id]->id = id; + + uint8 opt; + bool done = false; + while(!done) { + fin.read((char*)&opt, 1); + + if(opt == 0x00) { // Ground tile + fin.read((char*)&m_thingsAttributes[id]->speed, 2); + m_thingsAttributes[id]->group = THING_GROUP_GROUND; + } + else if(opt == 0x01) { // All OnTop + m_thingsAttributes[id]->alwaysOnTop = true; + m_thingsAttributes[id]->alwaysOnTopOrder = 1; + } + else if(opt == 0x02) { // Can walk trough (open doors, arces, bug pen fence) + m_thingsAttributes[id]->alwaysOnTop = true; + m_thingsAttributes[id]->alwaysOnTopOrder = 2; + } + else if(opt == 0x03) { // Can walk trough (arces) + m_thingsAttributes[id]->alwaysOnTop = true; + m_thingsAttributes[id]->alwaysOnTopOrder = 3; + } + else if(opt == 0x04) { // Container + m_thingsAttributes[id]->group = THING_GROUP_CONTAINER; + } + else if(opt == 0x05) { // Stackable + m_thingsAttributes[id]->stackable = true; + } + else if(opt == 0x06) { // Unknown + + } + else if(opt == 0x07) { // Useable + m_thingsAttributes[id]->useable = true; + } + else if(opt == 0x08) { // Writtable + m_thingsAttributes[id]->group = THING_GROUP_WRITEABLE; + m_thingsAttributes[id]->readable = true; + fin.read((char*)&m_thingsAttributes[id]->subParam07, 2); + } + else if(opt == 0x09) { // Writtable once + // Writtable objects that can't be edited by players + m_thingsAttributes[id]->readable = true; + fin.read((char*)&m_thingsAttributes[id]->subParam08, 2); + } + else if(opt == 0x0A) { // Fluid containers + fin.read((char*)&read_byte, 1); + m_thingsAttributes[id]->group = THING_GROUP_FLUID; + } + else if(opt == 0x0B) { // Splashes + m_thingsAttributes[id]->group = THING_GROUP_SPLASH; + } + else if(opt == 0x0C) { // Blocks solid objects (creatures, walls etc) + m_thingsAttributes[id]->blockSolid = true; + } + else if(opt == 0x0D) { // Not moveable + m_thingsAttributes[id]->moveable = false; + } + else if(opt == 0x0E) { // Blocks missiles (walls, magic wall etc) + m_thingsAttributes[id]->blockProjectile = true; + } + else if(opt == 0x0F) { // Blocks pathfind algorithms (monsters) + m_thingsAttributes[id]->blockPathFind = true; + } + else if(opt == 0x10) { // Blocks monster movement (flowers, parcels etc) + m_thingsAttributes[id]->pickupable = true; + } + else if(opt == 0x11) { // Hangable objects (wallpaper etc) + m_thingsAttributes[id]->isHangable = true; + } + else if(opt == 0x12) { // Horizontal wall + m_thingsAttributes[id]->isHorizontal = true; + } + else if(opt == 0x13) { // Vertical wall + m_thingsAttributes[id]->isVertical = true; + } + else if(opt == 0x14) { // Rotable + m_thingsAttributes[id]->rotable = true; + } + else if(opt == 0x15) { // Light info + fin.read((char*)&m_thingsAttributes[id]->lightLevel, 2); + fin.read((char*)&m_thingsAttributes[id]->lightColor, 2); + } + else if(opt == 0x16) { + } + else if(opt == 0x17) { // Changes floor + } + else if(opt == 0x18) { // Thing must be drawed with offset + m_thingsAttributes[id]->hasHeight = true; + fin.read((char*)&m_thingsAttributes[id]->xOffset, 1); + fin.read((char*)&m_thingsAttributes[id]->yOffset, 1); + + fin.read((char*)&read_short, 2); + } + else if(opt == 0x19) { // pixels characters height + + fin.read((char*)&m_thingsAttributes[id]->xOffset, 1); + fin.read((char*)&m_thingsAttributes[id]->yOffset, 1); + //logDebug((int)m_thingsAttributes[id]->xOffset, " ", (int)m_thingsAttributes[id]->yOffset); + } + else if(opt == 0x1A) { + //m_thingsAttributes[id]->hasHeight = true; + } + else if(opt == 0x1B) { + } + else if(opt == 0x1C) { // Minimap color + fin.read((char*)&m_thingsAttributes[id]->miniMapColor, 2); + m_thingsAttributes[id]->hasMiniMapColor = true; + } + else if(opt == 0x1D) { // Unknown + fin.read((char*)&read_short, 2); + if(read_short == 1112) + m_thingsAttributes[id]->readable = true; + } + else if(opt == 0x1E) { + } + else if(opt == 0x1F) { + m_thingsAttributes[id]->lookThrough = true; + } + else if(opt == 0x20) { + } + else if(opt == 0xFF) { + done = true; + } + else { + logDebug("Unknown byte code: ", opt); + return false; + } + } + + fin.read((char*)&m_thingsAttributes[id]->width, 1); + fin.read((char*)&m_thingsAttributes[id]->height, 1); + if((m_thingsAttributes[id]->width > 1) || (m_thingsAttributes[id]->height > 1)) + fin.read((char*)&read_byte, 1); + + fin.read((char*)&m_thingsAttributes[id]->blendframes, 1); + fin.read((char*)&m_thingsAttributes[id]->xdiv, 1); + fin.read((char*)&m_thingsAttributes[id]->ydiv, 1); + fin.read((char*)&m_thingsAttributes[id]->zdiv, 1); + fin.read((char*)&m_thingsAttributes[id]->animcount, 1); + + // Read sprites id. + uint16 totalSprites = m_thingsAttributes[id]->width * m_thingsAttributes[id]->height * m_thingsAttributes[id]->blendframes * m_thingsAttributes[id]->xdiv * m_thingsAttributes[id]->ydiv * m_thingsAttributes[id]->zdiv * m_thingsAttributes[id]->animcount; + + m_thingsAttributes[id]->sprites = new uint16[totalSprites]; + for(uint16 i = 0; i < totalSprites; i++) { + fin.read((char*)&read_short, 2); + m_thingsAttributes[id]->sprites[i] = read_short-1; + } + } + + return true; +} + +ThingAttributes *TibiaDat::getItemAttributes(uint16 id) +{ + return m_thingsAttributes[id - 100]; +} + +ThingAttributes *TibiaDat::getCreatureAttributes(uint16 id) +{ + return m_thingsAttributes[id - 1 + m_groupCount[0]]; +} + +ThingAttributes *TibiaDat::getEffectAttributes(uint16 id) +{ + return m_thingsAttributes[id - 1 + m_groupCount[0] + m_groupCount[1]]; +} + +ThingAttributes *TibiaDat::getShotAttributes(uint16 id) +{ + return m_thingsAttributes[id - 100 + m_groupCount[0] + m_groupCount[1] + m_groupCount[2]]; +} diff --git a/src/otclient/core/tibiadat.h b/src/otclient/core/tibiadat.h new file mode 100644 index 00000000..e2e7b2a6 --- /dev/null +++ b/src/otclient/core/tibiadat.h @@ -0,0 +1,31 @@ +#ifndef TIBIADAT_H +#define TIBIADAT_H + +#include +#include "thing.h" + +class TibiaDat +{ +public: + bool load(const std::string& filename); + + ThingAttributes *getItemAttributes(uint16 id); + ThingAttributes *getCreatureAttributes(uint16 id); + ThingAttributes *getEffectAttributes(uint16 id); + ThingAttributes *getShotAttributes(uint16 id); + + uint16 getGroupCount(int i) { return m_groupCount[i]; } + + uint32 getSignature() { return m_signature; } + uint16 getTotalCount() { return m_totalCount; } + +private: + uint32 m_signature, m_totalCount; + uint16 m_groupCount[4]; + + ThingAttributes **m_thingsAttributes; +}; + +extern TibiaDat g_tibiaDat; + +#endif // TIBIADAT_H diff --git a/src/otclient/core/tibiaspr.cpp b/src/otclient/core/tibiaspr.cpp new file mode 100644 index 00000000..18f5f36e --- /dev/null +++ b/src/otclient/core/tibiaspr.cpp @@ -0,0 +1,120 @@ +#include "tibiaspr.h" +#include + +TibiaSpr g_tibiaSpr; + +TibiaSpr::TibiaSpr() +{ + m_sprites = NULL; + m_spritesCount = 0; +} + +TibiaSpr::~TibiaSpr() +{ + //for(uint16 i = 0; i < m_spritesCount; i++) + //if(m_sprites[i]) + //delete m_sprites[i]; + + if(m_sprites) + delete []m_sprites; + + //delete m_transparentSprite; +} + +bool TibiaSpr::load(const std::string &filename) +{ + g_resources.loadFile(filename, m_fin); + + m_fin.read((char*)&m_signature, 4); + m_fin.read((char*)&m_spritesCount, 2); + + m_sprites = new TexturePtr[m_spritesCount]; + + //for(uint16 i = 0; i < m_spritesCount; i++) + //m_sprites[i] = NULL; + + + uchar pixels[4096]; + for(int i = 0; i < 32*32*4; i += 4) { + pixels[i + 0] = 0xFF; + pixels[i + 1] = 0x00; + pixels[i + 2] = 0xFF; + pixels[i + 3] = 0xFF; + } + m_transparentSprite = TexturePtr(new Texture(32, 32, 4, pixels)); + + return true; +} + +TexturePtr TibiaSpr::loadSprite(uint32 id) +{ + if(m_fin.eof()) + return TexturePtr(); + + m_fin.seekg((id * 4) + 6, std::ios_base::beg); + + uint32 spriteAddress; + m_fin.read((char*)&spriteAddress, 4); + + if(spriteAddress == 0) // Some error on tibia.spr, save a blank image. + return TexturePtr(); + + m_fin.seekg(spriteAddress, std::ios_base::beg); + + uint32 colorKey = 0; + m_fin.read((char*)&colorKey, 3); + + uint16 spriteSize; + m_fin.read((char*)&spriteSize, 2); + + uchar image[4096]; + uint16 imgPos = 0, read = 0, transparentPixels, coloredPixels, x; + while(read < spriteSize) { + m_fin.read((char*)&transparentPixels, 2); + m_fin.read((char*)&coloredPixels, 2); + + for(x = 0; x < transparentPixels; x++) { + image[imgPos + 0] = 0x00; + image[imgPos + 1] = 0x00; + image[imgPos + 2] = 0x00; + image[imgPos + 3] = 0x00; + imgPos += 4; + } + + for(x = 0; x < coloredPixels; x++) { + m_fin.read((char*)&image[imgPos + 0], 1); + m_fin.read((char*)&image[imgPos + 1], 1); + m_fin.read((char*)&image[imgPos + 2], 1); + image[imgPos + 3] = 0xFF; + + imgPos += 4; + } + + read += 4 + (3 * coloredPixels); + } + + // Fill remaining bytes with pink. + if(imgPos < 32 * 32 * 4) { + for(x = imgPos; x < 32 * 32 * 4; x += 4) { + image[x + 0] = 0xFF; + image[x + 1] = 0x00; + image[x + 2] = 0xFF; + image[x + 3] = 0x00; + } + } + + return TexturePtr(new Texture(32, 32, 4, image)); +} + +TexturePtr TibiaSpr::getSprite(uint32 id) +{ + if(!m_sprites) + return m_transparentSprite; + + if(!m_sprites[id]) { + m_sprites[id] = loadSprite(id); + if(!m_sprites[id]) + return m_transparentSprite; + } + return m_sprites[id]; +} diff --git a/src/otclient/core/tibiaspr.h b/src/otclient/core/tibiaspr.h new file mode 100644 index 00000000..2465391a --- /dev/null +++ b/src/otclient/core/tibiaspr.h @@ -0,0 +1,31 @@ +#ifndef TIBIASPR_H +#define TIBIASPR_H + +#include +#include + +class TibiaSpr +{ +public: + TibiaSpr(); + ~TibiaSpr(); + + bool load(const std::string& filename); + + uint32 getSignature() { return m_signature; } + uint16 getSpritesCount() { return m_spritesCount; } + TexturePtr getSprite(uint32 id); + +private: + TexturePtr loadSprite(uint32 id); + + std::stringstream m_fin; + uint32 m_signature; + uint16 m_spritesCount; + TexturePtr *m_sprites; + TexturePtr m_transparentSprite; +}; + +extern TibiaSpr g_tibiaSpr; + +#endif // TIBIASPR_H diff --git a/src/otclient/core/tile.cpp b/src/otclient/core/tile.cpp new file mode 100644 index 00000000..f39620fb --- /dev/null +++ b/src/otclient/core/tile.cpp @@ -0,0 +1,66 @@ +#include "tile.h" +#include "item.h" +#include "tibiadat.h" + +Tile::Tile() +{ + m_ground = NULL; +} + +void Tile::addThing(ThingPtr thing, uint8 stackpos) +{ + if(!thing) + return; + + ThingAttributes *thingAttributes = thing->getAttributes(); + if(thingAttributes) { + if(thing->getType() == Thing::TYPE_ITEM) { + if(thingAttributes->group == THING_GROUP_GROUND) + m_ground = thing; + else { + if(thingAttributes->alwaysOnTop) + m_itemsTop.push_back(thing); + else + m_itemsBot.push_back(thing); + } + } + else if(thing->getType() == Thing::TYPE_CREATURE) { + m_creatures.push_back(thing); + } + } + +} + +void Tile::draw(int x, int y) +{ + if(m_ground) + m_ground->draw(x, y); + + for(auto it = m_itemsTop.begin(), end = m_itemsTop.end(); it != end; ++it) + (*it)->draw(x, y); + + for(auto it = m_creatures.begin(), end = m_creatures.end(); it != end; ++it) + (*it)->draw(x, y); + + for(auto it = m_itemsBot.begin(), end = m_itemsBot.end(); it != end; ++it) + (*it)->draw(x, y); + +} + +bool Tile::hasGround() +{ + return m_ground != 0; +} + +int Tile::getStackSize() +{ + int ret = 0; + if(m_ground) + ret++; + + ret += m_itemsBot.size(); + ret += m_creatures.size(); + ret += m_itemsTop.size(); + + return ret; +} diff --git a/src/otclient/core/tile.h b/src/otclient/core/tile.h new file mode 100644 index 00000000..b84ff85d --- /dev/null +++ b/src/otclient/core/tile.h @@ -0,0 +1,24 @@ +#ifndef TILE_H +#define TILE_H + +#include "thing.h" + +class Tile +{ +public: + Tile(); + + void addThing(ThingPtr thing, uint8 stackpos); + + void draw(int x, int y); + bool hasGround(); + int getStackSize(); + +private: + ThingPtr m_ground; + std::list m_itemsBot; + std::list m_creatures; + std::list m_itemsTop; +}; + +#endif diff --git a/src/otclient/global.h b/src/otclient/global.h new file mode 100644 index 00000000..2e493b3e --- /dev/null +++ b/src/otclient/global.h @@ -0,0 +1,9 @@ +#ifndef OTCLIENT_GLOBAL_H +#define OTCLIENT_GLOBAL_H + +#include + +// widely used headers +#include + +#endif diff --git a/src/otclient/net/declarations.h b/src/otclient/net/declarations.h new file mode 100644 index 00000000..63bbc851 --- /dev/null +++ b/src/otclient/net/declarations.h @@ -0,0 +1,52 @@ +#ifndef OTCLIENT_NET_DECLARATIONS_H +#define OTCLIENT_NET_DECLARATIONS_H + +#include +#include + +class ProtocolLogin; +class ProtocolGame; + +typedef std::shared_ptr ProtocolGamePtr; +typedef std::shared_ptr ProtocolLoginPtr; + + + +#define CIPSOFT_PUBLIC_RSA "1321277432058722840622950990822933849527763264961655079678763618" \ + "4334395343554449668205332383339435179772895415509701210392836078" \ + "6959821132214473291575712138800495033169914814069637740318278150" \ + "2907336840325241747827401343576296990629870233111328210165697754" \ + "88792221429527047321331896351555606801473202394175817" + + +#define OTSERV_PUBLIC_RSA "1091201329673994292788609605089955415282375029027981291234687579" \ + "3726629149257644633073969600111060390723088861007265581882535850" \ + "3429057592827629436413108566029093628212635953836686562675849720" \ + "6207862794310902180176810615217550567108238764764442605581471797" \ + "07119674283982419152118103759076030616683978566631413" + +// TODO: place it somewhere else +enum SpeakClasses { + SPEAK_SAY = 0x01, //normal talk + SPEAK_WHISPER = 0x02, //whispering - #w text + SPEAK_YELL = 0x03, //yelling - #y text + SPEAK_PRIVATE_PN = 0x04, //Player-to-NPC speaking(NPCs channel) + SPEAK_PRIVATE_NP = 0x05, //NPC-to-Player speaking + SPEAK_PRIVATE = 0x06, //Players speaking privately to players + SPEAK_CHANNEL_Y = 0x07, //Yellow message in chat + SPEAK_CHANNEL_W = 0x08, //White message in chat + SPEAK_RVR_CHANNEL = 0x09, //Reporting rule violation - Ctrl+R + SPEAK_RVR_ANSWER = 0x0A, //Answering report + SPEAK_RVR_CONTINUE = 0x0B, //Answering the answer of the report + SPEAK_BROADCAST = 0x0C, //Broadcast a message - #b + SPEAK_CHANNEL_R1 = 0x0D, //Talk red on chat - #c + SPEAK_PRIVATE_RED = 0x0E, //Red private - @name@ text + SPEAK_CHANNEL_O = 0x0F, //Talk orange on text + //SPEAK_ = 0x10, //? + SPEAK_CHANNEL_R2 = 0x11, //Talk red anonymously on chat - #d + //SPEAK_ = 0x12, //? + SPEAK_MONSTER_SAY = 0x13, //Talk orange + SPEAK_MONSTER_YELL = 0x14 //Yell orange +}; + +#endif diff --git a/src/otclient/net/protocolgame.cpp b/src/otclient/net/protocolgame.cpp new file mode 100644 index 00000000..6134842d --- /dev/null +++ b/src/otclient/net/protocolgame.cpp @@ -0,0 +1,99 @@ +#include +#include +#include + +ProtocolGame::ProtocolGame() +{ + m_checksumEnabled = false; + g_game.setProtocol(ProtocolGamePtr(this)); +} + +ProtocolGame::~ProtocolGame() +{ + sendLogout(); + g_game.setProtocol(NULL); +} + +void ProtocolGame::login(const std::string& accountName, const std::string& accountPassword, uint32 ip, uint16 port, const std::string& characterName) +{ + if(accountName.empty() || accountPassword.empty()) { + callLuaField("onError", "You must enter an account name and password."); + return; + } + + m_accountName = accountName; + m_accountPassword = accountPassword; + m_characterName = characterName; + + char host[16]; + sprintf(host, "%d.%d.%d.%d", (uint8)ip, (uint8)(ip >> 8), (uint8)(ip >> 16), (uint8)(ip >> 24)); + + connect(host, port); +} + +void ProtocolGame::onConnect() +{ + recv(); +} + +void ProtocolGame::sendLoginPacket(uint32 timestamp, uint8 unknown) +{ + OutputMessage oMsg; + + oMsg.addU8(0x0A); // Protocol id + oMsg.addU16(0x02); // OS + oMsg.addU16(862); // Client version + + oMsg.addU8(0); // First RSA byte must be 0x00 // 1 + + // Generete xtea key. + m_xteaKey[0] = 432324; + m_xteaKey[1] = 24324; + m_xteaKey[2] = 423432; + m_xteaKey[3] = 234324; + + // Add xtea key + oMsg.addU32(m_xteaKey[0]); // 5 + oMsg.addU32(m_xteaKey[1]); // 9 + oMsg.addU32(m_xteaKey[2]); // 13 + oMsg.addU32(m_xteaKey[3]); // 17 + + oMsg.addU8(0); // is gm set? + oMsg.addString(m_accountName); // Account Name // 20 + oMsg.addString(m_characterName); // Character Name // 22 + oMsg.addString(m_accountPassword); // Password // 24 + + oMsg.addU32(timestamp); // 28 + oMsg.addU8(unknown); // 29 + + // Packet data must have since byte 0, start, 128 bytes + oMsg.addPaddingBytes(128 - (29 + m_accountName.length() + m_characterName.length() + m_accountPassword.length())); + + // Encrypt msg with RSA + if(!Rsa::encrypt((char*)oMsg.getBuffer() + 6 + oMsg.getMessageSize() - 128, 128, OTSERV_PUBLIC_RSA)) + return; + + send(oMsg); + + m_xteaEncryptionEnabled = true; + + recv(); +} + +void ProtocolGame::onRecv(InputMessage& inputMessage) +{ + static bool firstRecv = true; + if(firstRecv) { + inputMessage.skipBytes(3); + uint32 timestamp = inputMessage.getU32(); + uint8 unknown = inputMessage.getU8(); + + m_checksumEnabled = true; + sendLoginPacket(timestamp, unknown); + + firstRecv = false; + } + else { + parseMessage(inputMessage); + } +} diff --git a/src/otclient/net/protocolgame.h b/src/otclient/net/protocolgame.h new file mode 100644 index 00000000..b4faa949 --- /dev/null +++ b/src/otclient/net/protocolgame.h @@ -0,0 +1,120 @@ +#ifndef PROTOCOLGAME_H +#define PROTOCOLGAME_H + +#include "declarations.h" +#include + +#include +#include + +class ProtocolGame : public Protocol +{ + +public: + ProtocolGame(); + ~ProtocolGame(); + +public: + void login(const std::string& accountName, const std::string& accountPassword, uint32 ip, uint16 port, const std::string& characterName); + + void onConnect(); + void onRecv(InputMessage& inputMessage); + + // Send Messages + void sendLogout(); + void sendPing(); + void sendWalkNorth(); + void sendWalkEast(); + void sendWalkSouth(); + void sendWalkWest(); + +private: + void sendLoginPacket(uint32 timestamp, uint8 unknown); + + // Parse Messages + void parseMessage(InputMessage& msg); + + void parsePlayerLogin(InputMessage& msg); + void parseGMActions(InputMessage& msg); + void parseErrorMessage(InputMessage& msg); + void parseFYIMessage(InputMessage& msg); + void parseWaitList(InputMessage& msg); + void parsePing(InputMessage&); + void parseDeath(InputMessage&); + void parseCanReportBugs(InputMessage& msg); + void parseMapDescription(InputMessage& msg); + void parseMoveNorth(InputMessage& msg); + void parseMoveEast(InputMessage& msg); + void parseMoveSouth(InputMessage& msg); + void parseMoveWest(InputMessage& msg); + void parseUpdateTile(InputMessage& msg); + void parseTileAddThing(InputMessage& msg); + void parseTileTransformThing(InputMessage& msg); + void parseTileRemoveThing(InputMessage& msg); + void parseCreatureMove(InputMessage& msg); + void parseOpenContainer(InputMessage& msg); + void parseCloseContainer(InputMessage& msg); + void parseContainerAddItem(InputMessage& msg); + void parseContainerUpdateItem(InputMessage& msg); + void parseContainerRemoveItem(InputMessage& msg); + void parseAddInventoryItem(InputMessage& msg); + void parseRemoveInventoryItem(InputMessage& msg); + void parseOpenShopWindow(InputMessage& msg); + void parsePlayerCash(InputMessage& msg); + void parseCloseShopWindow(InputMessage&); + void parseWorldLight(InputMessage& msg); + void parseMagicEffect(InputMessage& msg); + void parseAnimatedText(InputMessage& msg); + void parseDistanceShot(InputMessage& msg); + void parseCreatureSquare(InputMessage& msg); + void parseCreatureHealth(InputMessage& msg); + void parseCreatureLight(InputMessage& msg); + void parseCreatureOutfit(InputMessage& msg); + void parseCreatureSpeed(InputMessage& msg); + void parseCreatureSkulls(InputMessage& msg); + void parseCreatureShields(InputMessage& msg); + void parseCreatureTurn(InputMessage& msg); + void parseItemTextWindow(InputMessage& msg); + void parseHouseTextWindow(InputMessage& msg); + void parsePlayerStats(InputMessage& msg); + void parsePlayerSkills(InputMessage& msg); + void parsePlayerIcons(InputMessage& msg); + void parsePlayerCancelAttack(InputMessage& msg); + void parseCreatureSpeak(InputMessage& msg); + void parseChannelList(InputMessage& msg); + void parseOpenChannel(InputMessage& msg); + void parseOpenPrivatePlayerChat(InputMessage& msg); + void parseOpenRuleViolation(InputMessage& msg); + void parseRuleViolationAF(InputMessage& msg); + void parseRuleViolationB0(InputMessage& msg); + void parseRuleViolationB1(InputMessage& msg); + void parseCreatePrivateChannel(InputMessage& msg); + void parseClosePrivateChannel(InputMessage& msg); + void parseSafeTradeRequest(InputMessage& msg); + void parseSafeTradeClose(InputMessage&); + void parseTextMessage(InputMessage& msg); + void parseCancelWalk(InputMessage& msg); + void parseFloorChangeUp(InputMessage& msg); + void parseFloorChangeDown(InputMessage& msg); + void parseOutfitWindow(InputMessage& msg); + void parseVipState(InputMessage& msg); + void parseVipLogin(InputMessage& msg); + void parseVipLogout(InputMessage& msg); + void parseShowTutorial(InputMessage& msg); + void parseAddMarker(InputMessage& msg); + void parseQuestList(InputMessage& msg); + void parseQuestPartList(InputMessage& msg); + + void setMapDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height); + void setFloorDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height, int32 offset, int32* skipTiles); + void setTileDescription(InputMessage& msg, Position position); + ThingPtr internalGetThing(InputMessage& msg); + Outfit internalCreatureOutfit(InputMessage& msg); + ItemPtr internalGetItem(InputMessage& msg, uint16 id); + + Position parsePosition(InputMessage& msg); + + std::string m_accountName, m_accountPassword, m_characterName; +}; + +#endif // PROTOCOLGAME_H diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp new file mode 100644 index 00000000..411d55f2 --- /dev/null +++ b/src/otclient/net/protocolgameparse.cpp @@ -0,0 +1,943 @@ +#include "protocolgame.h" + +#include +#include +#include +#include +#include + +void ProtocolGame::parseMessage(InputMessage& msg) +{ + while(!msg.end()) { + uint8 opt = msg.getU8(); + + dump << "Protocol opt: " << std::hex << (int)opt << std::dec; + + switch(opt) { + case 0x0A: + parsePlayerLogin(msg); + break; + case 0x0B: + parseGMActions(msg); + break; + case 0x14: + parseErrorMessage(msg); + break; + case 0x15: + parseFYIMessage(msg); + break; + case 0x16: + parseWaitList(msg); + break; + case 0x1E: + parsePing(msg); + break; + case 0x28: + parseDeath(msg); + break; + case 0x32: + parseCanReportBugs(msg); + break; + case 0x64: + parseMapDescription(msg); + break; + case 0x65: + parseMoveNorth(msg); + break; + case 0x66: + parseMoveEast(msg); + break; + case 0x67: + parseMoveSouth(msg); + break; + case 0x68: + parseMoveWest(msg); + break; + case 0x69: + parseUpdateTile(msg); + break; + case 0x6A: + parseTileAddThing(msg); + break; + case 0x6B: + parseTileTransformThing(msg); + break; + case 0x6C: + parseTileRemoveThing(msg); + break; + case 0x6D: + parseCreatureMove(msg); + break; + case 0x6E: + parseOpenContainer(msg); + break; + case 0x6F: + parseCloseContainer(msg); + break; + case 0x70: + parseContainerAddItem(msg); + break; + case 0x71: + parseContainerUpdateItem(msg); + break; + case 0x72: + parseContainerRemoveItem(msg); + break; + case 0x78: + parseAddInventoryItem(msg); + break; + case 0x79: + parseRemoveInventoryItem(msg); + break; + case 0x7A: + parseOpenShopWindow(msg); + break; + case 0x7B: + parsePlayerCash(msg); + break; + case 0x7C: + parseCloseShopWindow(msg); + break; + case 0x7D: + parseSafeTradeRequest(msg); + break; + case 0x7E: + parseSafeTradeRequest(msg); + break; + case 0x7F: + parseSafeTradeClose(msg); + break; + case 0x82: + parseWorldLight(msg); + break; + case 0x83: + parseMagicEffect(msg); + break; + case 0x84: + parseAnimatedText(msg); + break; + case 0x85: + parseDistanceShot(msg); + break; + case 0x86: + parseCreatureSquare(msg); + break; + case 0x8C: + parseCreatureHealth(msg); + break; + case 0x8D: + parseCreatureLight(msg); + break; + case 0x8E: + parseCreatureOutfit(msg); + break; + case 0x8F: + parseCreatureSpeed(msg); + break; + case 0x90: + parseCreatureSkulls(msg); + break; + case 0x91: + parseCreatureShields(msg); + break; + case 0x96: + parseItemTextWindow(msg); + break; + case 0x97: + parseHouseTextWindow(msg); + break; + case 0xA0: + parsePlayerStats(msg); + break; + case 0xA1: + parsePlayerSkills(msg); + break; + case 0xA2: + parsePlayerIcons(msg); + break; + case 0xA3: + parsePlayerCancelAttack(msg); + break; + case 0xAA: + parseCreatureSpeak(msg); + break; + case 0xAB: + parseChannelList(msg); + break; + case 0xAC: + parseOpenChannel(msg); + break; + case 0xAD: + parseOpenPrivatePlayerChat(msg); + break; + case 0xAE: + parseOpenRuleViolation(msg); + break; + case 0xAF: + parseRuleViolationAF(msg); + break; + case 0xB0: + parseRuleViolationB0(msg); + break; + case 0xB1: + parseRuleViolationB1(msg); + break; + case 0xB2: + parseCreatePrivateChannel(msg); + break; + case 0xB3: + parseClosePrivateChannel(msg); + break; + case 0xB4: + parseTextMessage(msg); + break; + case 0xB5: + parseCancelWalk(msg); + break; + case 0xBE: + parseFloorChangeUp(msg); + break; + case 0xBF: + parseFloorChangeDown(msg); + break; + case 0xC8: + parseOutfitWindow(msg); + break; + case 0xD2: + parseVipState(msg); + break; + case 0xD3: + parseVipLogin(msg); + break; + case 0xD4: + parseVipLogout(msg); + break; + case 0xDC: + parseShowTutorial(msg); + break; + case 0xDD: + parseAddMarker(msg); + break; + case 0xF0: + parseQuestList(msg); + break; + case 0xF1: + parseQuestPartList(msg); + break; + default: + logDebug("UNKNOWN PACKET BYTE.", opt); + //skipPacket = true; + break; + } + } + + recv(); +} + +void ProtocolGame::parsePlayerLogin(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->setId(msg.getU32()); + player->setDrawSpeed(msg.getU16()); + player->setCanReportBugs(msg.getU8()); + + g_game.setOnline(true); +} + +void ProtocolGame::parseGMActions(InputMessage& msg) +{ + for(uint8 i = 0; i < 28; ++i) + msg.getU8(); +} + +void ProtocolGame::parseErrorMessage(InputMessage& msg) +{ + std::string error = msg.getString(); + callLuaField("onError", error); +} + +void ProtocolGame::parseFYIMessage(InputMessage& msg) +{ + msg.getString(); // message +} + +void ProtocolGame::parseWaitList(InputMessage& msg) +{ + msg.getString(); // message + msg.getU8();// + 1 // time +} + +void ProtocolGame::parsePing(InputMessage&) +{ + sendPing(); +} + +void ProtocolGame::parseDeath(InputMessage&) +{ + +} + +void ProtocolGame::parseCanReportBugs(InputMessage& msg) +{ + msg.getU8(); // report bugs +} + +void ProtocolGame::parseMapDescription(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->setPosition(parsePosition(msg)); + setMapDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, player->getPosition()->z, 18, 14); +} + +void ProtocolGame::parseMoveNorth(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->y--; + setMapDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, player->getPosition()->z, 18, 1); +} + +void ProtocolGame::parseMoveEast(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->x++; + setMapDescription(msg, player->getPosition()->x + 9, player->getPosition()->y - 6, player->getPosition()->z, 1, 14); +} + +void ProtocolGame::parseMoveSouth(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->y++; + setMapDescription(msg, player->getPosition()->x - 8, player->getPosition()->y + 7, player->getPosition()->z, 18, 1); +} + +void ProtocolGame::parseMoveWest(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->x--; + setMapDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, player->getPosition()->z, 1, 14); +} + +void ProtocolGame::parseUpdateTile(InputMessage& msg) +{ + Position tilePos = parsePosition(msg); + uint16 thingId = msg.getU16(true); + if(thingId == 0xFF01) { + msg.getU16(); + } + else { + setTileDescription(msg, tilePos); + msg.getU16(); + } +} + +void ProtocolGame::parseTileAddThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos + internalGetThing(msg); +} + +void ProtocolGame::parseTileTransformThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos + + uint16 thingId = msg.getU16(); + if(thingId == 0x0061 || thingId == 0x0062 || thingId == 0x0063) { + msg.getU32(); // creatureId + msg.getU8(); // direction + } + else { + internalGetItem(msg, thingId); + } +} + +void ProtocolGame::parseTileRemoveThing(InputMessage& msg) +{ + parsePosition(msg); // tilePos + msg.getU8(); // stackPos +} + +void ProtocolGame::parseCreatureMove(InputMessage& msg) +{ + parsePosition(msg); // oldPos + msg.getU8(); // oldStackPos + parsePosition(msg); // newPos +} + +void ProtocolGame::parseOpenContainer(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU16(); // itemid + msg.getString(); // name + msg.getU8(); // capacity + msg.getU8(); // hasParent + uint8 size = msg.getU8(); // size + + for(uint32 i = 0; i < size; i++) + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseCloseContainer(InputMessage& msg) +{ + msg.getU8(); // cid +} + +void ProtocolGame::parseContainerAddItem(InputMessage& msg) +{ + msg.getU8(); // cid + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseContainerUpdateItem(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU8(); // slot + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseContainerRemoveItem(InputMessage& msg) +{ + msg.getU8(); // cid + msg.getU8(); // slot +} + +void ProtocolGame::parseAddInventoryItem(InputMessage& msg) +{ + msg.getU8(); // slot + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseRemoveInventoryItem(InputMessage& msg) +{ + msg.getU8(); // slot +} + +void ProtocolGame::parseOpenShopWindow(InputMessage& msg) +{ + uint8 listCount = msg.getU8(); + for(uint8 i = 0; i < listCount; ++i) { + msg.getU16(); // item id + msg.getU8(); // runecharges + msg.getString(); // item name + msg.getU32(); // weight + msg.getU32(); // buy price + msg.getU32(); // sell price + } +} + +void ProtocolGame::parsePlayerCash(InputMessage& msg) +{ + msg.getU32(); + uint8 size = msg.getU8(); + + for(int i = 0; i < size; i++) { + msg.getU16(); // itemid + msg.getU8(); // runecharges + } +} + +void ProtocolGame::parseCloseShopWindow(InputMessage&) +{ +} + +void ProtocolGame::parseSafeTradeRequest(InputMessage& msg) +{ + msg.getString(); // name + uint8 count = msg.getU8(); + + for(uint8 i = 0; i < count; i++) + internalGetItem(msg, 0xFFFF); +} + +void ProtocolGame::parseSafeTradeClose(InputMessage&) +{ +} + +void ProtocolGame::parseWorldLight(InputMessage& msg) +{ + msg.getU8(); // level + msg.getU8(); // color +} + +void ProtocolGame::parseMagicEffect(InputMessage& msg) +{ + EffectPtr effect = EffectPtr(new Effect()); + effect->setPosition(parsePosition(msg)); + effect->setId(msg.getU8()); + + g_game.getMap()->addThing(effect); +} + +void ProtocolGame::parseAnimatedText(InputMessage& msg) +{ + parsePosition(msg); // textPos + msg.getU8(); // color + msg.getString(); // text +} + +void ProtocolGame::parseDistanceShot(InputMessage& msg) +{ + parsePosition(msg); // fromPos + parsePosition(msg); // toPos + msg.getU8(); // effect +} + +void ProtocolGame::parseCreatureSquare(InputMessage& msg) +{ + msg.getU32(); // creatureId + msg.getU8(); // color +} + +void ProtocolGame::parseCreatureHealth(InputMessage& msg) +{ + msg.getU32(); // creatureId + msg.getU8(); // percent +} + +void ProtocolGame::parseCreatureLight(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // level + msg.getU8(); // color +} + +void ProtocolGame::parseCreatureOutfit(InputMessage& msg) +{ + msg.getU32(); // creature id + internalCreatureOutfit(msg); +} + +void ProtocolGame::parseCreatureSpeed(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU16(); // speed +} + +void ProtocolGame::parseCreatureSkulls(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // skull +} + +void ProtocolGame::parseCreatureShields(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // shield +} + +void ProtocolGame::parseCreatureTurn(InputMessage& msg) +{ + msg.getU32(); // creature id + msg.getU8(); // direction +} + +void ProtocolGame::parseItemTextWindow(InputMessage& msg) +{ + msg.getU32(); // windowId + msg.getU16(); // itemid + msg.getU16(); // max length + msg.getString(); // text + msg.getString(); // writter + msg.getString(); // date +} + +void ProtocolGame::parseHouseTextWindow(InputMessage& msg) +{ + msg.getU8(); // unknown + msg.getU32(); // windowId + msg.getString(); // text +} + +void ProtocolGame::parsePlayerStats(InputMessage& msg) +{ + msg.getU16(); // health + msg.getU16(); // max health + msg.getU32(); // free capacity + msg.getU32(); // experience + msg.getU16(); // level + msg.getU8(); // level percent + msg.getU16(); // mana + msg.getU16(); // max mana + msg.getU8(); // magic level + msg.getU8(); // magic level percent + msg.getU8(); // soul + msg.getU16(); // stamina +} + +void ProtocolGame::parsePlayerSkills(InputMessage& msg) +{ + msg.getU8(); // fist skill + msg.getU8(); // fist percent + + msg.getU8(); // club skill + msg.getU8(); // club percent + + msg.getU8(); // sword skill + msg.getU8(); // sword percent + + msg.getU8(); // axe skill + msg.getU8(); // axe percent + + msg.getU8(); // distance skill + msg.getU8(); // distance percent + + msg.getU8(); // shielding skill + msg.getU8(); // shielding percent + + msg.getU8(); // fishing skill + msg.getU8(); // fishing percent +} + +void ProtocolGame::parsePlayerIcons(InputMessage& msg) +{ + msg.getU16(); // icons +} + +void ProtocolGame::parsePlayerCancelAttack(InputMessage& msg) +{ + msg.getU32(); +} + +void ProtocolGame::parseCreatureSpeak(InputMessage& msg) +{ + msg.getU32(); // unkSpeak + msg.getString(); // name + msg.getU16(); // level + uint8 type = msg.getU8(); + + switch(type) { + case SPEAK_SAY: + case SPEAK_WHISPER: + case SPEAK_YELL: + case SPEAK_MONSTER_SAY: + case SPEAK_MONSTER_YELL: + case SPEAK_PRIVATE_NP: + parsePosition(msg); // creaturePos + break; + case SPEAK_CHANNEL_R1: + case SPEAK_CHANNEL_R2: + case SPEAK_CHANNEL_O: + case SPEAK_CHANNEL_Y: + case SPEAK_CHANNEL_W: + msg.getU16(); // channelId + break; + case SPEAK_RVR_CHANNEL: + msg.getU32(); // time + break; + default: + //qDebug() << "Unknown speak type. opt: 0xAA, type: " << type; + break; + } + + msg.getString(); // message +} + +void ProtocolGame::parseChannelList(InputMessage& msg) +{ + uint8 count = msg.getU8(); + for(uint8 i = 0; i < count; i++) { + msg.getU16(); + msg.getString(); + } +} + +void ProtocolGame::parseOpenChannel(InputMessage& msg) +{ + msg.getU16(); // channelId + msg.getString(); // name +} + +void ProtocolGame::parseOpenPrivatePlayerChat(InputMessage& msg) +{ + msg.getString(); // name +} + +void ProtocolGame::parseOpenRuleViolation(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationAF(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationB0(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseRuleViolationB1(InputMessage& msg) +{ + msg.getU16(); // a +} + +void ProtocolGame::parseCreatePrivateChannel(InputMessage& msg) +{ + msg.getU16(); // channel id + msg.getString(); // channel name +} + +void ProtocolGame::parseClosePrivateChannel(InputMessage& msg) +{ + msg.getU16(); // channel id +} + +void ProtocolGame::parseTextMessage(InputMessage& msg) +{ + msg.getU8(); // messageType + std::string message = msg.getString(); + logDebug(message); +} + +void ProtocolGame::parseCancelWalk(InputMessage& msg) +{ + msg.getU8(); // direction +} + +void ProtocolGame::parseFloorChangeUp(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->z--; + + int32 skip = 0; + if(player->getPosition()->z == 7) + for(int32 i = 5; i >= 0; i--) + setFloorDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, i, 18, 14, 8 - i, &skip); + else if(player->getPosition()->z > 7) + setFloorDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, player->getPosition()->z - 2, 18, 14, 3, &skip); + + player->getPosition()->x++; + player->getPosition()->y++; +} + +void ProtocolGame::parseFloorChangeDown(InputMessage& msg) +{ + Player *player = g_game.getPlayer(); + player->getPosition()->z++; + + int32 skip = 0; + if(player->getPosition()->z == 8) { + int32 j, i; + for(i = player->getPosition()->z, j = -1; i < (int32)player->getPosition()->z + 3; ++i, --j) + setFloorDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, i, 18, 14, j, &skip); + } + else if(player->getPosition()->z > 8 && player->getPosition()->z < 14) + setFloorDescription(msg, player->getPosition()->x - 8, player->getPosition()->y - 6, player->getPosition()->z + 2, 18, 14, -3, &skip); + + player->getPosition()->x--; + player->getPosition()->y--; +} + +void ProtocolGame::parseOutfitWindow(InputMessage& msg) +{ + internalCreatureOutfit(msg); + uint8 outfitCount = msg.getU8(); + + for(int i = 0; i < outfitCount; i++) { + msg.getU16(); // outfit id + msg.getString(); // outfit name + msg.getU8(); // addons + } +} + +void ProtocolGame::parseVipState(InputMessage& msg) +{ + msg.getU32(); // player id + msg.getString(); // player name + msg.getU8(); // online +} + +void ProtocolGame::parseVipLogin(InputMessage& msg) +{ + msg.getU32(); // player id +} + +void ProtocolGame::parseVipLogout(InputMessage& msg) +{ + msg.getU32(); // player id +} + +void ProtocolGame::parseShowTutorial(InputMessage& msg) +{ + msg.getU8(); // tutorial id +} + +void ProtocolGame::parseAddMarker(InputMessage& msg) +{ + parsePosition(msg); // position + msg.getU8(); // icon + msg.getString(); // message +} + +void ProtocolGame::parseQuestList(InputMessage& msg) +{ + uint16 questsCount = msg.getU16(); + for(uint16 i = 0; i < questsCount; i++) { + msg.getU16(); // quest id + msg.getString(); // quest name + msg.getU8(); // quest state + } +} + +void ProtocolGame::parseQuestPartList(InputMessage& msg) +{ + msg.getU16(); // quest id + uint8 missionCount = msg.getU8(); + for(uint8 i = 0; i < missionCount; i++) { + msg.getString(); // quest name + msg.getString(); // quest description + } +} + +void ProtocolGame::setMapDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height) +{ + int startz, endz, zstep, skip = 0; + + if(z > 7) { + startz = z - 2; + endz = (15 < z+2) ? 15 : z+2; + zstep = 1; + } + else { + startz = 7; + endz = 0; + zstep = -1; + } + + logDebug((int)startz); + + for(int nz = startz; nz != endz + zstep; nz += zstep) + setFloorDescription(msg, x, y, nz, width, height, z - nz, &skip); +} + +void ProtocolGame::setFloorDescription(InputMessage& msg, int32 x, int32 y, int32 z, int32 width, int32 height, int32 offset, int32* skipTiles) +{ + int32 skip = *skipTiles; + + for(int32 nx = 0; nx < width; nx++) { + for(int32 ny = 0; ny < height; ny++) { + if(skip == 0) { + uint16 tileOpt = msg.getU16(true); + if(tileOpt >= 0xFF00) + skip = (msg.getU16() & 0xFF); + else { + Position pos(x + nx + offset, y + ny + offset, z); + setTileDescription(msg, pos); + skip = (msg.getU16() & 0xFF); + } + } + else + skip--; + } + } + *skipTiles = skip; +} + +void ProtocolGame::setTileDescription(InputMessage& msg, Position position) +{ + int stackpos = 0; + while(1){ + stackpos++; + uint16 inspectTileId = msg.getU16(true); + if(inspectTileId >= 0xFF00) + return; + else { + if(stackpos > 10) { + logDebug("[ProtocolGame::setTileDescription] Too many things!."); + return; + } + + ThingPtr thing = internalGetThing(msg); + if(thing) + thing->setPosition(position); + g_game.getMap()->addThing(thing, stackpos); + } + } +} + +ThingPtr ProtocolGame::internalGetThing(InputMessage& msg) +{ + ThingPtr thing; + + uint16 thingId = msg.getU16(); + if(thingId == 0x0061 || thingId == 0x0062) { // add new creature + CreaturePtr creature = CreaturePtr(new Creature); + + if(thingId == 0x0062) { //creature is known + creature->setId(msg.getU32()); + } + else if(thingId == 0x0061) { //creature is not known + msg.getU32(); // remove id + creature->setId(msg.getU32()); + creature->setName(msg.getString()); + } + + creature->setHealthPercent(msg.getU8()); + creature->setDirection((Direction)msg.getU8()); + creature->setOutfit(internalCreatureOutfit(msg)); + msg.getU8(); // light level + msg.getU8(); // light color + msg.getU16(); // speed + msg.getU8(); // skull + msg.getU8(); // shield + + if(thingId == 0x0061) // emblem is sent only in packet type 0x61 + msg.getU8(); + msg.getU8(); // impassable + + thing = creature; + + } + else if(thingId == 0x0063) { // creature turn + parseCreatureTurn(msg); + } + else // item + thing = internalGetItem(msg, thingId); + + return thing; +} + +Outfit ProtocolGame::internalCreatureOutfit(InputMessage& msg) +{ + Outfit outfit; + + outfit.type = msg.getU16(); // looktype + if(outfit.type != 0) { + outfit.head = msg.getU8(); + outfit.body = msg.getU8(); + outfit.legs = msg.getU8(); + outfit.feet = msg.getU8(); + outfit.addons = msg.getU8(); + } + else { + outfit.type = msg.getU16(); + } + + return outfit; +} + +ItemPtr ProtocolGame::internalGetItem(InputMessage& msg, uint16 id) +{ + ItemPtr item = ItemPtr(new Item()); + + if(id == 0xFFFF) + id = msg.getU16(); + item->setId(id); + + ThingAttributes *itemAttributes = g_tibiaDat.getItemAttributes(id); + if(itemAttributes->stackable || itemAttributes->group == THING_GROUP_FLUID || itemAttributes->group == THING_GROUP_SPLASH) + item->setCount(msg.getU8()); + + return item; +} + +Position ProtocolGame::parsePosition(InputMessage& msg) +{ + uint16 x = msg.getU16(); + uint16 y = msg.getU16(); + uint8 z = msg.getU8(); + + return Position(x, y, z); +} diff --git a/src/otclient/net/protocolgamesend.cpp b/src/otclient/net/protocolgamesend.cpp new file mode 100644 index 00000000..696f794c --- /dev/null +++ b/src/otclient/net/protocolgamesend.cpp @@ -0,0 +1,44 @@ +#include "protocolgame.h" + +void ProtocolGame::sendLogout() +{ + OutputMessage oMsg; + oMsg.addU8(0x14); + send(oMsg); +} + +void ProtocolGame::sendPing() +{ + OutputMessage oMsg; + oMsg.addU8(0x1E); + send(oMsg); +} + +void ProtocolGame::sendWalkNorth() +{ + OutputMessage oMsg; + oMsg.addU8(0x65); + send(oMsg); +} + +void ProtocolGame::sendWalkEast() +{ + OutputMessage oMsg; + oMsg.addU8(0x66); + send(oMsg); +} + +void ProtocolGame::sendWalkSouth() +{ + OutputMessage oMsg; + oMsg.addU8(0x67); + send(oMsg); +} + +void ProtocolGame::sendWalkWest() +{ + OutputMessage oMsg; + oMsg.addU8(0x68); + send(oMsg); +} + diff --git a/src/otclient/net/protocollogin.cpp b/src/otclient/net/protocollogin.cpp new file mode 100644 index 00000000..30465732 --- /dev/null +++ b/src/otclient/net/protocollogin.cpp @@ -0,0 +1,153 @@ +#include "protocollogin.h" +#include +#include +#include +#include + +// TODO just testing +#include "protocolgame.h" + +ProtocolLogin::ProtocolLogin() +{ + +} + +ProtocolLogin::~ProtocolLogin() +{ + +} + +void ProtocolLogin::login(const std::string& accountName, const std::string& accountPassword) +{ + if(accountName.empty() || accountPassword.empty()) { + callLuaField("onError", "You must enter an account name and password."); + return; + } + + m_accountName = accountName; + m_accountPassword = accountPassword; + + /*static const char hosts[][32] = { + "login01.tibia.com", + "login02.tibia.com", + "login03.tibia.com", + "login04.tibia.com", + "login05.tibia.com", + "tibia01.cipsoft.com", + "tibia02.cipsoft.com", + "tibia03.cipsoft.com", + "tibia04.cipsoft.com", + "tibia05.cipsoft.com" + }; + + std::string host = hosts[rand() % 10];*/ + std::string host = "sv3.radbr.com"; + uint16 port = 7171; + + connect(host, port); +} + +void ProtocolLogin::onConnect() +{ + sendLoginPacket(); +} + +void ProtocolLogin::sendLoginPacket() +{ + OutputMessage oMsg; + + oMsg.addU8(0x01); // Protocol id + oMsg.addU16(0x02); // OS + oMsg.addU16(862); // Client version + + oMsg.addU32(0x4E12DAFF); // Data Signature + oMsg.addU32(0x4E12DB27); // Sprite Signature + oMsg.addU32(0x4E119CBF); // Picture Signature + + oMsg.addU8(0); // First RSA byte must be 0x00 // 1 + + // Generete xtea key. + m_xteaKey[0] = 432324; + m_xteaKey[1] = 24324; + m_xteaKey[2] = 423432; + m_xteaKey[3] = 234324; + + // Add xtea key + oMsg.addU32(m_xteaKey[0]); // 5 + oMsg.addU32(m_xteaKey[1]); // 9 + oMsg.addU32(m_xteaKey[2]); // 13 + oMsg.addU32(m_xteaKey[3]); // 17 + + oMsg.addString(m_accountName); // Account Name // 19 + oMsg.addString(m_accountPassword); // Password // 21 + + // Packet data must have since byte 0, start, 128 bytes + oMsg.addPaddingBytes(128 - (21 + m_accountName.length() + m_accountPassword.length())); + + // Encrypt msg with RSA + if(!Rsa::encrypt((char*)oMsg.getBuffer() + 6 + oMsg.getMessageSize() - 128, 128, OTSERV_PUBLIC_RSA)) + return; + + send(oMsg); + + m_xteaEncryptionEnabled = true; + + recv(); +} + +void ProtocolLogin::onRecv(InputMessage& inputMessage) +{ + while(!inputMessage.end()) { + uint8 opt = inputMessage.getU8(); + logDebug("opt:",(uint)opt); + switch(opt) { + case 0x0A: + parseError(inputMessage); + break; + case 0x14: + parseMOTD(inputMessage); + break; + case 0x1e: + callLuaField("onError", "Client needs update."); + break; + case 0x64: + parseCharacterList(inputMessage); + break; + } + } +} + +void ProtocolLogin::parseError(InputMessage& inputMessage) +{ + std::string error = inputMessage.getString(); + logDebug(error); + callLuaField("onError", error); +} + +void ProtocolLogin::parseMOTD(InputMessage& inputMessage) +{ + std::string motd = inputMessage.getString(); + logDebug(motd); + callLuaField("onMotd", motd); +} + +void ProtocolLogin::parseCharacterList(InputMessage& inputMessage) +{ + uint8 characters = inputMessage.getU8(); + for(int i = 0; i < characters; ++i) { + std::string name = inputMessage.getString(); + std::string world = inputMessage.getString(); + uint32 ip = inputMessage.getU32(); + uint16 port = inputMessage.getU16(); + + logDebug("character: ", name.c_str(), world.c_str(), ip, " ", port); + + // TODO just test + if(i == 0) { + ProtocolGamePtr protocolGame = ProtocolGamePtr(new ProtocolGame); + protocolGame->login(m_accountName, m_accountPassword, ip, port, name); + } + } + uint16 premiumDays = inputMessage.getU16(); + logDebug("prem days: ", premiumDays); +} diff --git a/src/otclient/net/protocollogin.h b/src/otclient/net/protocollogin.h new file mode 100644 index 00000000..45548420 --- /dev/null +++ b/src/otclient/net/protocollogin.h @@ -0,0 +1,39 @@ +#ifndef PROTOCOLLOGIN_H +#define PROTOCOLLOGIN_H + +#include "declarations.h" +#include + +class ProtocolLogin; +typedef std::shared_ptr ProtocolLoginPtr; + +class ProtocolLogin : public Protocol +{ +public: + ProtocolLogin(); + ~ProtocolLogin(); + + static ProtocolLoginPtr create() { return ProtocolLoginPtr(new ProtocolLogin); } + + void login(const std::string& accountName, const std::string& accountPassword); + + void onConnect(); + void onRecv(InputMessage& inputMessage); + + void cancel() { /* TODO: this func */ } + + ProtocolLoginPtr asProtocolLogin() { return std::static_pointer_cast(shared_from_this()); } + virtual const char* getLuaTypeName() const { return "ProtocolLogin"; } + +private: + void sendLoginPacket(); + + void parseError(InputMessage& inputMessage); + void parseMOTD(InputMessage& inputMessage); + void parseCharacterList(InputMessage& inputMessage); + + std::string m_accountName, m_accountPassword; + +}; + +#endif diff --git a/src/otclient/otclient.cpp b/src/otclient/otclient.cpp new file mode 100644 index 00000000..acf0e4d3 --- /dev/null +++ b/src/otclient/otclient.cpp @@ -0,0 +1,278 @@ +#include "otclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define POLL_CYCLE_DELAY 10 + +OTClient g_client; + +void OTClient::init(std::vector args) +{ + m_running = false; + m_stopping = false; + + // print client information + logInfo("OTClient 0.2.0"); + + // initialize platform related stuff + g_platform.init(this, "OTClient"); + + // initialize script environment + g_lua.init(); + + // register otclient lua functions + registerLuaFunctions(); + + // initialize resources + g_resources.init(args[0].c_str()); + + // load configurations + loadConfigurations(); + + // create the client window + g_platform.createWindow(g_configs.get("window x"), g_configs.get("window y"), + g_configs.get("window width"), g_configs.get("window height"), + 550, 450, + g_configs.get("window maximized")); + g_platform.setWindowTitle("OTClient"); + + // initialize graphics + g_graphics.init(); + + // initialize event dispatcher + g_dispatcher.init(); + + // initialize network + //g_network.init(); + + // initialize the ui + g_ui.init(); + + // discover and load modules + g_modules.discoverAndLoadModules(); + + // now that everything is initialized, setup configurations + setupConfigurations(); + + // now that everything is loaded, show the window + g_platform.showWindow(); +} + +void OTClient::run() +{ + std::string fpsText; + Size fpsTextSize; + FontPtr defaultFont = g_fonts.getDefaultFont(); + int frameTicks = g_platform.getTicks(); + int lastFpsTicks = frameTicks; + int lastPollTicks = frameTicks; + int frameCount = 0; + + m_stopping = false; + m_running = true; + + if(g_ui.getRootWidget()->getChildCount() == 0) { + logError("ERROR: there is no root widgets to display, the app will close"); + m_stopping = true; + } + + // run the first poll + poll(); + + while(!m_stopping) { + frameTicks = g_platform.getTicks(); + + // calculate fps + frameCount++; + if(frameTicks - lastFpsTicks >= 1000) { + fpsText = fw::mkstr("FPS: ", frameCount); + fpsTextSize = defaultFont->calculateTextRectSize(fpsText); + frameCount = 0; + lastFpsTicks = frameTicks; + } + + // poll events every POLL_CYCLE_DELAY + // this delay exists to avoid massive polling thus increasing framerate + if(frameTicks - lastPollTicks >= POLL_CYCLE_DELAY) { + poll(); + lastPollTicks = frameTicks; + } + + // only render when the windows is visible + if(g_platform.isWindowVisible()) { + // render begin + g_graphics.beginRender(); + + // render everything + render(); + + // render fps + defaultFont->renderText(fpsText, Point(g_graphics.getScreenSize().width() - fpsTextSize.width() - 10, 10)); + + // render end + g_graphics.endRender(); + + // swap the old buffer with the new rendered + g_platform.swapBuffers(); + } else { + // sleeps until next poll to avoid massive cpu usage + g_platform.sleep(POLL_CYCLE_DELAY+1); + } + } + + m_stopping = false; + m_running = false; +} + +void OTClient::terminate() +{ + // hide the window because there is no render anymore + g_platform.hideWindow(); + + // run modules unload event + g_modules.unloadModules(); + + // terminate ui + g_ui.terminate(); + + // release remaining lua object references + g_lua.collectGarbage(); + + // poll remaining events + poll(); + + // terminate network + //g_network.terminate(); + + // terminate dispatcher + g_dispatcher.terminate(); + + // terminate graphics + g_graphics.terminate(); + + // save configurations + saveConfigurations(); + + // release resources + g_resources.terminate(); + + // terminate script environment + g_lua.terminate(); + + // end platform related stuff + g_platform.terminate(); + +} + +void OTClient::exit() +{ + // stops the main loop + m_stopping = true; +} + +void OTClient::poll() +{ + // poll platform events + g_platform.poll(); + + // poll network events + //g_network.poll(); + Connection::poll(); + + // poll dispatcher events + g_dispatcher.poll(); +} + +void OTClient::render() +{ + if(g_game.getOnline()) + g_game.getMap()->draw(0, 0); + + // everything is rendered by UI components + g_ui.render(); +} + +void OTClient::loadConfigurations() +{ + // default window size + int defWidth = 550; + int defHeight = 450; + + // sets default window configuration + g_configs.set("window x", (g_platform.getDisplayWidth() - defWidth)/2); + g_configs.set("window y", (g_platform.getDisplayHeight() - defHeight)/2); + g_configs.set("window width", defWidth); + g_configs.set("window height", defHeight); + g_configs.set("window maximized", false); + g_configs.set("vsync", true); + + // loads user configuration + if(!g_configs.load("config.otml")) + logInfo("Using default configurations."); +} + +void OTClient::setupConfigurations() +{ + // activate vertical synchronization? + g_platform.setVerticalSync(g_configs.get("vsync")); +} + +void OTClient::saveConfigurations() +{ + g_configs.set("window x", g_platform.getWindowX()); + g_configs.set("window y", g_platform.getWindowY()); + g_configs.set("window width", g_platform.getWindowWidth()); + g_configs.set("window height", g_platform.getWindowHeight()); + g_configs.set("window maximized", g_platform.isWindowMaximized()); + + // saves user configuration + if(!g_configs.save()) + logError("ERROR: configurations are lost because it couldn't be saved"); +} + +void OTClient::onClose() +{ + if(m_onCloseCallback) + m_onCloseCallback(); + else + exit(); +} + +void OTClient::onResize(const Size& size) +{ + g_graphics.resize(size); + g_ui.resize(size); +} + +void OTClient::onInputEvent(const InputEvent& event) +{ + g_ui.inputEvent(event); + + ProtocolGamePtr protocol = g_game.getProtocol(); + if(protocol) { + if(event.type == EventKeyDown) { + if(event.keycode == KC_UP) + protocol->sendWalkNorth(); + if(event.keycode == KC_RIGHT) + protocol->sendWalkEast(); + if(event.keycode == KC_DOWN) + protocol->sendWalkSouth(); + if(event.keycode == KC_LEFT) + protocol->sendWalkWest(); + } + } +} diff --git a/src/otclient/otclient.h b/src/otclient/otclient.h new file mode 100644 index 00000000..a511601f --- /dev/null +++ b/src/otclient/otclient.h @@ -0,0 +1,52 @@ +#ifndef OTCLIENT_H +#define OTCLIENT_H + +#include + +class OTClient : public PlatformListener +{ +public: + /// Where everything begins... + void init(std::vector args); + /// Main loop + void run(); + /// Called when the client is terminating + void terminate(); + + /// Stop running + void exit(); + /// Poll platform, dispatcher and network events + void poll(); + /// Render each frame + void render(); + + /// Fired when user tryes to close the window + void onClose(); + /// Fired when user resize the window + void onResize(const Size& size); + /// Fired on an user input event + void onInputEvent(const InputEvent& event); + + bool isRunning() const { return m_running; } + + void setOnClose(const SimpleCallback& onCloseCallback) { m_onCloseCallback = onCloseCallback; } + SimpleCallback getOnClose() const { return m_onCloseCallback; } + +private: + /// Set default configurations and load the user configurations + void loadConfigurations(); + /// Use the loaded configurations to setup other classes + void setupConfigurations(); + void saveConfigurations(); + + void registerLuaFunctions(); + + bool m_running; + bool m_stopping; + + SimpleCallback m_onCloseCallback; +}; + +extern OTClient g_client; + +#endif diff --git a/src/otclient/otclientluafunctions.cpp b/src/otclient/otclientluafunctions.cpp new file mode 100644 index 00000000..81dcfacb --- /dev/null +++ b/src/otclient/otclientluafunctions.cpp @@ -0,0 +1,25 @@ +#include "otclient.h" + +#include +#include +#include +#include +#include + +void OTClient::registerLuaFunctions() +{ + // easy typing _1, _2, ... + using namespace std::placeholders; + + g_lua.bindGlobalFunction("exit", std::bind(&OTClient::exit, &g_client)); + g_lua.bindGlobalFunction("setOnClose", std::bind(&OTClient::setOnClose, &g_client, _1)); + g_lua.bindGlobalFunction("importDat", std::bind(&TibiaDat::load, &g_tibiaDat, _1)); + g_lua.bindGlobalFunction("importSpr", std::bind(&TibiaSpr::load, &g_tibiaSpr, _1)); + + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", &ProtocolLogin::create); + g_lua.bindClassMemberFunction("login", &ProtocolLogin::login); + g_lua.bindClassMemberFunction("cancel", &ProtocolLogin::cancel); + + g_lua.registerClass(); +} diff --git a/src/otclient/util/position.h b/src/otclient/util/position.h new file mode 100644 index 00000000..10969487 --- /dev/null +++ b/src/otclient/util/position.h @@ -0,0 +1,50 @@ +#ifndef POSITION_H +#define POSITION_H + +#include + +enum Direction +{ + DIRECTION_NORTH, + DIRECTION_EAST, + DIRECTION_SOUTH, + DIRECTION_WEST +}; + +class Position +{ +public: + Position() : x(-1), y(-1), z(-1) { } + Position(int x, int y, int z) : x(x), y(y), z(z) { } + + bool isValid() const { return x >= 0 && y >= 0 && z >= 0 && x < 65536 && y < 65536 && z < 15; } + + Position operator+(const Position& other) const { return Position(x + other.x, y + other.y, z + other.z); } + Position& operator+=(const Position& other) { x+=other.x; y+=other.y; z +=other.z; return *this; } + Position operator-(const Position& other) const { return Position(x - other.x, y - other.y, z + other.z); } + Position& operator-=(const Position& other) { x-=other.x; y-=other.y; z-=other.z; return *this; } + + Position& operator=(const Position& other) { x = other.x; y = other.y; z = other.z; return *this; } + bool operator==(const Position& other) const { return other.x == x && other.y == y && other.z == z; } + bool operator!=(const Position& other) const { return other.x!=x || other.y!=y || other.z!=z; } + + int x; + int y; + int z; +}; + +template +std::ostream& operator<<(std::ostream& out, const Position& pos) +{ + out << pos.x << " " << pos.y << " " << pos.z; + return out; +} + +template +std::istream& operator>>(std::istream& in, Position& pos) +{ + in >> pos.x >> pos.y >> pos.z; + return in; +} + +#endif