Ejemplo n.º 1
0
void MapComposite::update()
{
    // Update object status
    const std::vector< Entity * > &entities = getEverything();
    for (std::vector< Entity * >::const_iterator it = entities.begin(),
         it_end = entities.end(); it != it_end; ++it)
    {
        (*it)->update();
    }

    if (mUpdateCallback.isValid())
    {
        Script *s = ScriptManager::currentState();
        s->setMap(this);
        s->prepare(mUpdateCallback);
        s->push(mID);
        s->execute();
    }

    // Move objects around and update zones.
    for (BeingIterator it(getWholeMapIterator()); it; ++it)
    {
        (*it)->move();
    }

    for (int i = 0; i < mContent->mapHeight * mContent->mapWidth; ++i)
    {
        mContent->zones[i].destinations.clear();
    }

    // Cannot use a WholeMap iterator as objects will change zones under its feet.
    for (std::vector< Entity * >::iterator i = mContent->entities.begin(),
         i_end = mContent->entities.end(); i != i_end; ++i)
    {
        if (!(*i)->canMove())
            continue;

        Being *obj = static_cast< Being * >(*i);

        const Point &pos1 = obj->getOldPosition(),
                    &pos2 = obj->getPosition();

        MapZone &src = mContent->getZone(pos1),
                &dst = mContent->getZone(pos2);
        if (&src != &dst)
        {
            addZone(src.destinations, &dst - mContent->zones);
            src.remove(obj);
            dst.insert(obj);
        }
    }
}
Ejemplo n.º 2
0
/**
 * Informs a player of what happened around the character.
 */
