diff --git a/src/otclient/const.h b/src/otclient/const.h index 69a7b049..826b93b2 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -33,11 +33,21 @@ namespace Otc enum { TILE_PIXELS = 32, - SEA_LEVEL = 7, + MAX_ELEVATION = 24, + + SEA_FLOOR = 7, MAX_Z = 15, + UNDERGROUND_FLOOR = SEA_FLOOR+1, VISIBLE_X_TILES = 15, VISIBLE_Y_TILES = 11, - MAX_ELEVATION = 24, + AWARE_UNDEGROUND_FLOOR_RANGE = 2, + AWARE_X_TILES = VISIBLE_X_TILES + 3, + AWARE_Y_TILES = VISIBLE_Y_TILES + 3, + AWARE_X_LEFT_TILES = AWARE_X_TILES/2 - 1, + AWARE_X_RIGHT_TILES = AWARE_X_TILES/2, + AWARE_Y_TOP_TILES = AWARE_Y_TILES/2 - 1, + AWARE_Y_BOTTOM_TILES = AWARE_Y_TILES/2, + EFFECT_TICKS_PER_FRAME = 75, ITEM_TICKS_PER_FRAME = 500, ANIMATED_TEXT_DURATION = 1000, @@ -46,19 +56,19 @@ namespace Otc MAX_STATIC_TEXT_WIDTH = 200, }; - enum MapDrawFlags { - MapDrawNonMoveableItems = 1, - MapDrawMoveableItems = 2, - MapDrawCreatures = 4, - MapDrawCreaturesInformation = 8, - MapDrawStaticTexts = 16, - MapDrawAnimatedTexts = 32, - MapDrawEffects = 64, - MapDrawMissiles = 128, - MapDrawEverything = MapDrawNonMoveableItems | MapDrawMoveableItems | - MapDrawCreatures | MapDrawCreaturesInformation | - MapDrawStaticTexts | MapDrawAnimatedTexts | - MapDrawEffects | MapDrawMissiles + enum DrawFlags { + DrawGround = 1, + DrawWalls = 2, + DrawCommonItems = 4, + DrawCreatures = 8, + DrawEffects = 16, + DrawMissiles = 32, + DrawCreaturesInformation = 64, + DrawStaticTexts = 128, + DrawAnimatedTexts = 256, + DrawEverything = DrawGround | DrawWalls | DrawCommonItems | + DrawCreatures | DrawEffects | DrawMissiles | + DrawCreaturesInformation | DrawStaticTexts | DrawAnimatedTexts }; enum DatOpts { diff --git a/src/otclient/core/game.cpp b/src/otclient/core/game.cpp index ddc6432e..9c58697f 100644 --- a/src/otclient/core/game.cpp +++ b/src/otclient/core/game.cpp @@ -202,7 +202,7 @@ void Game::processInventoryChange(int slot, const ItemPtr& item) void Game::processCreatureMove(const CreaturePtr& creature, const Position& oldPos, const Position& newPos) { // animate walk - if(oldPos.isInRange(newPos, 1, 1, 0)) + if(oldPos.isInRange(newPos, 1, 1)) creature->walk(oldPos, newPos); } @@ -246,7 +246,7 @@ void Game::walk(Otc::Direction direction) }*/ // only do prewalk to walkable tiles - TilePtr toTile = g_map.getTile(m_localPlayer->getPosition() + Position::getPositionFromDirection(direction)); + TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction)); if(toTile && toTile->isWalkable()) m_localPlayer->preWalk(direction); else diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp index e45f1870..f4eeec1f 100644 --- a/src/otclient/core/item.cpp +++ b/src/otclient/core/item.cpp @@ -73,7 +73,7 @@ void Item::setPosition(const Position& position) Thing::setPosition(position); } -void Item::setData(int data) +void Item::setData(uint8 data) { if(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) { if(data < 5) { diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h index 7635e2f0..0a01f027 100644 --- a/src/otclient/core/item.h +++ b/src/otclient/core/item.h @@ -36,14 +36,14 @@ public: void draw(const Point& dest, float scaleFactor); void setPosition(const Position &position); - void setData(int data); + void setData(uint8 data); - int getData() { return m_data; } + uint8 getData() { return m_data; } ItemPtr asItem() { return std::static_pointer_cast(shared_from_this()); } private: - int m_data; + uint8 m_data; }; #endif diff --git a/src/otclient/core/localplayer.cpp b/src/otclient/core/localplayer.cpp index 9f6336a3..9c98ecc4 100644 --- a/src/otclient/core/localplayer.cpp +++ b/src/otclient/core/localplayer.cpp @@ -70,7 +70,7 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos) void LocalPlayer::preWalk(Otc::Direction direction) { // start walking to direction - Position newPos = m_position + Position::getPositionFromDirection(direction); + Position newPos = m_position.translatedToDirection(direction); m_preWalking = true; m_lastPrewalkDone = false; m_lastPrewalkDestionation = newPos; diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp index 8b72b37e..e23016a6 100644 --- a/src/otclient/core/map.cpp +++ b/src/otclient/core/map.cpp @@ -67,7 +67,13 @@ void Map::load() uint16 id; in.read((char*)&id, sizeof(id)); while(id != 0xFFFF) { - addThing(Item::create(id), pos); + ItemPtr item = Item::create(id); + if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { + uint8 data; + in.read((char*)&data, sizeof(data)); + item->setData(data); + } + addThing(item, pos, 255); in.read((char*)&id, sizeof(id)); } } @@ -80,7 +86,7 @@ void Map::save() for(auto& pair : m_tiles) { Position pos = pair.first; TilePtr tile = pair.second; - if(!tile) + if(!tile || tile->isEmpty()) continue; out.write((char*)&pos, sizeof(pos)); uint16 id; @@ -88,6 +94,10 @@ void Map::save() if(ItemPtr item = thing->asItem()) { id = item->getId(); out.write((char*)&id, sizeof(id)); + if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { + uint8 data = item->getData(); + out.write((char*)&data, sizeof(data)); + } } } id = 0xFFFF; @@ -121,7 +131,7 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) tile->addThing(thing, stackPos); // creature teleported - if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0)) + if(oldPos.isValid() && !oldPos.isInRange(pos,1,1)) g_game.processCreatureTeleport(creature); } else if(MissilePtr missile = thing->asMissile()) { m_floorMissiles[pos.z].push_back(missile); @@ -235,6 +245,36 @@ void Map::removeCreatureById(uint32 id) m_knownCreatures.erase(id); } +void Map::setCentralPosition(const Position& centralPosition) +{ + bool teleported = !m_centralPosition.isInRange(centralPosition, 1,1); + m_centralPosition = centralPosition; + + // remove all creatures when teleporting, the server will resend them again + if(teleported) { + for(const auto& pair : m_knownCreatures) { + const CreaturePtr& creature = pair.second; + const TilePtr& tile = creature->getCurrentTile(); + if(tile) { + tile->removeThing(creature); + creature->setPosition(Position()); + } + } + // remove creatures from tiles that we are not aware anymore + } else { + for(const auto& pair : m_knownCreatures) { + const CreaturePtr& creature = pair.second; + if(!isAwareOfPosition(creature->getPosition())) { + const TilePtr& tile = creature->getCurrentTile(); + if(tile) { + tile->removeThing(creature); + creature->setPosition(Position()); + } + } + } + } +} + std::vector Map::getSpectators(const Position& centerPos, bool multiFloor) { return getSpectatorsInRange(centerPos, multiFloor, (Otc::VISIBLE_X_TILES - 1)/2, (Otc::VISIBLE_Y_TILES - 1)/2); @@ -259,7 +299,7 @@ std::vector Map::getSpectatorsInRangeEx(const Position& centerPos, for(int iz=-minZRange; iz<=maxZRange; ++iz) { for(int iy=-minYRange; iy<=maxYRange; ++iy) { for(int ix=-minXRange; ix<=maxXRange; ++ix) { - TilePtr tile = getTile(centerPos + Position(ix,iy,iz)); + TilePtr tile = getTile(centerPos.translated(ix,iy,iz)); if(!tile) continue; @@ -282,13 +322,14 @@ bool Map::isCovered(const Position& pos, int firstFloor) { // check for tiles on top of the postion Position tilePos = pos; - tilePos.coveredUp(); - while(tilePos.z >= firstFloor) { + while(tilePos.coveredUp() && tilePos.z >= firstFloor) { TilePtr tile = getTile(tilePos); // the below tile is covered when the above tile has a full ground if(tile && tile->isFullGround()) return true; - tilePos.coveredUp(); + + if(tilePos.z == 0) + break; } return false; } @@ -296,8 +337,7 @@ bool Map::isCovered(const Position& pos, int firstFloor) bool Map::isCompletelyCovered(const Position& pos, int firstFloor) { Position tilePos = pos; - tilePos.coveredUp(); - while(tilePos.z >= firstFloor) { + while(tilePos.coveredUp() && tilePos.z >= firstFloor) { bool covered = true; // check in 2x2 range tiles that has no transparent pixels for(int x=0;x<2;++x) { @@ -311,7 +351,40 @@ bool Map::isCompletelyCovered(const Position& pos, int firstFloor) } if(covered) return true; - tilePos.coveredUp(); } return false; } + +bool Map::isAwareOfPosition(const Position& pos) +{ + if(pos.z < getFirstAwareFloor() || pos.z > getLastAwareFloor()) + return false; + + Position groundedPos = pos; + while(groundedPos.z != m_centralPosition.z) { + if(groundedPos.z > m_centralPosition.z) + groundedPos.coveredUp(); + else + groundedPos.coveredDown(); + } + return m_centralPosition.isInRange(groundedPos, Otc::AWARE_X_LEFT_TILES, + Otc::AWARE_X_RIGHT_TILES, + Otc::AWARE_Y_TOP_TILES, + Otc::AWARE_Y_BOTTOM_TILES); +} + +int Map::getFirstAwareFloor() +{ + if(m_centralPosition.z > Otc::SEA_FLOOR) + return m_centralPosition.z-2; + else + return 0; +} + +int Map::getLastAwareFloor() +{ + if(m_centralPosition.z > Otc::SEA_FLOOR) + return std::min(m_centralPosition.z+2, (int)Otc::MAX_Z); + else + return Otc::SEA_FLOOR; +} diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h index e203ac34..c7c369cc 100644 --- a/src/otclient/core/map.h +++ b/src/otclient/core/map.h @@ -58,14 +58,17 @@ public: std::vector getSpectatorsInRangeEx(const Position& centerPos, bool multiFloor, int minXRange, int maxXRange, int minYRange, int maxYRange); void setLight(const Light& light) { m_light = light; } - void setCentralPosition(const Position& centralPosition) { m_centralPosition = centralPosition; } + void setCentralPosition(const Position& centralPosition); bool isLookPossible(const Position& pos); bool isCovered(const Position& pos, int firstFloor = 0); bool isCompletelyCovered(const Position& pos, int firstFloor = 0); + bool isAwareOfPosition(const Position& pos); Light getLight() { return m_light; } Position getCentralPosition() { return m_centralPosition; } + int getFirstAwareFloor(); + int getLastAwareFloor(); std::vector getAnimatedTexts() { return m_animatedTexts; } diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index 6b8e11a1..334f795b 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -45,6 +45,7 @@ MapView::MapView() m_shaderProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader); m_shaderProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/map.frag"); assert(m_shaderProgram->link()); + //m_animated = false; } void MapView::draw(const Rect& rect) @@ -55,10 +56,26 @@ void MapView::draw(const Rect& rect) float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS; Position cameraPosition = getCameraPosition(); - if(updated || m_animated) { + int tileDrawFlags = 0; + if(isNearView()) + tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems | Otc::DrawCreatures | Otc::DrawEffects; + else if(isMidView()) + tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems; + else if(isFarView()) + tileDrawFlags = Otc::DrawGround | Otc::DrawWalls; + else // huge far view + tileDrawFlags = Otc::DrawGround; + + bool animate = m_animated; + + // avoid animation of mid/far views + if(!isNearView()) + animate = false; + + if(updated || animate) { m_framebuffer->bind(); for(const TilePtr& tile : m_cachedVisibleTiles) { - tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor); + tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor, tileDrawFlags); //TODO: restore missiles } m_framebuffer->generateMipmaps(); @@ -128,6 +145,12 @@ void MapView::draw(const Rect& rect) animatedText->draw(p, rect); } } + + // draw a arrow for position the center in non near views + if(!isNearView()) { + g_painter.setColor(Fw::red); + g_painter.drawFilledRect(Rect(rect.center(), 4, 4)); + } } bool MapView::updateVisibleTilesCache() @@ -159,10 +182,10 @@ bool MapView::updateVisibleTilesCache() // loop through / diagonal tiles for(int ix = std::min(diagonal, m_drawDimension.width() - 1), iy = std::max(diagonal - m_drawDimension.width() + 1, 0); ix >= 0 && iy < m_drawDimension.height(); --ix, ++iy) { // position on current floor - Position tilePos(cameraPosition.x + (ix - m_virtualCenterOffset.x), cameraPosition.y + (iy - m_virtualCenterOffset.y), cameraPosition.z); + Position tilePos = cameraPosition.translated(ix - m_virtualCenterOffset.x, iy - m_virtualCenterOffset.y); // adjust tilePos to the wanted floor tilePos.coveredUp(cameraPosition.z - iz); - if(TilePtr tile = g_map.getTile(tilePos)) { + if(const TilePtr& tile = g_map.getTile(tilePos)) { // skip tiles that have nothing if(tile->getThingCount() == 0) continue; @@ -257,28 +280,30 @@ int MapView::getFirstVisibleFloor() if(m_lockedFirstVisibleFloor != -1) return m_lockedFirstVisibleFloor; + Position cameraPosition = getCameraPosition(); + + // avoid rendering multile floors on far views + if(!isNearView()) + return cameraPosition.z; + // if nothing is limiting the view, the first visible floor is 0 int firstFloor = 0; - Position cameraPosition = getCameraPosition(); // limits to underground floors while under sea level - if(cameraPosition.z > Otc::SEA_LEVEL) - firstFloor = Otc::SEA_LEVEL+1; + if(cameraPosition.z > Otc::SEA_FLOOR) + firstFloor = Otc::SEA_FLOOR+1; // loop in 3x3 tiles around the camera for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) { for(int iy = -1; iy <= 1 && firstFloor < cameraPosition.z; ++iy) { - Position pos(cameraPosition.x + ix, cameraPosition.y + iy, cameraPosition.z); + Position pos = cameraPosition.translated(ix, iy); // process tiles that we can look through, e.g. windows, doors if((ix == 0 && iy == 0) || g_map.isLookPossible(pos)) { Position upperPos = pos; Position coveredPos = pos; - coveredPos.coveredUp(); - upperPos.up(); - - while(upperPos.z >= firstFloor) { + while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) { // check tiles physically above TilePtr tile = g_map.getTile(upperPos); if(tile && tile->limitsFloorsView()) { @@ -293,8 +318,8 @@ int MapView::getFirstVisibleFloor() break; } - coveredPos.coveredUp(); - upperPos.up(); + if(upperPos.z == 0) + break; } } } @@ -307,11 +332,15 @@ int MapView::getLastVisibleFloor() { Position cameraPosition = getCameraPosition(); + // avoid rendering multile floors on far views + if(!isNearView()) + return cameraPosition.z; + // view only underground floors when below sea level - if(cameraPosition.z > Otc::SEA_LEVEL) + if(cameraPosition.z > Otc::SEA_FLOOR) return Otc::MAX_Z; else - return Otc::SEA_LEVEL; + return Otc::SEA_FLOOR; } Position MapView::getCameraPosition() diff --git a/src/otclient/core/mapview.h b/src/otclient/core/mapview.h index 8c493c08..1d9ce297 100644 --- a/src/otclient/core/mapview.h +++ b/src/otclient/core/mapview.h @@ -30,12 +30,14 @@ class MapView : public LuaObject { enum { - DEFAULT_FRAMBUFFER_SIZE = 2048 + DEFAULT_FRAMBUFFER_SIZE = 2048, + NEAR_VIEW_AREA = 4096, + MID_VIEW_AREA = 16384, + FAR_VIEW_AREA = 32768 }; public: MapView(); - void draw(const Rect& rect); private: @@ -69,6 +71,10 @@ public: Size getVisibleSize() { return m_visibleDimension * m_tileSize; } CreaturePtr getFollowingCreature() { return m_followingCreature; } + bool isNearView() { return m_drawDimension.area() <= NEAR_VIEW_AREA; } + bool isMidView() { return m_drawDimension.area() > NEAR_VIEW_AREA && m_drawDimension.area() <= MID_VIEW_AREA; } + bool isFarView() { return m_drawDimension.area() > MID_VIEW_AREA && m_drawDimension.area() <= FAR_VIEW_AREA; } + bool isHugeFarView() { return m_drawDimension.area() > FAR_VIEW_AREA; } bool isAnimated() { return m_animated; } Point transformPositionTo2D(const Position& position); diff --git a/src/otclient/core/missile.cpp b/src/otclient/core/missile.cpp index 255161ac..e91020f5 100644 --- a/src/otclient/core/missile.cpp +++ b/src/otclient/core/missile.cpp @@ -35,7 +35,7 @@ Missile::Missile() : Thing() void Missile::draw(const Point& p, const Rect&) { float time = (g_clock.ticks() - m_startTicks) / m_duration; - //internalDraw(p + Point(m_positionDelta.x * time, m_positionDelta.y * time), 0); + //internalDraw(p + Point(m_deltax * time, m_deltay * time), 0); } void Missile::setPath(const Position& fromPosition, const Position& toPosition) @@ -80,11 +80,12 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition) } m_position = fromPosition; - m_positionDelta = toPosition - fromPosition; + m_deltax = toPosition.x - fromPosition.x; + m_deltay = toPosition.y - fromPosition.y; m_startTicks = g_clock.ticks(); - m_duration = 150 * std::sqrt(Point(m_positionDelta.x, m_positionDelta.y).length()); - m_positionDelta.x *= Otc::TILE_PIXELS; - m_positionDelta.y *= Otc::TILE_PIXELS; + m_duration = 150 * std::sqrt(Point(m_deltax, m_deltay).length()); + m_deltax *= Otc::TILE_PIXELS; + m_deltay *= Otc::TILE_PIXELS; // schedule removal auto self = asMissile(); diff --git a/src/otclient/core/missile.h b/src/otclient/core/missile.h index 1cd67e40..6a8395bf 100644 --- a/src/otclient/core/missile.h +++ b/src/otclient/core/missile.h @@ -47,7 +47,8 @@ public: private: ticks_t m_startTicks; - Position m_positionDelta; + int m_deltax; + int m_deltay; float m_duration; }; diff --git a/src/otclient/core/thing.h b/src/otclient/core/thing.h index b7b3c67b..4e8ca33f 100644 --- a/src/otclient/core/thing.h +++ b/src/otclient/core/thing.h @@ -109,7 +109,7 @@ protected: uint32 m_id; Position m_position; - int m_xPattern, m_yPattern, m_zPattern, m_animation; + uint8 m_xPattern, m_yPattern, m_zPattern, m_animation; private: ThingType *m_type; diff --git a/src/otclient/core/tile.cpp b/src/otclient/core/tile.cpp index 4ece2213..95a9fdc9 100644 --- a/src/otclient/core/tile.cpp +++ b/src/otclient/core/tile.cpp @@ -36,63 +36,75 @@ Tile::Tile(const Position& position) m_position = position; } -void Tile::draw(const Point& dest, float scaleFactor) +void Tile::draw(const Point& dest, float scaleFactor, int drawFlags) { m_drawElevation = 0; // first bottom items - for(const ThingPtr& thing : m_things) { - if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom()) - break; - thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor); + if(drawFlags & Otc::DrawGround || drawFlags & Otc::DrawWalls) { + for(const ThingPtr& thing : m_things) { + if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom()) + break; + + if((drawFlags & Otc::DrawGround && thing->isGround()) || (drawFlags & Otc::DrawWalls)) + thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor); - m_drawElevation += thing->getElevation(); - if(m_drawElevation > Otc::MAX_ELEVATION) - m_drawElevation = Otc::MAX_ELEVATION; + m_drawElevation += thing->getElevation(); + if(m_drawElevation > Otc::MAX_ELEVATION) + m_drawElevation = Otc::MAX_ELEVATION; + } } // now common items in reverse order - for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { - const ThingPtr& thing = *it; - if(thing->asCreature() || thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround()) - break; - thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor); - - m_drawElevation += thing->getElevation(); - if(m_drawElevation > Otc::MAX_ELEVATION) - m_drawElevation = Otc::MAX_ELEVATION; + if(drawFlags & Otc::DrawCommonItems) { + for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { + const ThingPtr& thing = *it; + if(thing->asCreature() || thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround()) + break; + thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor); + + m_drawElevation += thing->getElevation(); + if(m_drawElevation > Otc::MAX_ELEVATION) + m_drawElevation = Otc::MAX_ELEVATION; + } } // we can render creatures in 3x3 range - for(int xi = -1; xi <= 1; ++xi) { - for(int yi = -1; yi <= 1; ++yi) { - const TilePtr& tile = g_map.getTile(m_position + Position(xi, yi, 0)); - if(!tile) - continue; - for(const CreaturePtr& creature : tile->getCreatures()) { - int tileSize = Otc::TILE_PIXELS * scaleFactor; - Rect creatureRect(dest.x + xi*tileSize + (creature->getWalkOffset().x - creature->getDisplacementX())*scaleFactor, - dest.y + yi*tileSize + (creature->getWalkOffset().y - creature->getDisplacementY())*scaleFactor, - tileSize, tileSize); - Rect thisTileRect(dest.x, dest.y, tileSize, tileSize); - - // only render creatures where bottom right is inside our rect - if(thisTileRect.contains(creatureRect.bottomRight())) { - creature->draw(Point(dest.x + xi*tileSize - m_drawElevation*scaleFactor, - dest.y + yi*tileSize - m_drawElevation*scaleFactor), scaleFactor); + if(drawFlags & Otc::DrawCreatures) { + for(int xi = -1; xi <= 1; ++xi) { + for(int yi = -1; yi <= 1; ++yi) { + const TilePtr& tile = g_map.getTile(m_position.translated(xi, yi, 0)); + if(!tile) + continue; + for(const CreaturePtr& creature : tile->getCreatures()) { + int tileSize = Otc::TILE_PIXELS * scaleFactor; + Rect creatureRect(dest.x + xi*tileSize + (creature->getWalkOffset().x - creature->getDisplacementX())*scaleFactor, + dest.y + yi*tileSize + (creature->getWalkOffset().y - creature->getDisplacementY())*scaleFactor, + tileSize, tileSize); + Rect thisTileRect(dest.x, dest.y, tileSize, tileSize); + + // only render creatures where bottom right is inside our rect + if(thisTileRect.contains(creatureRect.bottomRight())) { + creature->draw(Point(dest.x + xi*tileSize - m_drawElevation*scaleFactor, + dest.y + yi*tileSize - m_drawElevation*scaleFactor), scaleFactor); + } } } } } // effects - for(const EffectPtr& effect : m_effects) - effect->draw(dest, scaleFactor); + if(drawFlags & Otc::DrawEffects) { + for(const EffectPtr& effect : m_effects) + effect->draw(dest, scaleFactor); + } // top items - for(const ThingPtr& thing : m_things) { - if(thing->isOnTop()) - thing->draw(dest - m_drawElevation, scaleFactor); + if(drawFlags & Otc::DrawWalls) { + for(const ThingPtr& thing : m_things) { + if(thing->isOnTop()) + thing->draw(dest - m_drawElevation, scaleFactor); + } } } diff --git a/src/otclient/core/tile.h b/src/otclient/core/tile.h index 92d9e872..7cd3a4d3 100644 --- a/src/otclient/core/tile.h +++ b/src/otclient/core/tile.h @@ -31,7 +31,7 @@ class Tile : public LuaObject public: Tile(const Position& position); - void draw(const Point& dest, float scaleFactor); + void draw(const Point& dest, float scaleFactor, int drawFlags); private: void updateVisibleItemsCache(); diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index ed05b3c8..a4d464ac 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -317,7 +317,7 @@ void ProtocolGame::parseMapDescription(InputMessage& msg) { Position pos = parsePosition(msg); g_map.setCentralPosition(pos); - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 18, 14); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES); } void ProtocolGame::parseMoveNorth(InputMessage& msg) @@ -325,7 +325,7 @@ void ProtocolGame::parseMoveNorth(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.y--; - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 18, 1); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, Otc::AWARE_X_TILES, 1); g_map.setCentralPosition(pos); } @@ -334,7 +334,7 @@ void ProtocolGame::parseMoveEast(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.x++; - setMapDescription(msg, pos.x + 9, pos.y - 6, pos.z, 1, 14); + setMapDescription(msg, pos.x + Otc::AWARE_X_RIGHT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES); g_map.setCentralPosition(pos); } @@ -343,7 +343,7 @@ void ProtocolGame::parseMoveSouth(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.y++; - setMapDescription(msg, pos.x - 8, pos.y + 7, pos.z, 18, 1); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y + Otc::AWARE_Y_BOTTOM_TILES, pos.z, Otc::AWARE_X_TILES, 1); g_map.setCentralPosition(pos); } @@ -352,7 +352,7 @@ void ProtocolGame::parseMoveWest(InputMessage& msg) Position pos = g_map.getCentralPosition(); pos.x--; - setMapDescription(msg, pos.x - 8, pos.y - 6, pos.z, 1, 14); + setMapDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z, 1, Otc::AWARE_Y_TILES); g_map.setCentralPosition(pos); } @@ -819,11 +819,11 @@ void ProtocolGame::parseFloorChangeUp(InputMessage& msg) pos.z--; int skip = 0; - if(pos.z == 7) - for(int i = 5; i >= 0; i--) - setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, 8 - i, &skip); - else if(pos.z > 7) - setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z - 2, 18, 14, 3, &skip); + if(pos.z == Otc::SEA_FLOOR) + for(int i = Otc::SEA_FLOOR - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; i >= 0; i--) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 8 - i, &skip); + else if(pos.z > Otc::SEA_FLOOR) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, 3, &skip); pos.x++; pos.y++; @@ -836,13 +836,13 @@ void ProtocolGame::parseFloorChangeDown(InputMessage& msg) pos.z++; int skip = 0; - if(pos.z == 8) { + if(pos.z == Otc::UNDERGROUND_FLOOR) { int j, i; - for(i = pos.z, j = -1; i < pos.z + 3; ++i, --j) - setFloorDescription(msg, pos.x - 8, pos.y - 6, i, 18, 14, j, &skip); + for(i = pos.z, j = -1; i <= pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE; ++i, --j) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, i, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, j, &skip); } - else if(pos.z > 8 && pos.z < 14) - setFloorDescription(msg, pos.x - 8, pos.y - 6, pos.z + 2, 18, 14, -3, &skip); + else if(pos.z > Otc::UNDERGROUND_FLOOR && pos.z < Otc::MAX_Z-1) + setFloorDescription(msg, pos.x - Otc::AWARE_X_LEFT_TILES, pos.y - Otc::AWARE_Y_TOP_TILES, pos.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES, -3, &skip); pos.x--; pos.y--; @@ -931,13 +931,13 @@ void ProtocolGame::setMapDescription(InputMessage& msg, int32 x, int32 y, int32 { int startz, endz, zstep, skip = 0; - if(z > 7) { - startz = z - 2; - endz = (15 < z+2) ? 15 : z+2; + if(z > Otc::SEA_FLOOR) { + startz = z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE; + endz = std::min(z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z); zstep = 1; } else { - startz = 7; + startz = Otc::SEA_FLOOR; endz = 0; zstep = -1; } @@ -952,13 +952,17 @@ void ProtocolGame::setFloorDescription(InputMessage& msg, int32 x, int32 y, int3 for(int nx = 0; nx < width; nx++) { for(int ny = 0; ny < height; ny++) { + Position tilePos(x + nx + offset, y + ny + offset, z); + + // clean pre stored tiles + g_map.cleanTile(tilePos); + if(skip == 0) { int tileOpt = msg.getU16(true); if(tileOpt >= 0xFF00) skip = (msg.getU16() & 0xFF); else { - Position pos(x + nx + offset, y + ny + offset, z); - setTileDescription(msg, pos); + setTileDescription(msg, tilePos); skip = (msg.getU16() & 0xFF); } } diff --git a/src/otclient/util/position.h b/src/otclient/util/position.h index cb5a87b0..33b93b19 100644 --- a/src/otclient/util/position.h +++ b/src/otclient/util/position.h @@ -30,51 +30,99 @@ class Position { public: - Position() : x(-1), y(-1), z(-1) { } - Position(int x, int y, int z) : x(x), y(y), z(z) { } + Position() : x(65535), y(65535), z(255) { } + Position(uint16 x, uint16 y, uint8 z) : x(x), y(y), z(z) { } - static Position getPositionFromDirection(Otc::Direction direction) { + Position translatedToDirection(Otc::Direction direction) { + Position pos = *this; switch(direction) { case Otc::North: - return Position( 0, -1, 0); + pos.y--; + break; case Otc::East: - return Position( 1, 0, 0); + pos.x++; + break; case Otc::South: - return Position( 0, 1, 0); + pos.y++; + break; case Otc::West: - return Position(-1, 0, 0); + pos.x--; + break; case Otc::NorthEast: - return Position( 1, -1, 0); + pos.x++; + pos.y--; + break; case Otc::SouthEast: - return Position( 1, 1, 0); + pos.x++; + pos.y++; + break; case Otc::SouthWest: - return Position(-1, 1, 0); + pos.x--; + pos.y++; + break; case Otc::NorthWest: - return Position(-1, -1, 0); - default: - return Position(); + pos.x--; + pos.y--; + break; } + return pos; + } + + Position translatedToReverseDirection(Otc::Direction direction) { + Position pos = *this; + switch(direction) { + case Otc::North: + pos.y++; + break; + case Otc::East: + pos.x--; + break; + case Otc::South: + pos.y--; + break; + case Otc::West: + pos.x++; + break; + case Otc::NorthEast: + pos.x--; + pos.y++; + break; + case Otc::SouthEast: + pos.x--; + pos.y--; + break; + case Otc::SouthWest: + pos.x++; + pos.y--; + break; + case Otc::NorthWest: + pos.x++; + pos.y++; + break; + } + return pos; } Otc::Direction getDirectionFromPosition(const Position& position) const { - Position positionDelta = position - *this; + int dx = position.x - x; + int dy = position.y - y; - if(positionDelta.x == 0 && positionDelta.y == 0) + if(dx == 0 && dy == 0) return Otc::InvalidDirection; - else if(positionDelta.x == 0) { - if(positionDelta.y < 0) + else if(dx == 0) { + if(dy < 0) return Otc::North; - else if(positionDelta.y > 0) + else if(dy > 0) return Otc::South; } - else if(positionDelta.y == 0) { - if(positionDelta.x < 0) + else if(dy == 0) { + if(dx < 0) return Otc::West; - else if(positionDelta.x > 0) + else if(dx > 0) return Otc::East; } else { - float angle = std::atan2(positionDelta.y * -1, positionDelta.x) * RAD_TO_DEC; + float angle = std::atan2(dy * -1, dx) * RAD_TO_DEC; if(angle < 0) angle += 360; @@ -98,7 +146,10 @@ public: return Otc::InvalidDirection; } - bool isValid() const { return x >= 0 && y >= 0 && z >= 0 && x <= 65535 && y <= 65535 && z <= 15; } + bool isValid() const { return !(x == 65535 && y == 65535 && z == 255); } + + void translate(int dx, int dy, short dz = 0) { x += dx; y += dy; z += dz; } + Position translated(int dx, int dy, short dz = 0) const { Position pos = *this; pos.x += dx; pos.y += dy; pos.z += dz; return pos; } 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; } @@ -109,19 +160,50 @@ public: 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; } - bool isInRange(const Position& pos, int xdif, int ydif, int zdif = 1) const { - return std::abs(x-pos.x) <= xdif && std::abs(y-pos.y) <= ydif && std::abs(pos.z-z) <= zdif; + bool isInRange(const Position& pos, int xRange, int yRange) const { return std::abs(x-pos.x) <= xRange && std::abs(y-pos.y) <= yRange; } + bool isInRange(const Position& pos, int minXRange, int maxXRange, int minYRange, int maxYRange) const { + return (pos.x >= x-minXRange && pos.x <= x+maxXRange && pos.y >= y-minYRange && pos.y <= y+maxYRange); + } + + bool up(int n = 1) { + int nz = z-n; + if(nz >= 0 && nz <= Otc::MAX_Z) { + z = nz; + return true; + } + return false; } - void up(int n = 1) { z-=n; } - void down(int n = 1) { z+=n; } + bool down(int n = 1) { + int nz = z+n; + if(nz >= 0 && nz <= Otc::MAX_Z) { + z = nz; + return true; + } + return false; + } - void coveredUp(int n = 1) { x+=n; y+=n; z-=n; } - void coveredDown(int n = 1) { x-=n; y-=n; z+=n; } + bool coveredUp(int n = 1) { + int nx = x+n, ny = y+n, nz = z-n; + if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) { + x = nx; y = ny; z = nz; + return true; + } + return false; + } + + bool coveredDown(int n = 1) { + int nx = x-n, ny = y-n, nz = z+n; + if(nx >= 0 && nx <= 65535 && ny >= 0 && ny <= 65535 && nz >= 0 && nz <= Otc::MAX_Z) { + x = nx; y = ny; z = nz; + return true; + } + return false; + } - int x; - int y; - int z; + uint16 x; + uint16 y; + uint8 z; }; struct PositionHasher : std::unary_function { @@ -132,13 +214,17 @@ struct PositionHasher : std::unary_function { inline std::ostream& operator<<(std::ostream& out, const Position& pos) { - out << pos.x << " " << pos.y << " " << pos.z; + out << (int)pos.x << " " << (int)pos.y << " " << (int)pos.z; return out; } inline std::istream& operator>>(std::istream& in, Position& pos) { - in >> pos.x >> pos.y >> pos.z; + int x, y, z; + in >> x >> y >> z; + pos.x = x; + pos.y = y; + pos.z = z; return in; }