/* * Copyright (c) 2010-2011 OTClient * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "creature.h" #include "thingstype.h" #include "localplayer.h" #include "map.h" #include "tile.h" #include "item.h" #include #include Creature::Creature() : Thing() { m_healthPercent = 0; m_direction = Otc::South; m_walking = false; m_walkOffsetX = 0; m_walkOffsetY = 0; m_lastWalkAnim = 1; m_informationFont = g_fonts.getFont("verdana-11px-rounded"); } void Creature::draw(int x, int y) { x += m_walkOffsetX; y += m_walkOffsetY; const ThingType& type = getType(); // Render creature for(m_yPattern = 0; m_yPattern < type.yPattern; m_yPattern++) { // continue if we dont have this addon. if(m_yPattern > 0 && !(m_outfit.addons & (1 << (m_yPattern-1)))) continue; // draw white item internalDraw(x, y, 0); // draw mask if exists if(type.layers > 1) { // switch to blend color mode g_graphics.bindBlendFunc(Fw::BlendColorzing); // head g_graphics.bindColor(Otc::OutfitColors[m_outfit.head]); internalDraw(x, y, 1, Otc::SpriteYellowMask); // body g_graphics.bindColor(Otc::OutfitColors[m_outfit.body]); internalDraw(x, y, 1, Otc::SpriteRedMask); // legs g_graphics.bindColor(Otc::OutfitColors[m_outfit.legs]); internalDraw(x, y, 1, Otc::SpriteGreenMask); // feet g_graphics.bindColor(Otc::OutfitColors[m_outfit.feet]); internalDraw(x, y, 1, Otc::SpriteBlueMask); // restore default blend func g_graphics.bindBlendFunc(Fw::BlendDefault); g_graphics.bindColor(Fw::white); } } // Update animation and position if(m_walking && type.animationPhases > 1) { if(g_platform.getTicks() - m_lastTicks >= m_walkTimePerPixel) { int pixelsWalked = std::floor((g_platform.getTicks() - m_lastTicks) / m_walkTimePerPixel); int remainingTime = (g_platform.getTicks() - m_lastTicks) % (int)m_walkTimePerPixel; if(m_inverseWalking) { if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest) m_walkOffsetY = std::max(m_walkOffsetY - pixelsWalked, 0); else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest) m_walkOffsetY = std::min(m_walkOffsetY + pixelsWalked, 0); if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) m_walkOffsetX = std::min(m_walkOffsetX + pixelsWalked, 0); else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) m_walkOffsetX = std::max(m_walkOffsetX - pixelsWalked, 0); if(m_walkOffsetX == 0 && m_walkOffsetY == 0) cancelWalk(m_direction); } else { if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest) m_walkOffsetY = std::max(m_walkOffsetY - pixelsWalked, -32); else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest) m_walkOffsetY = std::min(m_walkOffsetY + pixelsWalked, 32); if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) m_walkOffsetX = std::min(m_walkOffsetX + pixelsWalked, 32); else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) m_walkOffsetX = std::max(m_walkOffsetX - pixelsWalked, -32); if(std::abs(m_walkOffsetX) == 32 && std::abs(m_walkOffsetY) == 32) m_animation = 0; } int walkOffset = std::max(std::abs(m_walkOffsetX), std::abs(m_walkOffsetY)) + 32; if(walkOffset % (int)std::ceil(32 / (float)type.animationPhases) == 0) { if((m_lastWalkAnim+1) % type.animationPhases == 0) m_lastWalkAnim = 1; else m_lastWalkAnim++; } if(m_walking) m_animation = m_lastWalkAnim; m_lastTicks = g_platform.getTicks() - remainingTime; } } } void Creature::drawInformation(int x, int y, bool useGray, const Rect& rect) { Color fillColor = Color(96, 96, 96); if(!useGray) fillColor = m_informationColor; // calculate main rects Rect backgroundRect = Rect(x-(13.5), y, 27, 4); if(backgroundRect.left() < rect.left()) backgroundRect.moveLeft(rect.left()); if(backgroundRect.top() < rect.top()) backgroundRect.moveTop(rect.top()); if(backgroundRect.bottom() > rect.bottom()) backgroundRect.moveBottom(rect.bottom()); if(backgroundRect.right() > rect.right()) backgroundRect.moveRight(rect.right()); Size textSize = m_informationFont->calculateTextRectSize(m_name); Rect textRect = Rect(x - textSize.width() / 2.0, y-15, textSize); if(textRect.left() < rect.left()) textRect.moveLeft(rect.left()); if(textRect.top() < rect.top()) textRect.moveTop(rect.top()); if(textRect.bottom() > rect.bottom()) textRect.moveBottom(rect.bottom()); if(textRect.right() > rect.right()) textRect.moveRight(rect.right()); // distance them if(textRect.top() == rect.top()) backgroundRect.moveTop(textRect.top() + 15); if(backgroundRect.bottom() == rect.bottom()) textRect.moveTop(backgroundRect.top() - 15); // health rect is based on background rect, so no worries Rect healthRect = backgroundRect.expanded(-1); healthRect.setWidth((m_healthPercent / 100.0) * 25); // draw g_graphics.bindColor(Fw::black); g_graphics.drawFilledRect(backgroundRect); g_graphics.bindColor(fillColor); g_graphics.drawFilledRect(healthRect); m_informationFont->renderText(m_name, textRect, Fw::AlignTopCenter, fillColor); } void Creature::walk(const Position& position, bool inverse) { // set walking state m_walking = true; m_inverseWalking = inverse; int walkTimeFactor = 1; // set new direction if(m_position + Position(0, -1, 0) == position) { m_direction = Otc::North; m_walkOffsetY = 32; } else if(m_position + Position(1, 0, 0) == position) { m_direction = Otc::East; m_walkOffsetX = -32; } else if(m_position + Position(0, 1, 0) == position) { m_direction = Otc::South; m_walkOffsetY = -32; } else if(m_position + Position(-1, 0, 0) == position) { m_direction = Otc::West; m_walkOffsetX = 32; } else if(m_position + Position(1, -1, 0) == position) { m_direction = Otc::NorthEast; m_walkOffsetX = -32; m_walkOffsetY = 32; walkTimeFactor = 2; } else if(m_position + Position(1, 1, 0) == position) { m_direction = Otc::SouthEast; m_walkOffsetX = -32; m_walkOffsetY = -32; walkTimeFactor = 2; } else if(m_position + Position(-1, 1, 0) == position) { m_direction = Otc::SouthWest; m_walkOffsetX = 32; m_walkOffsetY = -32; walkTimeFactor = 2; } else if(m_position + Position(-1, -1, 0) == position) { m_direction = Otc::NorthWest; m_walkOffsetX = 32; m_walkOffsetY = 32; walkTimeFactor = 2; } else { // Teleport // we teleported, dont walk or change direction m_walking = false; } if(!m_inverseWalking) { m_walkOffsetX = 0; m_walkOffsetY = 0; } if(m_walking) { // Calculate xPattern if(m_direction >= 4) { if(m_direction == Otc::NorthEast || m_direction == Otc::SouthEast) m_xPattern = Otc::East; else if(m_direction == Otc::NorthWest || m_direction == Otc::SouthWest) m_xPattern = Otc::West; } else { m_xPattern = m_direction; } // get walk speed int groundSpeed = 0; ItemPtr ground = g_map.getTile(position)->getGround(); if(ground) groundSpeed = ground->getType().groundSpeed; float walkTime = walkTimeFactor * 1000.0 * (float)groundSpeed / m_speed; walkTime = walkTime == 0 ? 1000 : walkTime; m_walkTimePerPixel = walkTime / 32.0; m_lastTicks = g_platform.getTicks(); } } void Creature::cancelWalk(Otc::Direction direction) { m_walking = false; m_walkOffsetX = 0; m_walkOffsetY = 0; m_animation = 0; m_direction = direction; } void Creature::setHealthPercent(uint8 healthPercent) { int oldHealthPercent = m_healthPercent; m_healthPercent = healthPercent; onHealthPercentChange(oldHealthPercent); } const ThingType& Creature::getType() { return g_thingsType.getCreatureType(m_outfit.type); } void Creature::onHealthPercentChange(int) { m_informationColor = Fw::black; if(m_healthPercent > 92) { m_informationColor.setGreen(188); } else if(m_healthPercent > 60) { m_informationColor.setRed(80); m_informationColor.setGreen(161); m_informationColor.setBlue(80); } else if(m_healthPercent > 30) { m_informationColor.setRed(161); m_informationColor.setGreen(161); } else if(m_healthPercent > 8) { m_informationColor.setRed(160); m_informationColor.setGreen(39); m_informationColor.setBlue(39); } else if(m_healthPercent > 3) { m_informationColor.setRed(160); } else { m_informationColor.setRed(79); } }