void EntityManager::HandleUserAction(MsgEntry* me, Client *client) { psUserActionMessage actionMsg(me); csString action; if (!actionMsg.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psUserActionMessage from client id %u.",me->clientnum); return; } gemObject *object = gem->FindObject(actionMsg.target); if (actionMsg.target.IsValid() && !object) { Debug2(LOG_ANY, me->clientnum, "User action on unknown entity (%s)!", ShowID(actionMsg.target)); return; } client->SetTargetObject(object); // have special tracking for this for fast processing of other messages if (!object) { // TODO: Evaluate if this output is needed. Debug2(LOG_ANY, me->clientnum, "User action on none or unknown object (%s)!", ShowID(actionMsg.target)); return; } // Resolve default behaviour action = actionMsg.action; if (action == "dfltBehavior") { action = object->GetDefaultBehavior(actionMsg.dfltBehaviors); if (action.IsEmpty()) { return; } } if (action == "select" || action == "context") { if (object != NULL) object->SendTargetStatDR(client); } Debug4(LOG_USER,client->GetClientNum(), "User Action: %s %s %s",client->GetName(), (const char *)action, (object)?object->GetName():"None") object->SendBehaviorMessage(action, client->GetActor() ); }
/** * Informs a player of what happened around the character. */ static void informPlayer(MapComposite *map, Entity *p) { MessageOut moveMsg(GPMSG_BEINGS_MOVE); MessageOut damageMsg(GPMSG_BEINGS_DAMAGE); const Point &pold = p->getComponent<BeingComponent>()->getOldPosition(); const Point &ppos = p->getComponent<ActorComponent>()->getPosition(); int pflags = p->getComponent<ActorComponent>()->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) { Entity *o = *it; const Point &oold = o->getComponent<BeingComponent>()->getOldPosition(); const Point &opos = o->getComponent<ActorComponent>()->getPosition(); int otype = o->getType(); int oid = o->getComponent<ActorComponent>()->getPublicID(); int oflags = o->getComponent<ActorComponent>()->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 action change messages. if ((oflags & UPDATEFLAG_ACTIONCHANGE)) { MessageOut actionMsg(GPMSG_BEING_ACTION_CHANGE); actionMsg.writeInt16(oid); actionMsg.writeInt8( o->getComponent<BeingComponent>()->getAction()); gameHandler->sendTo(p, actionMsg); } // Send looks change messages. if (oflags & UPDATEFLAG_LOOKSCHANGE) { MessageOut looksMsg(GPMSG_BEING_LOOKS_CHANGE); looksMsg.writeInt16(oid); serializeLooks(o, looksMsg); gameHandler->sendTo(p, looksMsg); } // Send emote messages. if (oflags & UPDATEFLAG_EMOTE) { int emoteId = o->getComponent<BeingComponent>()->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 && o != p) { MessageOut dirMsg(GPMSG_BEING_DIR_CHANGE); dirMsg.writeInt16(oid); dirMsg.writeInt8( o->getComponent<BeingComponent>()->getDirection()); gameHandler->sendTo(p, dirMsg); } // Send ability uses if (oflags & UPDATEFLAG_ABILITY_ON_POINT) { MessageOut abilityMsg(GPMSG_BEING_ABILITY_POINT); abilityMsg.writeInt16(oid); auto *abilityComponent = o->getComponent<AbilityComponent>(); const Point &point = abilityComponent->getLastTargetPoint(); abilityMsg.writeInt8(abilityComponent->getLastUsedAbilityId()); abilityMsg.writeInt16(point.x); abilityMsg.writeInt16(point.y); gameHandler->sendTo(p, abilityMsg); } if (oflags & UPDATEFLAG_ABILITY_ON_BEING) { MessageOut abilityMsg(GPMSG_BEING_ABILITY_BEING); abilityMsg.writeInt16(oid); auto *abilityComponent = o->getComponent<AbilityComponent>(); abilityMsg.writeInt8(abilityComponent->getLastUsedAbilityId()); abilityMsg.writeInt16( abilityComponent->getLastTargetBeingId()); gameHandler->sendTo(p, abilityMsg); } if (oflags & UPDATEFLAG_ABILITY_ON_DIRECTION) { MessageOut abilityMsg(GPMSG_BEING_ABILITY_DIRECTION); abilityMsg.writeInt16(oid); auto *abilityComponent = o->getComponent<AbilityComponent>(); abilityMsg.writeInt8(abilityComponent->getLastUsedAbilityId()); abilityMsg.writeInt8( abilityComponent->getLastTargetDirection()); gameHandler->sendTo(p, abilityMsg); } // Send damage messages. if (o->canFight()) { auto *beingComponent = o->getComponent<BeingComponent>(); const Hits &hits = beingComponent->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(o->getComponent<BeingComponent>()->getAction()); enterMsg.writeInt16(opos.x); enterMsg.writeInt16(opos.y); enterMsg.writeInt8( o->getComponent<BeingComponent>()->getDirection()); enterMsg.writeInt8(o->getComponent<BeingComponent>()->getGender()); switch (otype) { case OBJECT_CHARACTER: { enterMsg.writeString( o->getComponent<BeingComponent>()->getName()); serializeLooks(o, enterMsg); } break; case OBJECT_MONSTER: { MonsterComponent *monsterComponent = o->getComponent<MonsterComponent>(); enterMsg.writeInt16(monsterComponent->getSpecy()->getId()); enterMsg.writeString( o->getComponent<BeingComponent>()->getName()); } break; case OBJECT_NPC: { NpcComponent *npcComponent = o->getComponent<NpcComponent>(); enterMsg.writeInt16(npcComponent->getNpcId()); enterMsg.writeString( o->getComponent<BeingComponent>()->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. auto *tpsSpeedAttribute = attributeManager->getAttributeInfo(ATTR_MOVE_SPEED_TPS); moveMsg.writeInt8((unsigned short) (o->getComponent<BeingComponent>() ->getModifiedAttribute(tpsSpeedAttribute) * 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->getComponent<CharacterComponent>()->sendStatus(*p); // Inform client about health change of party members for (CharacterIterator i(map->getWholeMapIterator()); i; ++i) { Entity *c = *i; // Make sure its not the same character if (c == p) continue; // make sure they are in the same party if (c->getComponent<CharacterComponent>()->getParty() == p->getComponent<CharacterComponent>()->getParty()) { int cflags = c->getComponent<ActorComponent>()->getUpdateFlags(); if (cflags & UPDATEFLAG_HEALTHCHANGE) { auto *beingComponent = c->getComponent<BeingComponent>(); MessageOut healthMsg(GPMSG_BEING_HEALTH_CHANGE); healthMsg.writeInt16( c->getComponent<ActorComponent>()->getPublicID()); auto *hpAttribute = attributeManager->getAttributeInfo(ATTR_HP); healthMsg.writeInt16( beingComponent->getModifiedAttribute(hpAttribute)); auto *maxHpAttribute = attributeManager->getAttributeInfo(ATTR_MAX_HP); healthMsg.writeInt16( beingComponent->getModifiedAttribute(maxHpAttribute)); 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) { Entity *o = *it; assert(o->getType() == OBJECT_ITEM || o->getType() == OBJECT_EFFECT); Point opos = o->getComponent<ActorComponent>()->getPosition(); int oflags = o->getComponent<ActorComponent>()->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>(); // Don't show old effects if (!(oflags & UPDATEFLAG_NEW_ON_MAP)) break; if (Entity *b = e->getBeing()) { auto *actorComponent = b->getComponent<ActorComponent>(); MessageOut effectMsg(GPMSG_CREATE_EFFECT_BEING); effectMsg.writeInt16(e->getEffectId()); effectMsg.writeInt16(actorComponent->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); }
bool CMissiveOMat::EnterViewMsg(CEnterViewMsg *msg) { CMissiveOMatActionMsg actionMsg(9); actionMsg.execute(this); return true; }