////////////////////////////////////////////////////////////////////////////////
// 액션을 실행한다.
////////////////////////////////////////////////////////////////////////////////
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
}
Example #4
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("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);
}