diff --git a/modules/core_widgets/uiitem.lua b/modules/core_widgets/uiitem.lua index a9883839..f6fd2665 100644 --- a/modules/core_widgets/uiitem.lua +++ b/modules/core_widgets/uiitem.lua @@ -30,14 +30,14 @@ function UIItem:onDrop(widget, mousePos) if not widget or not widget.currentDragThing then return true end local pos = self.position - local data = widget.currentDragThing:getCount() - if widget.currentDragThing:isStackable() and data > 1 then + local count = widget.currentDragThing:getCount() + if widget.currentDragThing:isStackable() and count > 1 then widget.parsed = true local moveWindow = displayUI('/game/movewindow.otui') local spinbox = moveWindow:getChildById('spinbox') - spinbox:setMaximum(data) + spinbox:setMaximum(count) spinbox:setMinimum(1) - spinbox:setCurrentIndex(data) + spinbox:setCurrentIndex(count) local okButton = moveWindow:getChildById('buttonOk') okButton.onClick = function() Game.move(widget.currentDragThing, pos, spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end diff --git a/modules/game/map.lua b/modules/game/map.lua index be7d9e64..d676e350 100644 --- a/modules/game/map.lua +++ b/modules/game/map.lua @@ -15,7 +15,7 @@ function UIMap:onDragLeave(widget, mousePos) if not self.parsed then self.currentDragThing = nil end - + restoreCursor() return true end @@ -25,16 +25,16 @@ function UIMap:onDrop(widget, mousePos) local tile = self:getTile(mousePos) if not tile then return false end - - local data = widget.currentDragThing:getCount() + + local count = widget.currentDragThing:getCount() if widget.currentDragThing:isStackable() and data > 1 then widget.parsed = true local moveWindow = displayUI('/game/movewindow.otui') local spinbox = moveWindow:getChildById('spinbox') - spinbox:setMaximum(data) + spinbox:setMaximum(count) spinbox:setMinimum(1) - spinbox:setCurrentIndex(data) - + spinbox:setCurrentIndex(count) + local okButton = moveWindow:getChildById('buttonOk') okButton.onClick = function() Game.move(widget.currentDragThing, tile:getPosition(), spinbox:getCurrentIndex()) okButton:getParent():destroy() widget.currentDragThing = nil end moveWindow.onEnter = okButton.onClick diff --git a/modules/game_shaders/item.frag b/modules/game_shaders/item.frag index 6cb096d3..abc75765 100644 --- a/modules/game_shaders/item.frag +++ b/modules/game_shaders/item.frag @@ -6,6 +6,5 @@ varying vec2 textureCoords; // map texture coords void main() { - vec4 outColor = texture2D(texture, textureCoords) * opacity; - gl_FragColor = outColor; + gl_FragColor = texture2D(texture, textureCoords) * opacity; } diff --git a/src/otclient/const.h b/src/otclient/const.h index 826b93b2..e3879329 100644 --- a/src/otclient/const.h +++ b/src/otclient/const.h @@ -49,6 +49,7 @@ namespace Otc AWARE_Y_BOTTOM_TILES = AWARE_Y_TILES/2, EFFECT_TICKS_PER_FRAME = 75, + INVISIBLE_TICKS_PER_FRAME = 500, ITEM_TICKS_PER_FRAME = 500, ANIMATED_TEXT_DURATION = 1000, STATIC_DURATION_PER_CHARACTER = 75, @@ -66,9 +67,11 @@ namespace Otc DrawCreaturesInformation = 64, DrawStaticTexts = 128, DrawAnimatedTexts = 256, - DrawEverything = DrawGround | DrawWalls | DrawCommonItems | + DrawAnimations = 512, + DrawGroundBorders = 1024, + DrawEverything = DrawGround | DrawGroundBorders | DrawWalls | DrawCommonItems | DrawCreatures | DrawEffects | DrawMissiles | - DrawCreaturesInformation | DrawStaticTexts | DrawAnimatedTexts + DrawCreaturesInformation | DrawStaticTexts | DrawAnimatedTexts | DrawAnimations }; enum DatOpts { diff --git a/src/otclient/core/creature.cpp b/src/otclient/core/creature.cpp index 59c37330..cf9f39cb 100644 --- a/src/otclient/core/creature.cpp +++ b/src/otclient/core/creature.cpp @@ -44,6 +44,7 @@ Creature::Creature() : Thing() m_showVolatileSquare = false; m_showStaticSquare = false; m_direction = Otc::South; + m_walkAnimationPhase = 0; m_walking = false; m_walkInterval = 0; m_walkAnimationInterval = 0; @@ -63,18 +64,20 @@ int LEGS_COLOR_UNIFORM = 12; int FEET_COLOR_UNIFORM = 13; int MASK_TEXTURE_UNIFORM = 14; -void Creature::draw(const Point& dest, float scaleFactor) +void Creature::draw(const Point& dest, float scaleFactor, bool animate) { int scaledTileSize = Otc::TILE_PIXELS * scaleFactor; - if(m_showVolatileSquare) { + Point animationOffset = animate ? m_walkOffset : Point(0,0); + + if(m_showVolatileSquare && animate) { g_painter.setColor(m_volatileSquareColor); - g_painter.drawBoundingRect(Rect(dest + (m_walkOffset - getDisplacement() + 3)*scaleFactor, Size(28*scaleFactor, 28*scaleFactor)), std::max((int)(2*scaleFactor), 1)); + g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 3)*scaleFactor, Size(28*scaleFactor, 28*scaleFactor)), std::max((int)(2*scaleFactor), 1)); } - if(m_showStaticSquare) { + if(m_showStaticSquare && animate) { g_painter.setColor(m_staticSquareColor); - g_painter.drawBoundingRect(Rect(dest + (m_walkOffset - getDisplacement() + 1)*scaleFactor, Size(scaledTileSize, scaledTileSize)), std::max((int)(2*scaleFactor), 1)); + g_painter.drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 1)*scaleFactor, Size(scaledTileSize, scaledTileSize)), std::max((int)(2*scaleFactor), 1)); } g_painter.setColor(Fw::white); @@ -90,12 +93,25 @@ void Creature::draw(const Point& dest, float scaleFactor) outfitProgram->bindUniformLocation(MASK_TEXTURE_UNIFORM, "maskTexture"); } - // Render creature + int xPattern = 0, yPattern = 0, zPattern = 0; + + // outfit is a real creature if(m_outfit.getCategory() == ThingsType::Creature) { - for(m_yPattern = 0; m_yPattern < getNumPatternsY(); m_yPattern++) { + int animationPhase = animate ? m_walkAnimationPhase : 0; + + // xPattern => creature direction + if(m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) + xPattern = Otc::East; + else if(m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) + xPattern = Otc::West; + else + xPattern = m_direction; + + // yPattern => creature addon + for(yPattern = 0; yPattern < getNumPatternsY(); yPattern++) { // continue if we dont have this addon. - if(m_yPattern > 0 && !(m_outfit.getAddons() & (1 << (m_yPattern-1)))) + if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1)))) continue; g_painter.setCustomProgram(outfitProgram); @@ -108,22 +124,23 @@ void Creature::draw(const Point& dest, float scaleFactor) for(int h = 0; h < getDimensionHeight(); h++) { for(int w = 0; w < getDimensionWidth(); w++) { - int spriteId = getSpriteId(w, h, 0, m_xPattern, m_yPattern, m_zPattern, m_animation); + int spriteId = getSpriteId(w, h, 0, xPattern, yPattern, zPattern, m_walkAnimationPhase); if(!spriteId) continue; TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId); if(!spriteTex) continue; + // setup texture outfit mask TexturePtr maskTex; if(getLayers() > 1) { - int maskId = getSpriteId(w, h, 1, m_xPattern, m_yPattern, m_zPattern, m_animation); + int maskId = getSpriteId(w, h, 1, xPattern, yPattern, zPattern, m_walkAnimationPhase); maskTex = g_sprites.getSpriteTexture(maskId); } outfitProgram->setUniformTexture(MASK_TEXTURE_UNIFORM, maskTex, 1); - Rect drawRect(dest.x + (m_walkOffset.x - w*Otc::TILE_PIXELS - getDisplacementX())*scaleFactor, - dest.y + (m_walkOffset.y - h*Otc::TILE_PIXELS - getDisplacementY())*scaleFactor, + Rect drawRect(dest.x + (animationOffset.x - w*Otc::TILE_PIXELS - getDisplacementX())*scaleFactor, + dest.y + (animationOffset.y - h*Otc::TILE_PIXELS - getDisplacementY())*scaleFactor, scaledTileSize, scaledTileSize); g_painter.drawTexturedRect(drawRect, spriteTex); } @@ -131,13 +148,32 @@ void Creature::draw(const Point& dest, float scaleFactor) g_painter.releaseCustomProgram(); } + // outfit is a creature imitating an item or the invisible effect + } else { + int animationPhase = 0; + int animationPhases = getAnimationPhases(); + int animateTicks = Otc::ITEM_TICKS_PER_FRAME; + + // when creature is an effect we cant render the first and last animation phase, + // instead we should loop in the phases between + if(m_outfit.getCategory() == ThingsType::Effect) { + animationPhases = std::max(1, animationPhases-2); + animateTicks = Otc::INVISIBLE_TICKS_PER_FRAME; + } + + if(animationPhases > 1) { + if(animate) + animationPhase = (g_clock.ticks() % (animateTicks * animationPhases)) / animateTicks; + else + animationPhase = animationPhases-1; + } + + if(m_outfit.getCategory() == ThingsType::Effect) + animationPhase = std::min(animationPhase+1, getAnimationPhases()); + + for(int layer = 0; layer < getLayers(); layer++) + internalDraw(dest + animationOffset*scaleFactor, scaleFactor, 0, 0, 0, layer, animationPhase); } - else if(m_outfit.getCategory() == ThingsType::Item) { - for(int l = 0; l < getLayers(); l++) - internalDraw(dest + m_walkOffset, scaleFactor, l); - } - else if(m_outfit.getCategory() == ThingsType::Effect) - internalDraw(dest + m_walkOffset, scaleFactor, 0); } void Creature::drawInformation(const Point& point, bool useGray, const Rect& parentRect) @@ -244,7 +280,7 @@ void Creature::stopWalk() // reset walk animation states m_walkOffset = Point(0,0); - m_animation = 0; + m_walkAnimationPhase = 0; // stops the walk right away terminateWalk(); @@ -257,9 +293,9 @@ void Creature::updateWalkAnimation(int totalPixelsWalked) return; if(totalPixelsWalked == 32 || totalPixelsWalked == 0 || getAnimationPhases() <= 1) - m_animation = 0; + m_walkAnimationPhase = 0; else if(getAnimationPhases() > 1) - m_animation = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1); + m_walkAnimationPhase = 1 + ((totalPixelsWalked * 4) / Otc::TILE_PIXELS) % (getAnimationPhases() - 1); } void Creature::updateWalkOffset(int totalPixelsWalked) @@ -402,40 +438,13 @@ void Creature::setHealthPercent(uint8 healthPercent) void Creature::setDirection(Otc::Direction direction) { - if(m_outfit.getCategory() == ThingsType::Creature) { - if(direction == Otc::NorthEast || direction == Otc::SouthEast) - m_xPattern = Otc::East; - else if(direction == Otc::NorthWest || direction == Otc::SouthWest) - m_xPattern = Otc::West; - else - m_xPattern = direction; - } else { - m_xPattern = 0; - } - m_direction = direction; } void Creature::setOutfit(const Outfit& outfit) { m_outfit = outfit; - updateType(); - m_animation = 0; - - if(m_outfit.getCategory() == ThingsType::Effect) { - updateInvisibleAnimation(); - - m_xPattern = 0; - m_yPattern = 0; - } - if(m_outfit.getCategory() == ThingsType::Item) { - m_xPattern = 0; - m_yPattern = 0; - } - - if(m_outfit.getCategory() == ThingsType::Creature && getLayers() == 1) { - m_outfit.resetClothes(); - } + m_type = g_thingsType.getThingType(outfit.getId(), outfit.getCategory()); } void Creature::setSkull(uint8 skull) @@ -493,26 +502,6 @@ void Creature::addVolatileSquare(uint8 color) }, VOLATILE_SQUARE_DURATION); } -void Creature::updateInvisibleAnimation() -{ - if(!g_game.isOnline() || m_outfit.getCategory() != ThingsType::Effect) - return; - - if(m_animation == 1) - m_animation = 2; - else if(m_animation == 2) - m_animation = 3; - else if(m_animation == 3) - m_animation = 1; - else - m_animation = 1; - - auto self = asCreature(); - g_dispatcher.scheduleEvent([self]() { - self->updateInvisibleAnimation(); - }, INVISIBLE_TICKS); -} - void Creature::updateShield() { m_showShieldTexture = !m_showShieldTexture; diff --git a/src/otclient/core/creature.h b/src/otclient/core/creature.h index e85db78e..1f5a5a86 100644 --- a/src/otclient/core/creature.h +++ b/src/otclient/core/creature.h @@ -34,16 +34,16 @@ class Creature : public Thing public: enum { SHIELD_BLINK_TICKS = 500, - INVISIBLE_TICKS = 500, VOLATILE_SQUARE_DURATION = 1000 }; Creature(); virtual ~Creature() { } - virtual void draw(const Point& dest, float scaleFactor); + virtual void draw(const Point& dest, float scaleFactor, bool animate); void drawInformation(const Point& point, bool useGray, const Rect& parentRect); + void setId(uint32 id) { m_id = id; } void setName(const std::string& name); void setHealthPercent(uint8 healthPercent); void setDirection(Otc::Direction direction); @@ -64,6 +64,7 @@ public: void showStaticSquare(const Color& color) { m_showStaticSquare = true; m_staticSquareColor = color; } void hideStaticSquare() { m_showStaticSquare = false; } + uint32 getId() { return m_id; } std::string getName() { return m_name; } uint8 getHealthPercent() { return m_healthPercent; } Otc::Direction getDirection() { return m_direction; } @@ -96,6 +97,7 @@ protected: virtual void updateWalk(); virtual void terminateWalk(); + uint32 m_id; std::string m_name; Size m_nameSize; uint8 m_healthPercent; @@ -114,6 +116,7 @@ protected: Color m_informationColor; // walk related + int m_walkAnimationPhase; Timer m_walkTimer; TilePtr m_walkingTile; int m_walkInterval; diff --git a/src/otclient/core/effect.cpp b/src/otclient/core/effect.cpp index 77ac24bc..1d1661da 100644 --- a/src/otclient/core/effect.cpp +++ b/src/otclient/core/effect.cpp @@ -27,35 +27,27 @@ #include #include -void Effect::draw(const Point& dest, float scaleFactor) +void Effect::draw(const Point& dest, float scaleFactor, bool animate) { - internalDraw(dest, scaleFactor, 0); + if(m_id == 0) + return; + + int animationPhase = std::min((int)(m_animationTimer.ticksElapsed() / Otc::EFFECT_TICKS_PER_FRAME), getAnimationPhases() - 1); + for(int layer = 0; layer < getLayers(); layer++) + internalDraw(dest, scaleFactor, 0, 0, 0, layer, animate ? animationPhase : 0); } void Effect::startAnimation() { m_animationTimer.restart(); - auto self = asEffect(); - - // schedule next animation update - if(getAnimationPhases() > 1) - g_dispatcher.scheduleEvent([self]() { self->updateAnimation(); }, Otc::EFFECT_TICKS_PER_FRAME); - // schedule removal + auto self = asEffect(); g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, Otc::EFFECT_TICKS_PER_FRAME * getAnimationPhases()); } -void Effect::updateAnimation() +void Effect::setId(uint32 id) { - int animationPhase = m_animationTimer.ticksElapsed() / Otc::EFFECT_TICKS_PER_FRAME; - - if(animationPhase < getAnimationPhases()) - m_animation = animationPhase; - - if(animationPhase < getAnimationPhases() - 1) { - //schedule next animation update - auto self = asEffect(); - g_dispatcher.scheduleEvent([self]() { self->updateAnimation(); }, Otc::EFFECT_TICKS_PER_FRAME); - } + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Effect); } diff --git a/src/otclient/core/effect.h b/src/otclient/core/effect.h index ae2d663e..fb1794b5 100644 --- a/src/otclient/core/effect.h +++ b/src/otclient/core/effect.h @@ -30,15 +30,17 @@ class Effect : public Thing { public: - void draw(const Point& dest, float scaleFactor); + void draw(const Point& dest, float scaleFactor, bool animate); + void setId(uint32 id); void startAnimation(); - void updateAnimation(); + uint32 getId() { return m_id; } EffectPtr asEffect() { return std::static_pointer_cast(shared_from_this()); } private: Timer m_animationTimer; + uint16 m_id; }; #endif diff --git a/src/otclient/core/item.cpp b/src/otclient/core/item.cpp index b9d7e423..687af289 100644 --- a/src/otclient/core/item.cpp +++ b/src/otclient/core/item.cpp @@ -32,7 +32,8 @@ Item::Item() : Thing() { - m_count = 1; + m_id = 0; + m_countOrSubType = 1; } ItemPtr Item::create(int id) @@ -48,11 +49,115 @@ ItemPtr Item::create(int id) PainterShaderProgramPtr itemProgram; -void Item::draw(const Point& dest, float scaleFactor) +void Item::draw(const Point& dest, float scaleFactor, bool animate) { - if(getAnimationPhases() > 1) - m_animation = (g_clock.ticks() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME; + if(m_id == 0) + return; + // determine animation phase + int animationPhase = 0; + if(getAnimationPhases() > 1) { + if(animate) + animationPhase = (g_clock.ticks() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME; + else + animationPhase = getAnimationPhases()-1; + } + + // determine x,y,z patterns + int xPattern = 0, yPattern = 0, zPattern = 0; + if(isGround()) { + xPattern = m_position.x % getNumPatternsX(); + yPattern = m_position.y % getNumPatternsY(); + zPattern = m_position.z % getNumPatternsZ(); + } else if(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) { + if(m_countOrSubType < 5) { + xPattern = m_countOrSubType-1; + yPattern = 0; + } else if(m_countOrSubType < 10) { + xPattern = 0; + yPattern = 1; + } else if(m_countOrSubType < 25) { + xPattern = 1; + yPattern = 1; + } else if(m_countOrSubType < 50) { + xPattern = 2; + yPattern = 1; + } else if(m_countOrSubType <= 100) { + xPattern = 3; + yPattern = 1; + } + } else if(isHangable()) { + if(isHookSouth()) + xPattern = getNumPatternsX() >= 2 ? 1 : 0; + else if(isHookEast()) + xPattern = getNumPatternsX() >= 3 ? 2 : 0; + } else if(isFluid() || isFluidContainer()) { + int color = Otc::FluidTransparent; + switch(m_countOrSubType) { + case Otc::FluidNone: + color = Otc::FluidTransparent; + break; + case Otc::FluidWater: + color = Otc::FluidBlue; + break; + case Otc::FluidMana: + color = Otc::FluidPurple; + break; + case Otc::FluidBeer: + color = Otc::FluidBrown; + break; + case Otc::FluidOil: + color = Otc::FluidBrown; + break; + case Otc::FluidBlood: + color = Otc::FluidRed; + break; + case Otc::FluidSlime: + color = Otc::FluidGreen; + break; + case Otc::FluidMud: + color = Otc::FluidBrown; + break; + case Otc::FluidLemonade: + color = Otc::FluidYellow; + break; + case Otc::FluidMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidWine: + color = Otc::FluidPurple; + break; + case Otc::FluidHealth: + color = Otc::FluidRed; + break; + case Otc::FluidUrine: + color = Otc::FluidYellow; + break; + case Otc::FluidRum: + color = Otc::FluidBrown; + break; + case Otc::FluidFruidJuice: + color = Otc::FluidYellow; + break; + case Otc::FluidCoconutMilk: + color = Otc::FluidWhite; + break; + case Otc::FluidTea: + color = Otc::FluidBrown; + break; + case Otc::FluidMead: + color = Otc::FluidBrown; + break; + default: + color = Otc::FluidTransparent; + break; + } + + xPattern = (color % 4) % getNumPatternsX(); + yPattern = (color / 4) % getNumPatternsY(); + } + + // setup item drawing shader if(!itemProgram) { itemProgram = PainterShaderProgramPtr(new PainterShaderProgram); itemProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader); @@ -61,121 +166,36 @@ void Item::draw(const Point& dest, float scaleFactor) } g_painter.setCustomProgram(itemProgram); + // draw all item layers for(int layer = 0; layer < getLayers(); layer++) - internalDraw(dest, scaleFactor, layer); + internalDraw(dest, scaleFactor, xPattern, yPattern, zPattern, layer, animationPhase); + + // release draw shader g_painter.releaseCustomProgram(); } -void Item::setPosition(const Position& position) +void Item::setId(uint32 id) { - if(isGround()) { - m_xPattern = position.x % getNumPatternsX(); - m_yPattern = position.y % getNumPatternsY(); - m_zPattern = position.z % getNumPatternsZ(); + if(id < g_thingsType.getFirstItemId() || id > g_thingsType.getMaxItemid()) { + logTraceError("invalid item id ", id); + return; } - - Thing::setPosition(position); + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Item); } -void Item::setCount(int count) +int Item::getCount() { - count = std::max(std::min(count, 255), 0); - - if(isStackable() && getNumPatternsX() == 4 && getNumPatternsY() == 2) { - if(count < 5) { - m_xPattern = count-1; - m_yPattern = 0; - } - else if(count < 10) { - m_xPattern = 0; - m_yPattern = 1; - } - else if(count < 25) { - m_xPattern = 1; - m_yPattern = 1; - } - else if(count < 50) { - m_xPattern = 2; - m_yPattern = 1; - } - else if(count <= 100) { - m_xPattern = 3; - m_yPattern = 1; - } - } - else if(isHangable()) { - if(isHookSouth()) { - m_xPattern = getNumPatternsX() >= 2 ? 1 : 0; - } - else if(isHookEast()) { - m_xPattern = getNumPatternsX() >= 3 ? 2 : 0; - } - } - else if(isFluid() || isFluidContainer()) { - int color = Otc::FluidTransparent; - switch(count) { - case Otc::FluidNone: - color = Otc::FluidTransparent; - break; - case Otc::FluidWater: - color = Otc::FluidBlue; - break; - case Otc::FluidMana: - color = Otc::FluidPurple; - break; - case Otc::FluidBeer: - color = Otc::FluidBrown; - break; - case Otc::FluidOil: - color = Otc::FluidBrown; - break; - case Otc::FluidBlood: - color = Otc::FluidRed; - break; - case Otc::FluidSlime: - color = Otc::FluidGreen; - break; - case Otc::FluidMud: - color = Otc::FluidBrown; - break; - case Otc::FluidLemonade: - color = Otc::FluidYellow; - break; - case Otc::FluidMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidWine: - color = Otc::FluidPurple; - break; - case Otc::FluidHealth: - color = Otc::FluidRed; - break; - case Otc::FluidUrine: - color = Otc::FluidYellow; - break; - case Otc::FluidRum: - color = Otc::FluidBrown; - break; - case Otc::FluidFruidJuice: - color = Otc::FluidYellow; - break; - case Otc::FluidCoconutMilk: - color = Otc::FluidWhite; - break; - case Otc::FluidTea: - color = Otc::FluidBrown; - break; - case Otc::FluidMead: - color = Otc::FluidBrown; - break; - default: - color = Otc::FluidTransparent; - break; - } - - m_xPattern = (color % 4) % getNumPatternsX(); - m_yPattern = (color / 4) % getNumPatternsY(); - } + if(isStackable()) + return m_countOrSubType; + else + return 1; +} - m_count = count; +int Item::getSubType() +{ + if(isFluid() || isFluidContainer()) + return m_countOrSubType; + else + return 0; } diff --git a/src/otclient/core/item.h b/src/otclient/core/item.h index 308a2446..38000e6f 100644 --- a/src/otclient/core/item.h +++ b/src/otclient/core/item.h @@ -33,17 +33,23 @@ public: static ItemPtr create(int id); - void draw(const Point& dest, float scaleFactor); + void draw(const Point& dest, float scaleFactor, bool animate); - void setPosition(const Position &position); - void setCount(int count); + void setId(uint32 id); + void setCountOrSubType(uint8 value) { m_countOrSubType = value; } + void setCount(int count) { setCountOrSubType(count); } + void setSubType(int subType) { setCountOrSubType(subType); } - int getCount() { return m_count; } + uint8 getCountOrSubType() { return m_countOrSubType; } + int getSubType(); + int getCount(); + uint32 getId() { return m_id; } ItemPtr asItem() { return std::static_pointer_cast(shared_from_this()); } private: - uint8 m_count; + uint16 m_id; + uint8 m_countOrSubType; }; #endif diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp index 0f0baae8..18d670a4 100644 --- a/src/otclient/core/map.cpp +++ b/src/otclient/core/map.cpp @@ -69,9 +69,9 @@ void Map::load() while(id != 0xFFFF) { ItemPtr item = Item::create(id); if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { - uint8 data; - in.read((char*)&data, sizeof(data)); - item->setCount(data); + uint8 countOrSubType; + in.read((char*)&countOrSubType, sizeof(countOrSubType)); + item->setCountOrSubType(countOrSubType); } addThing(item, pos, 255); in.read((char*)&id, sizeof(id)); @@ -95,8 +95,8 @@ void Map::save() id = item->getId(); out.write((char*)&id, sizeof(id)); if(item->isStackable() || item->isFluidContainer() || item->isFluid()) { - uint8 data = item->getCount(); - out.write((char*)&data, sizeof(data)); + uint8 countOrSubType = item->getCountOrSubType(); + out.write((char*)&countOrSubType, sizeof(countOrSubType)); } } } @@ -264,7 +264,7 @@ void Map::setCentralPosition(const Position& centralPosition) if(teleported) { for(const auto& pair : m_knownCreatures) { const CreaturePtr& creature = pair.second; - const TilePtr& tile = creature->getCurrentTile(); + const TilePtr& tile = creature->getTile(); if(tile) { tile->removeThing(creature); creature->setPosition(Position()); @@ -275,7 +275,7 @@ void Map::setCentralPosition(const Position& centralPosition) for(const auto& pair : m_knownCreatures) { const CreaturePtr& creature = pair.second; if(!isAwareOfPosition(creature->getPosition())) { - const TilePtr& tile = creature->getCurrentTile(); + const TilePtr& tile = creature->getTile(); if(tile) { tile->removeThing(creature); creature->setPosition(Position()); diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index 1723e82f..ec17740d 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -60,12 +60,14 @@ void MapView::draw(const Rect& rect) int tileDrawFlags = 0; if(m_viewRange == NEAR_VIEW) - tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems | Otc::DrawCreatures | Otc::DrawEffects; + tileDrawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawCommonItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawAnimations; else if(m_viewRange == MID_VIEW) - tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems; + tileDrawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawCommonItems; else if(m_viewRange == FAR_VIEW) - tileDrawFlags = Otc::DrawGround | Otc::DrawWalls; - else // HUGE_VIEW + tileDrawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls; + else if(m_tileSize >= 4) // HUGE_VIEW 1 + tileDrawFlags = Otc::DrawGround | Otc::DrawGroundBorders; + else // HUGE_VIEW 2 tileDrawFlags = Otc::DrawGround; bool animate = m_animated; @@ -109,11 +111,11 @@ void MapView::draw(const Rect& rect) // avoid drawing texts on map in far zoom outs if(m_viewRange == NEAR_VIEW) { for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) { - const TilePtr& tile = creature->getCurrentTile(); + const TilePtr& tile = creature->getTile(); Position pos = tile->getPosition(); Point p = transformPositionTo2D(pos) - drawOffset; - p += (creature->getWalkOffset()-tile->getDrawElevation() + Point(8, -8)) * scaleFactor; + p += (creature->getWalkOffset()-tile->getDrawElevation() + Point(8, -10)) * scaleFactor; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); diff --git a/src/otclient/core/missile.cpp b/src/otclient/core/missile.cpp index e91020f5..494f578a 100644 --- a/src/otclient/core/missile.cpp +++ b/src/otclient/core/missile.cpp @@ -27,24 +27,18 @@ #include #include -Missile::Missile() : Thing() -{ - m_startTicks = 0; -} - void Missile::draw(const Point& p, const Rect&) { - float time = (g_clock.ticks() - m_startTicks) / m_duration; - //internalDraw(p + Point(m_deltax * time, m_deltay * time), 0); -} + if(m_id == 0) + return; -void Missile::setPath(const Position& fromPosition, const Position& toPosition) -{ - Otc::Direction direction = fromPosition.getDirectionFromPosition(toPosition); + /* + float time = (g_clock.ticks() - m_startTicks) / m_duration; + int xPattern = 0, yPattern = 0; if(direction == Otc::NorthWest) { - m_xPattern = 0; - m_yPattern = 0; + xPattern = 0; + yPattern = 0; } else if(direction == Otc::North) { m_xPattern = 1; @@ -79,20 +73,29 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition) m_yPattern = 1; } + //internalDraw(p + Point(m_deltax * time, m_deltay * time), 0, 0); + */ +} + +void Missile::setPath(const Position& fromPosition, const Position& toPosition) +{ + m_direction = fromPosition.getDirectionFromPosition(toPosition); + m_position = 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_deltax, m_deltay).length()); m_deltax *= Otc::TILE_PIXELS; m_deltay *= Otc::TILE_PIXELS; + m_animationTimer.restart(); // schedule removal auto self = asMissile(); g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_duration); } -ThingType *Missile::getType() +void Missile::setId(uint32 id) { - return g_thingsType.getThingType(m_id, ThingsType::Missile); + m_id = id; + m_type = g_thingsType.getThingType(m_id, ThingsType::Missile); } diff --git a/src/otclient/core/missile.h b/src/otclient/core/missile.h index 6a8395bf..a89ca50f 100644 --- a/src/otclient/core/missile.h +++ b/src/otclient/core/missile.h @@ -24,6 +24,7 @@ #define SHOT_H #include +#include #include "thing.h" class Missile : public Thing @@ -33,23 +34,24 @@ class Missile : public Thing }; public: - Missile(); - void draw(const Point& p, const Rect&); void updateAnimation(); + void setId(uint32 id); void setPath(const Position& fromPosition, const Position& toPosition); - ThingType *getType(); + uint32 getId() { return m_id; } MissilePtr asMissile() { return std::static_pointer_cast(shared_from_this()); } private: - ticks_t m_startTicks; + Timer m_animationTimer; int m_deltax; int m_deltay; float m_duration; + uint16 m_id; + Otc::Direction m_direction; }; #endif diff --git a/src/otclient/core/thing.cpp b/src/otclient/core/thing.cpp index c8bdfd1b..678780db 100644 --- a/src/otclient/core/thing.cpp +++ b/src/otclient/core/thing.cpp @@ -28,20 +28,9 @@ Thing::Thing() { - m_id = 0; - m_xPattern = 0; - m_yPattern = 0; - m_zPattern = 0; - m_animation = 0; m_type = g_thingsType.getEmptyThingType(); } -void Thing::setId(uint32 id) -{ - m_id = id; - updateType(); -} - int Thing::getStackPriority() { if(isGround()) @@ -58,42 +47,28 @@ int Thing::getStackPriority() return 5; } -const TilePtr& Thing::getCurrentTile() +const TilePtr& Thing::getTile() { return g_map.getTile(m_position); } -void Thing::internalDraw(const Point& dest, float scaleFactor, int layer) +void Thing::internalDraw(const Point& dest, float scaleFactor, int xPattern, int yPattern, int zPattern, int layer, int animationPhase) { int scaledSize = Otc::TILE_PIXELS * scaleFactor; for(int h = 0; h < getDimensionHeight(); h++) { for(int w = 0; w < getDimensionWidth(); w++) { - int spriteId = getSpriteId(w, h, layer, m_xPattern, m_yPattern, m_zPattern, m_animation); + int spriteId = getSpriteId(w, h, layer, xPattern, yPattern, zPattern, animationPhase); if(!spriteId) continue; TexturePtr spriteTex = g_sprites.getSpriteTexture(spriteId); - Rect drawRect((dest.x - w*scaledSize) - getDisplacementX()*scaleFactor, - (dest.y - h*scaledSize) - getDisplacementY()*scaleFactor, + Rect drawRect(dest.x - (w*Otc::TILE_PIXELS - getDisplacementX())*scaleFactor, + dest.y - (h*Otc::TILE_PIXELS - getDisplacementY())*scaleFactor, scaledSize, scaledSize); - g_painter.setColor(Fw::white); g_painter.drawTexturedRect(drawRect, spriteTex); } } } -void Thing::updateType() -{ - if(CreaturePtr creature = asCreature()) - m_type = g_thingsType.getThingType(creature->getOutfit().getId(), creature->getOutfit().getCategory()); - else if(asItem()) - m_type = g_thingsType.getThingType(m_id, ThingsType::Item); - else if(asMissile()) - m_type = g_thingsType.getThingType(m_id, ThingsType::Missile); - else if(asEffect()) - m_type = g_thingsType.getThingType(m_id, ThingsType::Effect); - else - m_type = g_thingsType.getEmptyThingType(); -} diff --git a/src/otclient/core/thing.h b/src/otclient/core/thing.h index ca0ed219..fadc10c0 100644 --- a/src/otclient/core/thing.h +++ b/src/otclient/core/thing.h @@ -40,19 +40,15 @@ public: virtual ~Thing() { } virtual void startAnimation() { } - virtual void draw(const Point& dest, float scaleFactor) { } + virtual void draw(const Point& dest, float scaleFactor, bool animate) { } - virtual void setId(uint32 id); - virtual void setPosition(const Position& position) { m_position = position; } + virtual void setId(uint32 id) { } + void setPosition(const Position& position) { m_position = position; } - uint32 getId() { return m_id; } + virtual uint32 getId() { return 0; } Position getPosition() { return m_position; } int getStackPriority(); - const TilePtr& getCurrentTile(); - - void setXPattern(int xPattern) { m_xPattern = xPattern; } - void setYPattern(int yPattern) { m_yPattern = yPattern; } - void setZPattern(int zPattern) { m_zPattern = zPattern; } + const TilePtr& getTile(); ThingPtr asThing() { return std::static_pointer_cast(shared_from_this()); } virtual ItemPtr asItem() { return nullptr; } @@ -105,14 +101,9 @@ public: int getSpriteId(int w = 0, int h = 0, int layer = 0, int xPattern = 0, int yPattern = 0, int zPattern = 0, int animation = 0) { return m_type->getSpriteId(w, h, layer, xPattern, yPattern, zPattern, animation); } protected: - void internalDraw(const Point& dest, float scaleFactor, int layer); - void updateType(); + void internalDraw(const Point& dest, float scaleFactor, int xPattern, int yPattern, int zPattern, int layer, int animationPhase); - uint32 m_id; //TODO: move to derived class to use less memory Position m_position; - uint8 m_xPattern, m_yPattern, m_zPattern, m_animation; //TODO: remove this variables to use less memory - -private: ThingType *m_type; }; diff --git a/src/otclient/core/thingstype.cpp b/src/otclient/core/thingstype.cpp index 4773a8b6..bc2bbc84 100644 --- a/src/otclient/core/thingstype.cpp +++ b/src/otclient/core/thingstype.cpp @@ -115,11 +115,12 @@ void ThingsType::parseThingType(std::stringstream& fin, ThingType& thingType) ThingType *ThingsType::getThingType(uint16 id, Categories category) { - assert(id != 0); if(category == Item) id -= 100; else if(category == Creature || category == Effect || category == Missile) id -= 1; - assert(id < m_things[category].size()); + + if(id == 0 || id >= m_things[category].size()) + return &m_emptyThingType; return &m_things[category][id]; } diff --git a/src/otclient/core/thingstype.h b/src/otclient/core/thingstype.h index 1acd87d8..66203d0a 100644 --- a/src/otclient/core/thingstype.h +++ b/src/otclient/core/thingstype.h @@ -49,8 +49,8 @@ public: uint32 getSignature() { return m_signature; } bool isLoaded() { return m_loaded; } - int getFirstItemId() { return 100; } - int getMaxItemid() { return m_things[Item].size() + 100 - 1; } + uint16 getFirstItemId() { return 100; } + uint16 getMaxItemid() { return m_things[Item].size() + 99; } private: uint32 m_signature; diff --git a/src/otclient/core/tile.cpp b/src/otclient/core/tile.cpp index 624e31a5..edf3ce3d 100644 --- a/src/otclient/core/tile.cpp +++ b/src/otclient/core/tile.cpp @@ -39,23 +39,18 @@ Tile::Tile(const Position& position) void Tile::draw(const Point& dest, float scaleFactor, int drawFlags) { int drawElevation = 0; - - // optimization far far views - if(drawFlags == Otc::DrawGround) { - const ThingPtr& thing = m_things.front(); - if(thing) - thing->draw(dest, scaleFactor); - return; - } + bool animate = drawFlags & Otc::DrawAnimations; // first bottom items - if(drawFlags & Otc::DrawGround || drawFlags & Otc::DrawWalls) { + if(drawFlags & Otc::DrawGround || drawFlags & Otc::DrawWalls || drawFlags & Otc::DrawGroundBorders) { 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 - drawElevation*scaleFactor, scaleFactor); + if((thing->isGround() && drawFlags & Otc::DrawGround) || + (thing->isGroundBorder() && drawFlags & Otc::DrawGroundBorders) || + (thing->isOnBottom() && drawFlags & Otc::DrawWalls)) + thing->draw(dest - drawElevation*scaleFactor, scaleFactor, animate); drawElevation += thing->getElevation(); if(drawElevation > Otc::MAX_ELEVATION) @@ -69,7 +64,7 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags) const ThingPtr& thing = *it; if(thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround() || thing->asCreature()) break; - thing->draw(dest - drawElevation*scaleFactor, scaleFactor); + thing->draw(dest - drawElevation*scaleFactor, scaleFactor, animate); drawElevation += thing->getElevation(); if(drawElevation > Otc::MAX_ELEVATION) @@ -79,30 +74,32 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags) // creatures if(drawFlags & Otc::DrawCreatures) { - for(const CreaturePtr& creature : m_walkingCreatures) { - creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - drawElevation)*scaleFactor, - dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - drawElevation)*scaleFactor), scaleFactor); + if(animate) { + for(const CreaturePtr& creature : m_walkingCreatures) { + creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - drawElevation)*scaleFactor, + dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - drawElevation)*scaleFactor), scaleFactor, animate); + } } for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) { CreaturePtr creature = (*it)->asCreature(); - if(creature && !creature->isWalking()) - creature->draw(dest - drawElevation, scaleFactor); + if(creature && (!creature->isWalking() || !animate)) + creature->draw(dest - drawElevation, scaleFactor, animate); } } // effects if(drawFlags & Otc::DrawEffects) { for(const EffectPtr& effect : m_effects) - effect->draw(dest, scaleFactor); + effect->draw(dest, scaleFactor, animate); } // top items if(drawFlags & Otc::DrawWalls) { for(const ThingPtr& thing : m_things) { if(thing->isOnTop()) - thing->draw(dest - drawElevation, scaleFactor); + thing->draw(dest - drawElevation, scaleFactor, animate); } } } diff --git a/src/otclient/luafunctions.cpp b/src/otclient/luafunctions.cpp index 0dc60b87..07d95e19 100644 --- a/src/otclient/luafunctions.cpp +++ b/src/otclient/luafunctions.cpp @@ -92,9 +92,6 @@ void OTClient::registerLuaFunctions() g_lua.bindClassMemberFunction("getPosition", &Thing::getPosition); g_lua.bindClassMemberFunction("getStackPriority", &Thing::getStackPriority); g_lua.bindClassMemberFunction("getAnimationPhases", &Thing::getAnimationPhases); - g_lua.bindClassMemberFunction("setXPattern", &Thing::setXPattern); - g_lua.bindClassMemberFunction("setYPattern", &Thing::setYPattern); - g_lua.bindClassMemberFunction("setZPattern", &Thing::setZPattern); g_lua.bindClassMemberFunction("asThing", &Thing::asThing); g_lua.bindClassMemberFunction("asItem", &Thing::asItem); g_lua.bindClassMemberFunction("asCreature", &Thing::asCreature); diff --git a/src/otclient/net/protocolgameparse.cpp b/src/otclient/net/protocolgameparse.cpp index 72ca6635..ebf8dc38 100644 --- a/src/otclient/net/protocolgameparse.cpp +++ b/src/otclient/net/protocolgameparse.cpp @@ -1125,7 +1125,7 @@ ItemPtr ProtocolGame::internalGetItem(InputMessage& msg, int id) ItemPtr item = Item::create(id); if(item->isStackable() || item->isFluidContainer() || item->isFluid()) - item->setCount(msg.getU8()); + item->setCountOrSubType(msg.getU8()); return item; } diff --git a/src/otclient/ui/uicreature.cpp b/src/otclient/ui/uicreature.cpp index c21f85ff..db7a5fd6 100644 --- a/src/otclient/ui/uicreature.cpp +++ b/src/otclient/ui/uicreature.cpp @@ -30,7 +30,7 @@ void UICreature::draw() if(m_creature) { g_painter.setColor(Fw::white); - m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), 1); + m_creature->draw(m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top), 1, false); } drawChildren(); diff --git a/src/otclient/ui/uiitem.cpp b/src/otclient/ui/uiitem.cpp index d41434dc..0810b925 100644 --- a/src/otclient/ui/uiitem.cpp +++ b/src/otclient/ui/uiitem.cpp @@ -38,7 +38,7 @@ void UIItem::draw() Point topLeft = m_rect.bottomRight() - Point(32, 32) + Point(m_padding.left, m_padding.top); g_painter.setColor(Fw::white); - m_item->draw(topLeft, 1); + m_item->draw(topLeft, 1, true); if(m_font && m_item->isStackable() && m_item->getCount() > 1) { std::string count = Fw::tostring(m_item->getCount()); @@ -65,12 +65,6 @@ void UIItem::setItemId(int id) } } -void UIItem::setItemCount(int count) -{ - if(m_item) - m_item->setCount(count); -} - void UIItem::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode) { UIWidget::onStyleApply(styleName, styleNode); diff --git a/src/otclient/ui/uiitem.h b/src/otclient/ui/uiitem.h index d1d148a2..b0d575e9 100644 --- a/src/otclient/ui/uiitem.h +++ b/src/otclient/ui/uiitem.h @@ -34,13 +34,15 @@ public: void draw(); void setItemId(int id); - void setItemCount(int count); + void setItemCount(int count) { if(m_item) m_item->setCount(count); } + void setItemSubType(int subType) { if(m_item) m_item->setSubType(subType); } void setItem(const ItemPtr& item) { m_item = item; } void setVirtual(bool virt) { m_virtual = virt; } void clearItem() { setItemId(0); } int getItemId() { return m_item ? m_item->getId() : 0; } int getItemCount() { return m_item ? m_item->getCount() : 0; } + int getItemSubType() { return m_item ? m_item->getSubType() : 0; } ItemPtr getItem() { return m_item; } bool isVirtual() { return m_virtual; }