/** * 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("visualRange", 320); // Inform client about activities of other beings near its character for (BeingIterator i(map->getAroundBeingIterator(p, visualRange)); i; ++i) { Being *o = *i; 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.writeShort(oid); AttackMsg.writeByte(o->getDirection()); AttackMsg.writeByte(static_cast< Being * >(o)->getAttackType()); gameHandler->sendTo(p, AttackMsg); } // Send action change messages. if ((oflags & UPDATEFLAG_ACTIONCHANGE)) { MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE); ActionMsg.writeShort(oid); ActionMsg.writeByte(static_cast< Being * >(o)->getAction()); gameHandler->sendTo(p, ActionMsg); } // Send looks change messages. if (oflags & UPDATEFLAG_LOOKSCHANGE) { MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE); LooksMsg.writeShort(oid); Character * c = static_cast<Character * >(o); serializeLooks(c, LooksMsg, false); LooksMsg.writeShort(c->getHairStyle()); LooksMsg.writeShort(c->getHairColor()); LooksMsg.writeShort(c->getGender()); gameHandler->sendTo(p, LooksMsg); } // Send direction change messages. if (oflags & UPDATEFLAG_DIRCHANGE) { MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE); DirMsg.writeShort(oid); DirMsg.writeByte(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.writeShort(oid); damageMsg.writeShort(*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.writeShort(oid); gameHandler->sendTo(p, leaveMsg); continue; } if (!wereInRange) { // o is now visible by p. Send enter message. MessageOut enterMsg(GPMSG_BEING_ENTER); enterMsg.writeByte(otype); enterMsg.writeShort(oid); enterMsg.writeByte(static_cast< Being *>(o)->getAction()); enterMsg.writeShort(opos.x); enterMsg.writeShort(opos.y); switch (otype) { case OBJECT_CHARACTER: { Character *q = static_cast< Character * >(o); enterMsg.writeString(q->getName()); enterMsg.writeByte(q->getHairStyle()); enterMsg.writeByte(q->getHairColor()); enterMsg.writeByte(q->getGender()); serializeLooks(q, enterMsg, true); } break; case OBJECT_MONSTER: { Monster *q = static_cast< Monster * >(o); enterMsg.writeShort(q->getSpecy()->getType()); enterMsg.writeString(q->getName()); } break; case OBJECT_NPC: { NPC *q = static_cast< NPC * >(o); enterMsg.writeShort(q->getNPC()); enterMsg.writeString(q->getName()); } break; default: assert(false); // TODO } gameHandler->sendTo(p, enterMsg); } if (opos != oold) { flags |= MOVING_POSITION; } // Send move messages. moveMsg.writeShort(oid); moveMsg.writeByte(flags); if (flags & MOVING_POSITION) { moveMsg.writeShort(opos.x); moveMsg.writeShort(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.writeByte((unsigned short) (o->getSpeed() * 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.writeShort(c->getPublicID()); healthMsg.writeShort(c->getHealth()); gameHandler->sendTo(p, healthMsg); } } } // Inform client about items on the ground around its character MessageOut itemMsg(GPMSG_ITEMS); for (FixedActorIterator i(map->getAroundBeingIterator(p, visualRange)); i; ++i) { assert((*i)->getType() == OBJECT_ITEM || (*i)->getType() == OBJECT_EFFECT); Actor *o = *i; 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: { Item *o = static_cast< Item * >(*i); 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.writeShort(o->getItemClass()->getDatabaseID()); appearMsg.writeShort(opos.x); appearMsg.writeShort(opos.y); gameHandler->sendTo(p, appearMsg); } else { itemMsg.writeShort(willBeInRange ? o->getItemClass()->getDatabaseID() : 0); itemMsg.writeShort(opos.x); itemMsg.writeShort(opos.y); } } break; case OBJECT_EFFECT: { Effect *o = static_cast< Effect * >(*i); o->show(); // Don't show old effects if (!(oflags & UPDATEFLAG_NEW_ON_MAP)) break; Being *b = o->getBeing(); if (b) { MessageOut effectMsg(GPMSG_CREATE_EFFECT_BEING); effectMsg.writeShort(o->getEffectId()); effectMsg.writeShort(b->getPublicID()); gameHandler->sendTo(p, effectMsg); } else { MessageOut effectMsg(GPMSG_CREATE_EFFECT_POS); effectMsg.writeShort(o->getEffectId()); effectMsg.writeShort(opos.x); effectMsg.writeShort(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); }