//////////////////////////////////////////////////////////////////////////////// // 액션을 실행한다. //////////////////////////////////////////////////////////////////////////////// void ActionInitSimpleQuest::execute (Creature * pCreature1 , Creature * pCreature2) throw(Error) { __BEGIN_TRY Assert(pCreature1 != NULL); Assert(pCreature1->isNPC()); NPC* pNPC = dynamic_cast<NPC*>(pCreature1); SimpleQuestInfoManager* pQIM = new SimpleQuestInfoManager(pNPC); pQIM->load(pNPC->getName()); pNPC->setQuestInfoManager(pQIM); SimpleQuestRewardManager* pRIM = new SimpleQuestRewardManager(pNPC); pRIM->load(pNPC->getName()); pNPC->setRewardClassInfoManager(pRIM); __END_CATCH }
void CGShopRequestSellHandler::executeOpSwapAdvancementItem(CGShopRequestSell* pPacket, Player* pPlayer) throw(ProtocolException, Error) { __BEGIN_TRY #ifdef __GAME_SERVER__ ObjectID_t NPCID = pPacket->getObjectID(); ObjectID_t ITEMOID = pPacket->getItemObjectID(); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); Creature* pCreature = pGamePlayer->getCreature(); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); if (!pPC->isAdvanced() ) { return sendFailPacket(pPacket, pPlayer); } Zone* pZone = pPC->getZone(); if (pZone == NULL) return sendFailPacket(pPacket, pPlayer); Creature* pNPCBase = pZone->getCreature(NPCID); if (pNPCBase == NULL || !pNPCBase->isNPC()) return sendFailPacket(pPacket, pPlayer); NPC* pNPC = dynamic_cast<NPC*>(pNPCBase); Inventory* pInventory = pPC->getInventory(); Item* pItem = pInventory->getItemWithObjectID(ITEMOID); if (pItem == NULL || pItem->isTimeLimitItem() ) { return sendFailPacket(pPacket, pPlayer); } Item::ItemClass iClass = Item::ITEM_CLASS_MAX; ItemType_t iType = 0; Grade_t iGrade = 0; FlagSetType fType = FLAGSET_MAX; ItemInfo* pItemInfo = g_pItemInfoManager->getItemInfo(pItem->getItemClass(), pItem->getItemType()); Assert(pItemInfo!=NULL); switch (pItem->getItemClass() ) { case Item::ITEM_CLASS_SWORD: case Item::ITEM_CLASS_BLADE: case Item::ITEM_CLASS_AR: case Item::ITEM_CLASS_SR: case Item::ITEM_CLASS_SG: case Item::ITEM_CLASS_SMG: { switch (pItem->getItemType() ) { case 9: { iClass = pItem->getItemClass(); iType = 14; iGrade = 2; break; } case 11: { iClass = pItem->getItemClass(); iType = 14; iGrade = 4; break; } case 12: { iClass = pItem->getItemClass(); iType = 14; iGrade = 6; break; } case 13: { iClass = pItem->getItemClass(); iType = 14; iGrade = 8; break; } default: break; } if (iClass == Item::ITEM_CLASS_SG ) iClass = Item::ITEM_CLASS_SR; if (iClass == Item::ITEM_CLASS_SMG ) iClass = Item::ITEM_CLASS_AR; break; } case Item::ITEM_CLASS_CROSS: case Item::ITEM_CLASS_MACE: { switch (pItem->getItemType() ) { case 7: { iClass = pItem->getItemClass(); iType = 12; iGrade = 2; break; } case 9: { iClass = pItem->getItemClass(); iType = 12; iGrade = 4; break; } case 10: { iClass = pItem->getItemClass(); iType = 12; iGrade = 6; break; } case 11: { iClass = pItem->getItemClass(); iType = 12; iGrade = 8; break; } default: break; } break; } case Item::ITEM_CLASS_COAT: case Item::ITEM_CLASS_TROUSER: { switch (pItem->getItemType() ) { case 14: case 15: { iClass = pItem->getItemClass(); iType = 24 + (pItem->getItemType()%2); iGrade = 2; break; } case 18: case 19: case 20: case 21: case 22: case 23: { iClass = pItem->getItemClass(); iType = 24 + (pItem->getItemType()%2); iGrade = 4 + (((pItem->getItemType()-18)/2)*2); break; } default: break; } break; } case Item::ITEM_CLASS_VAMPIRE_WEAPON: { switch (pItem->getItemType() ) { case 14: { iClass = pItem->getItemClass(); iType = 19; iGrade = 2; break; } case 16: { iClass = pItem->getItemClass(); iType = 19; iGrade = 4; break; } case 17: { iClass = pItem->getItemClass(); iType = 19; iGrade = 6; break; } case 18: { iClass = pItem->getItemClass(); iType = 19; iGrade = 8; break; } default: break; } break; } case Item::ITEM_CLASS_VAMPIRE_COAT: { switch (pItem->getItemType() ) { case 10: case 11: { iClass = pItem->getItemClass(); iType = 20 + (pItem->getItemType()%2); iGrade = 2; break; } case 14: case 15: case 16: case 17: case 18: case 19: { iClass = pItem->getItemClass(); iType = 20 + (pItem->getItemType()%2); iGrade = 4 + (((pItem->getItemType()-14)/2)*2); break; } default: break; } break; } case Item::ITEM_CLASS_OUSTERS_CHAKRAM: { switch (pItem->getItemType() ) { case 9: { iClass = pItem->getItemClass(); iType = 14; iGrade = 2; break; } case 10: { iClass = pItem->getItemClass(); iType = 14; iGrade = 4; break; } case 11: { iClass = pItem->getItemClass(); iType = 14; iGrade = 6; break; } case 13: { iClass = pItem->getItemClass(); iType = 14; iGrade = 8; break; } default: break; } break; } case Item::ITEM_CLASS_OUSTERS_WRISTLET: { switch (pItem->getItemType() ) { case 9: case 30: case 31: case 39: { iClass = pItem->getItemClass(); iType = 42; iGrade = pItemInfo->getItemLevel() * 2 - 16; break; } case 19: case 32: case 33: case 40: { iClass = pItem->getItemClass(); iType = 43; iGrade = pItemInfo->getItemLevel() * 2 - 16; break; } case 29: case 34: case 35: case 41: { iClass = pItem->getItemClass(); iType = 44; iGrade = pItemInfo->getItemLevel() * 2 - 16; break; } default: break; } break; } case Item::ITEM_CLASS_OUSTERS_COAT: case Item::ITEM_CLASS_OUSTERS_BOOTS: { switch (pItem->getItemType() ) { case 7: { iClass = pItem->getItemClass(); iType = 12; iGrade = 2; break; } case 8: { iClass = pItem->getItemClass(); iType = 12; iGrade = 4; break; } case 9: { iClass = pItem->getItemClass(); iType = 12; iGrade = 6; break; } case 11: { iClass = pItem->getItemClass(); iType = 12; iGrade = 8; break; } default: break; } break; } default: break; } switch (iClass ) { case Item::ITEM_CLASS_SWORD: case Item::ITEM_CLASS_BLADE: case Item::ITEM_CLASS_AR: case Item::ITEM_CLASS_SR: case Item::ITEM_CLASS_CROSS: case Item::ITEM_CLASS_MACE: case Item::ITEM_CLASS_VAMPIRE_WEAPON: case Item::ITEM_CLASS_OUSTERS_CHAKRAM: case Item::ITEM_CLASS_OUSTERS_WRISTLET: fType = FLAGSET_SWAP_WEAPON; break; case Item::ITEM_CLASS_COAT: case Item::ITEM_CLASS_VAMPIRE_COAT: case Item::ITEM_CLASS_OUSTERS_COAT: fType = FLAGSET_SWAP_COAT; break; case Item::ITEM_CLASS_TROUSER: case Item::ITEM_CLASS_OUSTERS_BOOTS: fType = FLAGSET_SWAP_TROUSER; break; default: break; } if (iClass == Item::ITEM_CLASS_MAX || fType == FLAGSET_MAX ) { sendFailPacket(pPacket, pPlayer); return; } FlagSet* pFlagSet = pPC->getFlagSet(); if (pFlagSet->isOn(fType) ) { sendFailPacket(pPacket, pPlayer); return; } Item* pNewItem = g_pItemFactoryManager->createItem(iClass, iType, pItem->getOptionTypeList()); if (pNewItem == NULL ) { sendFailPacket(pPacket, pPlayer); return; } pNewItem->setGrade(iGrade); _TPOINT tp; if (!pInventory->getEmptySlot(pNewItem, tp) ) { SAFE_DELETE(pNewItem); sendFailPacket(pPacket, pPlayer); return; } filelog("ItemSwap.log", "[%s:%s] %s <-> %s", pGamePlayer->getID().c_str(), pPC->getName().c_str(), pItem->toString().c_str(), pNewItem->toString().c_str()); pNewItem->setTraceItem(bTraceLog(pNewItem )); pInventory->deleteItem(ITEMOID); pItem->whenPCLost(pPC); if (!pItem->destroy()) { filelog("shopDBBug.txt", "NoSuchItemInDB-destroy: %s", pItem->toString().c_str()); throw DisconnectException("아이템 지울려는데 DB에 없다."); } pZone->registerObject(pNewItem); GCShopSellOK okpkt; okpkt.setObjectID(NPCID); okpkt.setShopVersion(-1); okpkt.setItemObjectID(ITEMOID); okpkt.setPrice(0); pGamePlayer->sendPacket(&okpkt); // 삭제할 아이템의 ItemTrace Log 를 남겨야 한다면 남긴다 if (pItem != NULL && pItem->isTraceItem() ) { remainTraceLog(pItem, pCreature->getName() , pNPC->getName(), ITEM_LOG_DELETE, DETAIL_SHOPSELL); } // 인벤토리에 추가 Assert(pInventory->addItem(pNewItem, tp )); // DB 에 생성 pNewItem->create(pPC->getName(), STORAGE_INVENTORY, 0, tp.x, tp.y); // 교환해줄 아이템의 ItemTrace Log 를 남겨야 한다면 남긴다 if (pNewItem->isTraceItem() ) { remainTraceLog(pNewItem, pNPC->getName() , pCreature->getName(), ITEM_LOG_CREATE, DETAIL_SHOPBUY); } pFlagSet->turnOn(fType); pFlagSet->save(pPC->getName()); GCCreateItem gcCI; makeGCCreateItem(&gcCI, pNewItem, tp.x, tp.y); pGamePlayer->sendPacket(&gcCI); #endif __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // // 일반 아이템을 처리한다. // ////////////////////////////////////////////////////////////////////////////// void CGShopRequestSellHandler::executeNormal (CGShopRequestSell* pPacket , Player* pPlayer) throw(ProtocolException , Error) { __BEGIN_TRY __BEGIN_DEBUG_EX #ifdef __GAME_SERVER__ ObjectID_t NPCID = pPacket->getObjectID(); ObjectID_t ITEMOID = pPacket->getItemObjectID(); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); Creature* pCreature = pGamePlayer->getCreature(); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); BYTE index = 0; bool bSpecialItem = false; Zone* pZone = pPC->getZone(); if (pZone == NULL) return sendFailPacket(pPacket, pPlayer); Creature* pNPCBase = NULL; /* try { pNPCBase = pZone->getCreature(NPCID); } catch (NoSuchElementException & nsee) { pNPCBase = NULL; } */ // NoSuch제거. by sigi. 2002.5.2 pNPCBase = pZone->getCreature(NPCID); if (pNPCBase == NULL || !pNPCBase->isNPC()) return sendFailPacket(pPacket, pPlayer); NPC* pNPC = dynamic_cast<NPC*>(pNPCBase); // 플레이어가 팔려고 하는 아이템을 가지고 있는지 검사 Inventory* pInventory = pPC->getInventory(); //Gold_t playerMoney = pPC->getGold(); Item* pItem = pInventory->getItemWithObjectID(ITEMOID); ItemNum_t itemNumber = pItem->getNum(); Price_t itemPrice = g_pPriceManager->getPrice(pItem, pNPC->getMarketCondBuy(), SHOP_RACK_NORMAL, pPC) * itemNumber; // 플레이어의 인벤토리에 아이템을 제거한다. pInventory->deleteItem(ITEMOID); pItem->whenPCLost(pPC); if (!pItem->destroy()) { filelog("shopDBBug.txt", "NoSuchItemInDB-destroy: %s", pItem->toString().c_str()); throw DisconnectException("아이템 지울려는데 DB에 없다."); } // 만약 벨트라면 안에 있는 포션을 삭제해준다. // DB에서 지우는 것은 Belt::destroy()를 부르는 것만으로 포션까지 삭제된다. if (pItem->getItemClass() == Item::ITEM_CLASS_BELT) { Inventory* pBeltInventory = dynamic_cast<Belt*>(pItem)->getInventory(); for (int y=0; y<pBeltInventory->getHeight(); y++) { for (int x=0; x<pBeltInventory->getWidth(); x++) { Item* pBeltItem = pBeltInventory->getItem(x, y); if (pBeltItem != NULL) { pBeltInventory->deleteItem(x, y); SAFE_DELETE(pBeltItem); } } } } // Skull 일 경우 Variable Manager 에서 머리값 배수 값으로 가격을 새로 계산한다 if (pItem->getItemClass() == Item::ITEM_CLASS_SKULL) itemPrice = itemPrice * (g_pVariableManager->getHeadPriceBonus() / 100); // ItemTrace Log 를 남겨야 한다면 남긴다 if (pItem != NULL && pItem->isTraceItem() ) remainTraceLog(pItem, pCreature->getName() , pNPC->getName(), ITEM_LOG_DELETE, DETAIL_SHOPSELL); // 플레이어에게 물건값을 지불한다. // pPC->setGoldEx(playerMoney+itemPrice); // by sigi. 2002.9.4 pPC->increaseGoldEx(itemPrice); // 플레이어가 물건 팔 때 처리할 것들을 처리한다. pPC->sellItem(pItem); if (pItem->getItemClass() == Item::ITEM_CLASS_MOON_CARD && pItem->getItemType() == 4) addOlympicStat(pPC, 4, (uint)(itemNumber)); bool bClearDefaultOptionTypes = false; if (pItem->getItemClass() == Item::ITEM_CLASS_EVENT_ITEM && pItem->getItemType() >= 32 && pItem->getItemType() <= 36) bClearDefaultOptionTypes = true; // NPC에게 자리가 충분하다면 플레이어가 판 아이템을 보관한다. // 운영자 명령어로 만든 아이템은 바로 없앤다. // 단 스페셜 아이템만을 보관한다. 노말 아이템은 그냥 버림. // 퀘스트 아이템은 보관하지 않고 버린다. if (pNPC->getShopType()==SHOPTYPE_NORMAL && pItem->getCreateType()!=Item::CREATE_TYPE_CREATE && !pItem->getOptionTypeList().empty() && !pItem->isTimeLimitItem()) { bSpecialItem = true; index = pNPC->getFirstEmptySlot(SHOP_RACK_SPECIAL); if (index < SHOP_RACK_INDEX_MAX) { // 아이템을 추가한다. pNPC->insertShopItem(SHOP_RACK_SPECIAL, index, pItem); // 스페셜 아이템을 NPC가 진열장에 추가했으므로, 상점 버전이 올라간다. pNPC->increaseShopVersion(SHOP_RACK_SPECIAL); //////////////////////////////////////////////////////////////////////////// // 근처의 플레이어들에게는 GCShopBought를... //////////////////////////////////////////////////////////////////////////// int CenterX = pNPC->getX(); int CenterY = pNPC->getY(); Creature* pNearCreature = NULL; Player* pNearPlayer = NULL; GCShopBought boughtpkt; boughtpkt.setObjectID(NPCID); if (!pItem->getOptionTypeList().empty()) { boughtpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_SPECIAL)); boughtpkt.setShopType(SHOP_RACK_SPECIAL); } else { boughtpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_NORMAL)); boughtpkt.setShopType(SHOP_RACK_NORMAL); } boughtpkt.setShopIndex(index); boughtpkt.setItemObjectID(ITEMOID); boughtpkt.setItemClass(pItem->getItemClass()); boughtpkt.setItemType(pItem->getItemType()); boughtpkt.setOptionType(pItem->getOptionTypeList()); boughtpkt.setDurability(pItem->getDurability()); boughtpkt.setSilver(pItem->getSilver()); boughtpkt.setGrade(pItem->getGrade()); boughtpkt.setEnchantLevel(pItem->getEnchantLevel()); //pZone->broadcastPacket(pNPC->getX(), pNPC->getY(), &boughtpkt, pPC); try { for (int zx=CenterX-5; zx<=CenterX+5; zx++) { for (int zy=CenterY-5; zy<=CenterY+5; zy++) { // 바운드를 넘어가지 않는가를 체크 if (!isValidZoneCoord(pZone, zx, zy)) continue; Tile & tile = pZone->getTile(zx, zy); // 걸어다니는 크리쳐를 검색 if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { pNearCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); if (pNearCreature == NULL) continue; // 방금 물건을 판 플레이어라면 생략 if (pNearCreature->getObjectID() == pPC->getObjectID()) continue; // 만약 플레이어라면 패킷을 보내준다. if (pNearCreature->isPC()) { pNearPlayer = pNearCreature->getPlayer(); if (pNearPlayer == NULL) continue; pNearPlayer->sendPacket(&boughtpkt); } } // 날아다니는 크리쳐를 검색 if (tile.hasCreature(Creature::MOVE_MODE_FLYING)) { pNearCreature = tile.getCreature(Creature::MOVE_MODE_FLYING); if (pNearCreature == NULL) continue; // 방금 물건을 판 플레이어라면 생략 if (pNearCreature->getObjectID() == pPC->getObjectID()) continue; // 만약 플레이어라면 패킷을 보내준다. if (pNearCreature->isPC()) { pNearPlayer = pNearCreature->getPlayer(); if (pNearPlayer == NULL) continue; pNearPlayer->sendPacket(&boughtpkt); } } } // end of for (ZoneCoord_t zy=CenterY-5; zy<=CenterY+5; zy++) } // end of for (ZoneCoord_t zx=CenterX-5; zx<=CenterX+5; zx++) } catch (Throwable & t) { filelog("shopbug_packet.log", "%s", t.toString().c_str()); } } // if (index < SHOP_RACK_INDEX_MAX) else { SAFE_DELETE(pItem); } } // if (pItem->getOptionType() != 0) else { bSpecialItem = false; SAFE_DELETE(pItem); } // 물건을 산 플레이어에게 GCShopSellOK를...보낸다. GCShopSellOK okpkt; okpkt.setObjectID(NPCID); if (bSpecialItem) okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_SPECIAL)); else okpkt.setShopVersion(pNPC->getShopVersion(SHOP_RACK_NORMAL)); okpkt.setItemObjectID(ITEMOID); //okpkt.setPrice(playerMoney+itemPrice); // playerMoney + itemPrice 가 MAX_MONEY를 넘어갈 수 있다. // 2003.1.8 by bezz okpkt.setPrice(pPC->getGold()); pPlayer->sendPacket(&okpkt); if (bClearDefaultOptionTypes ) { pPC->clearDefaultOptionTypes(); pPC->initAllStatAndSend(); if (pPC->isSlayer() ) { Slayer* pSlayer = dynamic_cast<Slayer*>(pPC); Assert(pSlayer != NULL); pSlayer->sendRealWearingInfo(); } else if (pPC->isVampire() ) { Vampire* pVampire = dynamic_cast<Vampire*>(pPC); Assert(pVampire != NULL); pVampire->sendRealWearingInfo(); } else if (pPC->isOusters() ) { Ousters* pOusters = dynamic_cast<Ousters*>(pPC); Assert(pOusters != NULL); pOusters->sendRealWearingInfo(); } } #endif __END_DEBUG_EX __END_CATCH }
/** * 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); }