static void informPlayer(MapComposite *map, Character *p)
{
    MessageOut moveMsg(GPMSG_BEINGS_MOVE);
    MessageOut damageMsg(GPMSG_BEINGS_DAMAGE);
    const Point &pold = p->getOldPosition(), ppos = p->getPosition();
    int pid = p->getPublicID(), pflags = p->getUpdateFlags();
    int visualRange = Configuration::getValue("game_visualRange", 448);

    // Inform client about activities of other beings near its character
    for (BeingIterator it(map->getAroundBeingIterator(p, visualRange));
         it; ++it)
    {
        Being *o = *it;

        const Point &oold = o->getOldPosition(), opos = o->getPosition();
        int otype = o->getType();
        int oid = o->getPublicID(), oflags = o->getUpdateFlags();
        int flags = 0;

        // Check if the character p and the moving object o are around.
        bool wereInRange = pold.inRangeOf(oold, visualRange) &&
                           !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP);
        bool willBeInRange = ppos.inRangeOf(opos, visualRange);

        if (!wereInRange && !willBeInRange)
        {
            // Nothing to report: o and p are far away from each other.
            continue;
        }


        if (wereInRange && willBeInRange)
        {
            // Send attack messages.
            if ((oflags & UPDATEFLAG_ATTACK) && oid != pid)
            {
                MessageOut AttackMsg(GPMSG_BEING_ATTACK);
                AttackMsg.writeInt16(oid);
                AttackMsg.writeInt8(o->getDirection());
                AttackMsg.writeInt8(static_cast< Being * >(o)->getAttackId());
                gameHandler->sendTo(p, AttackMsg);
            }

            // Send action change messages.
            if ((oflags & UPDATEFLAG_ACTIONCHANGE))
            {
                MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE);
                ActionMsg.writeInt16(oid);
                ActionMsg.writeInt8(static_cast< Being * >(o)->getAction());
                gameHandler->sendTo(p, ActionMsg);
            }

            // Send looks change messages.
            if (oflags & UPDATEFLAG_LOOKSCHANGE)
            {
                MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE);
                LooksMsg.writeInt16(oid);
                Character * c = static_cast<Character * >(o);
                serializeLooks(c, LooksMsg);
                LooksMsg.writeInt16(c->getHairStyle());
                LooksMsg.writeInt16(c->getHairColor());
                LooksMsg.writeInt16(c->getGender());
                gameHandler->sendTo(p, LooksMsg);
            }

            // Send emote messages.
            if (oflags & UPDATEFLAG_EMOTE)
            {
                int emoteId = o->getLastEmote();
                if (emoteId > -1)
                {
                    MessageOut EmoteMsg(GPMSG_BEING_EMOTE);
                    EmoteMsg.writeInt16(oid);
                    EmoteMsg.writeInt16(emoteId);
                    gameHandler->sendTo(p, EmoteMsg);
                }
            }

            // Send direction change messages.
            if (oflags & UPDATEFLAG_DIRCHANGE)
            {
                MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE);
                DirMsg.writeInt16(oid);
                DirMsg.writeInt8(o->getDirection());
                gameHandler->sendTo(p, DirMsg);
            }

            // Send damage messages.
            if (o->canFight())
            {
                Being *victim = static_cast< Being * >(o);
                const Hits &hits = victim->getHitsTaken();
                for (Hits::const_iterator j = hits.begin(),
                     j_end = hits.end(); j != j_end; ++j)
                {
                    damageMsg.writeInt16(oid);
                    damageMsg.writeInt16(*j);
                }
            }

            if (oold == opos)
            {
                // o does not move, nothing more to report.
                continue;
            }
        }

        if (!willBeInRange)
        {
            // o is no longer visible from p. Send leave message.
            MessageOut leaveMsg(GPMSG_BEING_LEAVE);
            leaveMsg.writeInt16(oid);
            gameHandler->sendTo(p, leaveMsg);
            continue;
        }

        if (!wereInRange)
        {
            // o is now visible by p. Send enter message.
            MessageOut enterMsg(GPMSG_BEING_ENTER);
            enterMsg.writeInt8(otype);
            enterMsg.writeInt16(oid);
            enterMsg.writeInt8(static_cast< Being *>(o)->getAction());
            enterMsg.writeInt16(opos.x);
            enterMsg.writeInt16(opos.y);
            enterMsg.writeInt8(o->getDirection());
            enterMsg.writeInt8(o->getGender());
            switch (otype)
            {
                case OBJECT_CHARACTER:
                {
                    Character *q = static_cast< Character * >(o);
                    enterMsg.writeString(q->getName());
                    enterMsg.writeInt8(q->getHairStyle());
                    enterMsg.writeInt8(q->getHairColor());
                    serializeLooks(q, enterMsg);
                } break;

                case OBJECT_MONSTER:
                {
                    Monster *q = static_cast< Monster * >(o);
                    enterMsg.writeInt16(q->getSpecy()->getId());
                    enterMsg.writeString(q->getName());
                } break;

                case OBJECT_NPC:
                {
                    NpcComponent *npcComponent = o->getComponent<NpcComponent>();
                    enterMsg.writeInt16(npcComponent->getNpcId());
                    enterMsg.writeString(o->getName());
                } break;

                default:
                    assert(false); // TODO
                    break;
            }
            gameHandler->sendTo(p, enterMsg);
        }

        if (opos != oold)
        {
            // Add position check coords every 5 seconds.
            if (currentTick % 50 == 0)
                flags |= MOVING_POSITION;

            flags |= MOVING_DESTINATION;
        }

        // Send move messages.
        moveMsg.writeInt16(oid);
        moveMsg.writeInt8(flags);
        if (flags & MOVING_POSITION)
        {
            moveMsg.writeInt16(oold.x);
            moveMsg.writeInt16(oold.y);
        }

        if (flags & MOVING_DESTINATION)
        {
            moveMsg.writeInt16(opos.x);
            moveMsg.writeInt16(opos.y);
            // We multiply the sent speed (in tiles per second) by ten
            // to get it within a byte with decimal precision.
            // For instance, a value of 4.5 will be sent as 45.
            moveMsg.writeInt8((unsigned short)
                (o->getModifiedAttribute(ATTR_MOVE_SPEED_TPS) * 10));
        }
    }

    // Do not send a packet if nothing happened in p's range.
    if (moveMsg.getLength() > 2)
        gameHandler->sendTo(p, moveMsg);

    if (damageMsg.getLength() > 2)
        gameHandler->sendTo(p, damageMsg);

    // Inform client about status change.
    p->sendStatus();

    // Inform client about health change of party members
    for (CharacterIterator i(map->getWholeMapIterator()); i; ++i)
    {
        Character *c = *i;

        // Make sure its not the same character
        if (c == p)
            continue;

        // make sure they are in the same party
        if (c->getParty() == p->getParty())
        {
            int cflags = c->getUpdateFlags();
            if (cflags & UPDATEFLAG_HEALTHCHANGE)
            {
                MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE);
                healthMsg.writeInt16(c->getPublicID());
                healthMsg.writeInt16(c->getModifiedAttribute(ATTR_HP));
                healthMsg.writeInt16(c->getModifiedAttribute(ATTR_MAX_HP));
                gameHandler->sendTo(p, healthMsg);
            }
        }
    }

    // Inform client about items on the ground around its character
    MessageOut itemMsg(GPMSG_ITEMS);
    for (FixedActorIterator it(map->getAroundBeingIterator(p, visualRange));
         it; ++it)
    {
        Actor *o = *it;

        assert(o->getType() == OBJECT_ITEM ||
               o->getType() == OBJECT_EFFECT);

        Point opos = o->getPosition();
        int oflags = o->getUpdateFlags();
        bool willBeInRange = ppos.inRangeOf(opos, visualRange);
        bool wereInRange = pold.inRangeOf(opos, visualRange) &&
                           !((pflags | oflags) & UPDATEFLAG_NEW_ON_MAP);

        if (willBeInRange ^ wereInRange)
        {
            switch (o->getType())
            {
                case OBJECT_ITEM:
                {
                    ItemComponent *item = o->getComponent<ItemComponent>();
                    ItemClass *itemClass = item->getItemClass();

                    if (oflags & UPDATEFLAG_NEW_ON_MAP)
                    {
                        /* Send a specific message to the client when an item appears
                           out of nowhere, so that a sound/animation can be performed. */
                        MessageOut appearMsg(GPMSG_ITEM_APPEAR);
                        appearMsg.writeInt16(itemClass->getDatabaseID());
                        appearMsg.writeInt16(opos.x);
                        appearMsg.writeInt16(opos.y);
                        gameHandler->sendTo(p, appearMsg);
                    }
                    else
                    {
                        itemMsg.writeInt16(willBeInRange ? itemClass->getDatabaseID() : 0);
                        itemMsg.writeInt16(opos.x);
                        itemMsg.writeInt16(opos.y);
                    }
                }
                break;
                case OBJECT_EFFECT:
                {
                    EffectComponent *e = o->getComponent<EffectComponent>();
                    e->setShown();
                    // Don't show old effects
                    if (!(oflags & UPDATEFLAG_NEW_ON_MAP))
                        break;

                    if (Being *b = e->getBeing())
                    {
                        MessageOut effectMsg(GPMSG_CREATE_EFFECT_BEING);
                        effectMsg.writeInt16(e->getEffectId());
                        effectMsg.writeInt16(b->getPublicID());
                        gameHandler->sendTo(p, effectMsg);
                    } else {
                        MessageOut effectMsg(GPMSG_CREATE_EFFECT_POS);
                        effectMsg.writeInt16(e->getEffectId());
                        effectMsg.writeInt16(opos.x);
                        effectMsg.writeInt16(opos.y);
                        gameHandler->sendTo(p, effectMsg);
                    }
                }
                break;
                default: break;
            } // Switch
        }
    }

    // Do not send a packet if nothing happened in p's range.
    if (itemMsg.getLength() > 2)
        gameHandler->sendTo(p, itemMsg);
}