#include "actor.h" #include "util.h" #include #include #include static QVariant myBooleanInterpolator(const bool &start, const bool &end, qreal progress) { return (progress == 1.0) ? end : start; } Actor::Actor(Color::Color color, bool local, QGraphicsItem *parent) : GameEntity(color, parent), m_direction(Actor::None), m_local(local), m_reset(true), m_wakaPlayer(NULL), m_roundPoints(0), m_gamePoints(0) { m_type = Type; /* DON'T set any pixmap here. we've a pixmap in the animation * but we need a sprite for the collision detection */ setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); /* higher player "over" lower player */ setZValue(m_color * 10); /* rest of the ctor is only for clients */ if (Constants::server) return; /* our actor pixmap. created after server part */ m_pix = ":/" + QString("actor%1").arg(Util::floorLog2(color) + 1); /* setup icon for player */ m_icon.setPixmap(m_pix); m_icon.setSprite(Constants::sprite_margin, Constants::sprite_margin, Constants::field_size.width, Constants::field_size.height); /* setup static images first * they are visible if no animation is running * and will be the first in m_images */ for (int i = 0; i < 5; i++) { PixmapItem *img = new PixmapItem(m_pix, this); m_images.append(img); int x = i * Constants::sprite_offset + Constants::sprite_margin; int y = Actor::None * Constants::sprite_offset + Constants::sprite_margin; img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); img->setVisible(false); } /* setup animation stuff */ qRegisterAnimationInterpolator(myBooleanInterpolator); m_moving = new QParallelAnimationGroup(this); m_eating.append(NULL); // Actor::None m_eating.append(setupEatingAnimation(Actor::Left)); m_eating.append(setupEatingAnimation(Actor::Right)); m_eating.append(setupEatingAnimation(Actor::Up)); m_eating.append(setupEatingAnimation(Actor::Down)); /* dieing animation */ qWarning() << "color=" << Util::colorToString(m_color) << "zValue=" << zValue() << "dieing.z=" << 100 - m_color; m_dieing = new QSequentialAnimationGroup(this); for (int i = 0; i < 11; i++) { PixmapItem *img = new PixmapItem(m_pix, this); m_images.append(img); int x = i * Constants::sprite_offset + Constants::sprite_margin; int y = 5 * Constants::sprite_offset + Constants::sprite_margin; img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); img->setVisible(false); QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", m_dieing); fadein->setDuration(0); fadein->setEndValue(true); m_dieing->addPause(130); QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", m_dieing); fadeout->setDuration(0); fadeout->setEndValue(false); } /* setup waka sound */ if (local) m_wakaPlayer = new GaplessAudioPlayer(Sound::WakaWaka, 100, this); /* make the picture showing the current direction visible */ m_images[m_direction]->setVisible(true); } QSequentialAnimationGroup *Actor::setupEatingAnimation(Actor::Movement direction) { QSequentialAnimationGroup *eating = new QSequentialAnimationGroup(this); eating->setLoopCount(-1); for (int i = 0; i < 4; i++) { PixmapItem *img = new PixmapItem(m_pix, this); m_images.append(img); int x = i * Constants::sprite_offset + Constants::sprite_margin; int y = direction * Constants::sprite_offset + Constants::sprite_margin; img->setSprite(x, y, Constants::field_size.width, Constants::field_size.height); img->setVisible(false); QPropertyAnimation *fadein = new QPropertyAnimation(img, "visible", eating); fadein->setDuration(0); fadein->setEndValue(true); eating->addPause(100); QPropertyAnimation *fadeout = new QPropertyAnimation(img, "visible", eating); fadeout->setDuration(0); fadeout->setEndValue(false); QPropertyAnimation *move = new QPropertyAnimation(img, "pos", m_moving); move->setDuration(Constants::tick - 30); move->setEndValue(QPoint(0, 0)); } return eating; } PixmapItem &Actor::icon() { return m_icon; } Actor::Movement Actor::direction() { return m_direction; } void Actor::setDirection(Movement direction) { m_direction = direction; } bool Actor::isLocal() { return m_local; } bool Actor::hadReset() { if (!m_reset) return false; m_reset = false; return true; } void Actor::reset() { m_reset = true; if (Constants::server) { m_direction = Actor::None; return; } stopEating(); m_moving->stop(); setZValue(m_color * 10); m_dieing->stop(); /* hide all pictures */ for (int i = 0; i < m_images.size(); ++i) m_images.at(i)->setVisible(false); if (m_eating[m_direction] != NULL) m_eating[m_direction]->stop(); m_direction = Actor::None; m_images[m_direction]->setVisible(true); } void Actor::move(QPoint newpos) { QPoint oldpos = pos().toPoint(); Actor::Movement direction = Actor::None; if (oldpos.x() - newpos.x() < 0) direction = Actor::Right; else if (oldpos.x() - newpos.x() > 0) direction = Actor::Left; else if (oldpos.y() - newpos.y() < 0) direction = Actor::Down; else if (oldpos.y() - newpos.y() > 0) direction = Actor::Up; move(direction); } void Actor::move(Actor::Movement direction) { if (Constants::server) return moveByServer(direction); /* stop current animation if direction changed */ if (direction != m_direction) { /* hide all pictures */ for (int i = 0; i < m_images.size(); ++i) m_images.at(i)->setVisible(false); if (m_eating[m_direction] != NULL) m_eating[m_direction]->stop(); } QPointF endpos = movementToPoint(direction); switch(direction) { case Actor::Left: case Actor::Right: endpos *= Constants::field_size.width; break; case Actor::Up: case Actor::Down: endpos *= Constants::field_size.height; break; case Actor::None: default: break; } for(int i = 0; i < m_moving->animationCount(); ++i) { QPropertyAnimation *move = dynamic_cast(m_moving->animationAt(i)); move->setStartValue(QPoint(0, 0) - endpos); } setPos(pos() + endpos); /* start new animation if direction changed */ if (direction != m_direction) { if (direction == Actor::None) m_images[m_direction]->setVisible(true); else m_eating[direction]->start(); } /* start moving animation */ if (direction != Actor::None) m_moving->start(); m_direction = direction; } void Actor::moveByServer(Actor::Movement direction) { QPointF endpos = movementToPoint(direction); switch(direction) { case Actor::Left: case Actor::Right: endpos *= Constants::field_size.width; break; case Actor::Up: case Actor::Down: endpos *= Constants::field_size.height; break; case Actor::None: default: break; } setPos(pos() + endpos); m_direction = direction; } bool Actor::isMoving() { return (m_moving->state() == QAbstractAnimation::Running); } bool Actor::canEat(Actor *other, const QList &order) { if (other == NULL || order.empty() || m_color == other->color()) return false; int idx = order.indexOf(m_color); return (order.at(idx + 1) == other->color()); } void Actor::onDie(Actor *other) { other->eatingPacman(); die(); } void Actor::die() { if (Constants::server) return; reset(); m_images[m_direction]->setVisible(false); setZValue(zValue() * 10); m_dieing->start(); if (m_local) AudioManager::self()->play(Sound::Die); } void Actor::eatingFruit() { if (!m_local) return; AudioManager::self()->play(Sound::EatingFruit); } void Actor::eatingPacman() { if (!m_local) return; AudioManager::self()->play(Sound::EatingGhost); } void Actor::startEating() { if (!m_local || !m_wakaPlayer->isWorking()) return; m_wakaPlayer->play(); } void Actor::stopEating() { if (!m_local || !m_wakaPlayer->isWorking()) return; m_wakaPlayer->pause(); } unsigned int Actor::getRoundPoints() { return m_roundPoints; } unsigned int Actor::getGamePoints() { return m_gamePoints; } void Actor::addRoundPoints(unsigned int amount) { m_roundPoints += amount; } void Actor::finishRound(bool died) { if (!died) m_gamePoints += m_roundPoints; m_roundPoints = 0; } QPoint Actor::movementToPoint(const Actor::Movement direction) { QPoint endpos(0,0); switch (direction) { case Actor::Up: endpos = QPoint(0, -1); break; case Actor::Down: endpos = QPoint(0, 1); break; case Actor::Left: endpos = QPoint(-1, 0); break; case Actor::Right: endpos = QPoint(1, 0); break; case Actor::None: break; default: Q_ASSERT(false); } return endpos; }