/* * The Mana Client * Copyright (C) 2010-2012 The Mana Developers * * This file is part of The Mana Client. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "actorsprite.h" #include "configuration.h" #include "event.h" #include "localplayer.h" #include "log.h" #include "particle.h" #include "simpleanimation.h" #include "sprite.h" #include "resources/animation.h" #include "resources/imageset.h" #include "resources/resourcemanager.h" #include "resources/theme.h" #include "utils/time.h" #include #include #define EFFECTS_FILE "effects.xml" ResourceRef ActorSprite::targetCursorImages[2][NUM_TC]; SimpleAnimation *ActorSprite::targetCursor[2][NUM_TC]; bool ActorSprite::loaded = false; ActorSprite::ActorSprite(int id) : mId(id) {} ActorSprite::~ActorSprite() { // Notify listeners of the destruction. Event event(Event::Destroyed); event.setActor("source", this); event.trigger(Event::ActorSpriteChannel); } int ActorSprite::getDrawOrder() const { return Actor::getDrawOrder() + paths.getIntValue("spriteOffsetY"); } bool ActorSprite::draw(Graphics *graphics, int offsetX, int offsetY) const { int px = getPixelX() + offsetX; int py = getPixelY() + offsetY; if (mUsedTargetCursor) mUsedTargetCursor->draw(graphics, px, py); return drawSpriteAt(graphics, px, py); } bool ActorSprite::drawSpriteAt(Graphics *graphics, int x, int y) const { y += paths.getIntValue("spriteOffsetY"); return mSprites.draw(graphics, x, y); } void ActorSprite::logic() { // Update sprite animations mSprites.update(Time::deltaTimeMs()); if (mUsedTargetCursor) mUsedTargetCursor->update(Time::deltaTimeMs()); // Erase all extinct particle effects mChildParticleEffects.erase(std::remove_if(mChildParticleEffects.begin(), mChildParticleEffects.end(), [](const Particle *p) { return p->isExtinct(); }), mChildParticleEffects.end()); // Move the remaining const float py = mPos.y + paths.getIntValue("spriteOffsetY"); for (Particle *p : mChildParticleEffects) p->moveTo(mPos.x, py); } void ActorSprite::setMap(Map* map) { Actor::setMap(map); // Clear particle effect list because child particles became invalid mChildParticleEffects.clear(); } void ActorSprite::controlParticle(Particle *particle) { mChildParticleEffects.emplace_back(particle); } void ActorSprite::setTargetType(TargetCursorType type) { if (type == TCT_NONE) untarget(); else mUsedTargetCursor = targetCursor[type][getTargetCursorSize()]; } void ActorSprite::setupSpriteDisplay(const SpriteDisplay &display, bool forceDisplay) { mSprites.clear(); for (const auto &sprite : display.sprites) { std::string file = paths.getStringValue("sprites") + sprite.sprite; mSprites.add(Sprite::load(file, sprite.variant)); } // Ensure that something is shown, if desired if (mSprites.size() == 0 && forceDisplay) { mSprites.add(Sprite::load(paths.getStringValue("sprites") + paths.getStringValue("spriteErrorFile"))); } mChildParticleEffects.clear(); //setup particle effects if (Particle::enabled) { for (const auto &particle : display.particles) controlParticle(particleEngine->addEffect(particle, 0, 0)); } } void ActorSprite::load() { if (loaded) unload(); initTargetCursor(); loaded = true; } void ActorSprite::unload() { if (!loaded) return; cleanupTargetCursors(); loaded = false; } void ActorSprite::initTargetCursor() { static const std::string targetCursor = "graphics/target-cursor-%s-%s.png"; static const char * const cursorTypeStr[NUM_TCT] = { "normal", "in-range" }; static const int targetWidths[NUM_TC] = { 44, 62, 82 }; static const int targetHeights[NUM_TC] = { 35, 44, 60 }; static const char * const cursorSizeStr[NUM_TC] = { "s", "m", "l" }; // Load target cursors for (int size = 0; size < NUM_TC; size++) { for (int type = 0; type < NUM_TCT; type++) { loadTargetCursor(strprintf(targetCursor.c_str(), cursorTypeStr[type], cursorSizeStr[size]), targetWidths[size], targetHeights[size], type, size); } } } void ActorSprite::cleanupTargetCursors() { for (int size = 0; size < NUM_TC; size++) { for (int type = 0; type < NUM_TCT; type++) { delete targetCursor[type][size]; targetCursorImages[type][size] = nullptr; } } } void ActorSprite::loadTargetCursor(const std::string &filename, int width, int height, int type, int size) { assert(size > -1); assert(size < 3); ResourceManager *resman = ResourceManager::getInstance(); auto currentImageSet = resman->getImageSet(filename, width, height); if (!currentImageSet) { Log::info("Error loading target cursor: %s", filename.c_str()); return; } Animation anim; for (unsigned int i = 0; i < currentImageSet->size(); ++i) { anim.addFrame(currentImageSet->get(i), DEFAULT_FRAME_DELAY, -(currentImageSet->getWidth() / 2), -(currentImageSet->getHeight() / 2)); } auto *currentCursor = new SimpleAnimation(std::move(anim)); targetCursorImages[type][size] = currentImageSet; targetCursor[type][size] = currentCursor; }