Beispiel #1
0
void EffectEvade::unaffect(Creature* pCreature)
	throw(Error)
{
	__BEGIN_TRY

	//cout << "EffectEvade" << "unaffect BEGIN" << endl;

	Assert(pCreature != NULL);
	Assert(pCreature->isOusters());

	// 플래그를 끈다.
	pCreature->removeFlag(Effect::EFFECT_CLASS_EVADE);

	Zone* pZone = pCreature->getZone();
	Assert(pZone != NULL);

	Ousters* pTargetOusters = dynamic_cast<Ousters*>(pCreature);
	Assert(pTargetOusters != NULL);

	OUSTERS_RECORD prev;

	pTargetOusters->getOustersRecord(prev);
	pTargetOusters->initAllStat();
	pTargetOusters->sendRealWearingInfo();
	pTargetOusters->sendModifyInfo(prev);

	// 이펙트를 삭제하라고 알려준다.
	GCRemoveEffect gcRemoveEffect;
	gcRemoveEffect.setObjectID(pCreature->getObjectID());
	gcRemoveEffect.addEffectList(Effect::EFFECT_CLASS_EVADE);
	pZone->broadcastPacket(pCreature->getX(), pCreature->getY(), &gcRemoveEffect);

	//cout << "EffectEvade" << "unaffect END" << endl;

	__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
}
Beispiel #3
0
void EffectDoom::unaffect(Creature* pCreature)
	throw(Error)
{
	__BEGIN_TRY
	__BEGIN_DEBUG

	//cout << "EffectDoom" << "unaffect BEGIN" << endl;

	Assert(pCreature != NULL);

	// 능력치를 정상적으로 되돌리기 위해서는 플래그를 끄고,
	// initAllStat을 불러야 한다.
	pCreature->removeFlag(Effect::EFFECT_CLASS_DOOM);

	if (pCreature->isVampire())
	{
		Vampire* pTargetVampire = dynamic_cast<Vampire*>(pCreature);
		VAMPIRE_RECORD prev;

		pTargetVampire->getVampireRecord(prev);
		pTargetVampire->initAllStat();
		pTargetVampire->sendRealWearingInfo();
		pTargetVampire->sendModifyInfo(prev);
    }
	else if (pCreature->isMonster())
	{
		Monster* pMonster = dynamic_cast<Monster*>(pCreature);
		pMonster->initAllStat();
	}
	else if (pCreature->isSlayer())
	{
		Slayer* pTargetSlayer = dynamic_cast<Slayer*>(pCreature);
		SLAYER_RECORD prev;

		pTargetSlayer->getSlayerRecord(prev);
		pTargetSlayer->initAllStat();
		pTargetSlayer->sendRealWearingInfo();
		pTargetSlayer->sendModifyInfo(prev);
    }
	else if (pCreature->isOusters())
	{
		Ousters* pTargetOusters = dynamic_cast<Ousters*>(pCreature);
		OUSTERS_RECORD prev;

		pTargetOusters->getOustersRecord(prev);
		pTargetOusters->initAllStat();
		pTargetOusters->sendRealWearingInfo();
		pTargetOusters->sendModifyInfo(prev);
    }
	else Assert(false);

	Zone* pZone = pCreature->getZone();
	Assert(pZone != NULL);

	GCRemoveEffect gcRemoveEffect;
	gcRemoveEffect.setObjectID(pCreature->getObjectID());
	gcRemoveEffect.addEffectList(Effect::EFFECT_CLASS_DOOM);
	pZone->broadcastPacket(pCreature->getX(), pCreature->getY(), &gcRemoveEffect);

	//cout << "EffectDoom" << "unaffect END" << endl;

	__END_DEBUG
	__END_CATCH
}
void CGUseBonusPointHandler::execute (CGUseBonusPoint* pPacket , Player* pPlayer)
	 throw(Error)
{
	__BEGIN_TRY __BEGIN_DEBUG_EX
	__BEGIN_DEBUG
		
#ifdef __GAME_SERVER__

	Assert(pPacket != NULL);
	Assert(pPlayer != NULL);

	// 정상적인 상태가 아니라면 리턴
	GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer);
	if (pGamePlayer->getPlayerStatus() != GPS_NORMAL) return;

	Creature*      pCreature  = pGamePlayer->getCreature();
	BYTE           which      = pPacket->getWhich();
	Attr_t         cur        = 0;

	if (pCreature->isSlayer() )
	{
		Slayer* pSlayer = dynamic_cast<Slayer*>(pCreature);
		if (pSlayer->getBonus() <= 0 )
		{
			GCUseBonusPointFail failPkt;
			pPlayer->sendPacket(&failPkt);
			return;
		}

		bool success = false;
		if (which == INC_INT )
		{
			success = pSlayer->putAdvancedBonusToINT();
		}
		else if (which == INC_STR)
		{
			success = pSlayer->putAdvancedBonusToSTR();
		}
		else if (which == INC_DEX)
		{
			success = pSlayer->putAdvancedBonusToDEX();
		}

		if (success )
		{
			GCUseBonusPointOK okpkt;
			pGamePlayer->sendPacket (&okpkt);

			pSlayer->saveExps();
			pSlayer->initAllStatAndSend();
		}
		else
		{
			GCUseBonusPointFail failPkt;
			pPlayer->sendPacket(&failPkt);
			return;
		}
	}
	else if (pCreature->isVampire() ) 
	{
		// 보너스 포인트가 없다면 리턴
		Vampire* pVampire = dynamic_cast<Vampire*>(pCreature);
		if (pVampire->getBonus() <= 0)
		{
			GCUseBonusPointFail failPkt;
			pPlayer->sendPacket(&failPkt);
			return;
		}

		VAMPIRE_RECORD oldRecord;
		// 능력치를 올리기 전에 기존의 능력치를 저장한다.
		pVampire->getVampireRecord(oldRecord);

		if (which == INC_INT)
		{
			cur = pVampire->getINT(ATTR_BASIC) + 1;
			pVampire->setINT(cur, ATTR_BASIC);

			StringStream sst;
			sst << "INTE = " << (int)cur;
			pVampire->tinysave(sst.toString());
			
			/*
			// INT가 증가하면 새로운 기술을 배울 수 있는 가능성이 있다.
			SkillType_t lastSkill = pVampire->findLastSkill();


			// lastSkill의 다음 level의 기술을 찾는다.
			// 못찾았다면 더이상 배울것이 없다는 것.
			for(int i = SKILL_BLOOD_DRAIN + 1 ; i < SKILL_MAX; i++)
			{
				SkillParentInfo* pParentInfo = g_pSkillParentInfoManager->getSkillParentInfo(i);

				if (pParentInfo->hasParent(lastSkill))// 찾았다!
				{
					SkillInfo* pNewSkillInfo = g_pSkillInfoManager->getSkillInfo(i);
					if (pNewSkillInfo->getEXP() <= cur && pVampire->hasSkill(i) == NULL)
					{
						//cout << "(" << pVampire->getName() << ") can learn new skill >> ";

						// 새로운 기술을 배울 수 있다.
						GCLearnSkillReady gcLSR;
						gcLSR.setSkillDomainType(SKILL_DOMAIN_VAMPIRE);
						pVampire->getPlayer()->sendPacket(&gcLSR);
						break;
					}
				}

			}
			*/

			//log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "INT");
		}
		else if (which == INC_STR)
		{
			cur = pVampire->getSTR(ATTR_BASIC) + 1;
			pVampire->setSTR(cur, ATTR_BASIC);

			StringStream sst;
			sst << "STR = " << (int)cur;
			pVampire->tinysave(sst.toString());

			//log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "STR");
		}
		else if (which == INC_DEX)
		{
			cur = pVampire->getDEX(ATTR_BASIC) + 1;
			pVampire->setDEX(cur, ATTR_BASIC);

			StringStream sst;
			sst << "DEX = " << (int)cur;
			pVampire->tinysave(sst.toString());

			//log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "DEX");
		}

		// 바뀐 보너스 포인트를 저장한다.
		Bonus_t OldBonus = pVampire->getBonus();
		pVampire->setBonus(OldBonus - 1);
		StringStream sst;
		sst << "Bonus = " << (int)(OldBonus - 1);
		pVampire->tinysave(sst.toString());

		// 능력치가 변화되었으니, stat을 새로 고친다.
		pVampire->initAllStat();
		
		// 클라이언트의 계산 순서 때문에 생기는 버그로 인하여,
		// 먼저 인증 패킷을 날려준 후에, 바뀐 능력치에 대한 정보를 보낸다.
		// 나중에 CGUseBonusPointOK에다 바로 바뀐 능력치에 대한 정보를 
		// 실어보내도록 해야 한다.

		// OK 패킷을 보내준다.
		GCUseBonusPointOK okpkt;
		pGamePlayer->sendPacket (&okpkt);

		// 바뀐 능력치에 관한 정보를 보내준다.
		pVampire->sendModifyInfo(oldRecord);
		pVampire->sendRealWearingInfo();
	}
	else if (pCreature->isOusters() ) 
	{
		// 보너스 포인트가 없다면 리턴
		Ousters* pOusters = dynamic_cast<Ousters*>(pCreature);
		if (pOusters->getBonus() <= 0)
		{
			GCUseBonusPointFail failPkt;
			pPlayer->sendPacket(&failPkt);
			return;
		}

		OUSTERS_RECORD oldRecord;
		// 능력치를 올리기 전에 기존의 능력치를 저장한다.
		pOusters->getOustersRecord(oldRecord);

		if (which == INC_INT)
		{
			cur = pOusters->getINT(ATTR_BASIC) + 1;
			pOusters->setINT(cur, ATTR_BASIC);

			StringStream sst;
			sst << "INTE = " << (int)cur;
			pOusters->tinysave(sst.toString());
		}
		else if (which == INC_STR)
		{
			cur = pOusters->getSTR(ATTR_BASIC) + 1;
			pOusters->setSTR(cur, ATTR_BASIC);

			StringStream sst;
			sst << "STR = " << (int)cur;
			pOusters->tinysave(sst.toString());
		}
		else if (which == INC_DEX)
		{
			cur = pOusters->getDEX(ATTR_BASIC) + 1;
			pOusters->setDEX(cur, ATTR_BASIC);

			StringStream sst;
			sst << "DEX = " << (int)cur;
			pOusters->tinysave(sst.toString());
		}

		// 바뀐 보너스 포인트를 저장한다.
		Bonus_t OldBonus = pOusters->getBonus();
		pOusters->setBonus(OldBonus - 1);
		StringStream sst;
		sst << "Bonus = " << (int)(OldBonus - 1);
		pOusters->tinysave(sst.toString());

		// 능력치가 변화되었으니, stat을 새로 고친다.
		pOusters->initAllStat();
		
		// OK 패킷을 보내준다.
		GCUseBonusPointOK okpkt;
		pGamePlayer->sendPacket(&okpkt);

		// 바뀐 능력치에 관한 정보를 보내준다.
		pOusters->sendModifyInfo(oldRecord);
		pOusters->sendRealWearingInfo();
	}
	else
	{
		GCUseBonusPointFail failPkt;
		pPlayer->sendPacket(&failPkt);
		return;
	}


#endif	// __GAME_SERVER__

	__END_DEBUG
	__END_DEBUG_EX __END_CATCH
}
void CGDownSkillHandler::execute (CGDownSkill* pPacket , Player* pPlayer)
throw(ProtocolException, Error)
{
    __BEGIN_TRY __BEGIN_DEBUG_EX

#ifdef __GAME_SERVER__

    Assert(pPacket != NULL);
    Assert(pPlayer != NULL);

    GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer);
    Assert(pGamePlayer != NULL);

    PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pGamePlayer->getCreature());
    Assert(pPC != NULL);

    SkillType_t targetSkillType = pPacket->getSkillType();

    GCDownSkillFailed failpkt;
    failpkt.setSkillType(targetSkillType);

    if (!pPC->isOusters() )
    {
        failpkt.setDesc(NOT_OUSTERS);
        pPlayer->sendPacket(&failpkt);

        return;
    }

    Ousters* pOusters = dynamic_cast<Ousters*>(pPC);
    Assert(pOusters != NULL);
    OustersSkillSlot* pTargetSkillSlot = pOusters->getSkill(targetSkillType);

    if (pTargetSkillSlot == NULL )
    {
        failpkt.setDesc(HAVE_NOT_SKILL);
        pPlayer->sendPacket(&failpkt);
        return;
    }

    SkillInfo* pTargetSkillInfo = NULL;

    try
    {
        pTargetSkillInfo = g_pSkillInfoManager->getSkillInfo(targetSkillType);
    }
    catch(Exception& e)
    {
        failpkt.setDesc(INVALID_SKILL);
        pPlayer->sendPacket(&failpkt);

        return;
    }

    if (pTargetSkillSlot->getExpLevel() <= 1 )
    {
        if (!pTargetSkillInfo->canDelete() )
        {
            failpkt.setDesc(TOO_LOW);
            pPlayer->sendPacket(&failpkt);
            return;
        }

        list<SkillType_t>& rRequiredSkills = pTargetSkillInfo->getRequiredSkills();
        list<SkillType_t>::iterator itr = rRequiredSkills.begin();

        for (; itr != rRequiredSkills.end(); ++itr )
        {
            if (pOusters->hasSkill(*itr) != NULL )
            {
                bool canDrop = false;
                SkillInfo* pFollowingSkillInfo = g_pSkillInfoManager->getSkillInfo(*itr);
                list<SkillType_t>& rRequireSkills = pFollowingSkillInfo->getRequireSkills();
                list<SkillType_t>::iterator itr2 = rRequireSkills.begin();
                for (; itr2 != rRequireSkills.end(); ++itr2 )
                {
                    if ((*itr2) != targetSkillType && pOusters->hasSkill(*itr2) != NULL )
                    {
                        SkillInfo* pAlternativeSkillInfo = g_pSkillInfoManager->getSkillInfo(*itr2);
                        if (getSkillMapID((ElementalDomain)pAlternativeSkillInfo->getElementalDomain()) == getSkillMapID((ElementalDomain)pTargetSkillInfo->getElementalDomain()) )
                            canDrop = true;
                    }
                }

                if (!canDrop )
                {
                    failpkt.setDesc(CANNOT_DROP_SKILL);
                    pPlayer->sendPacket(&failpkt);
                    return;
                }
            }
        }
    }

    /*	if (pTargetSkillSlot->getExpLevel() >= 30 )
    	{
    		failpkt.setDesc(TOO_HIGH);
    		pPlayer->sendPacket(&failpkt);
    		return;
    	}*/

    Assert(pTargetSkillInfo != NULL);

    int backPoint = pTargetSkillInfo->getLevelUpPoint();
    Price_t downPrice = (int)(backPoint * pow(pOusters->getLevel(),1.3) * 200);

    if (pTargetSkillSlot->getExpLevel() <= 1 )
    {
        downPrice *= 5;
        if (downPrice == 0 ) downPrice = 1000000;
    }

    if (pOusters->getGold() < downPrice )
    {
        failpkt.setDesc(NOT_ENOUGH_MONEY);
        pPlayer->sendPacket(&failpkt);

        return;
    }

    pOusters->decreaseGoldEx(downPrice);
    pTargetSkillSlot->setExpLevel(pTargetSkillSlot->getExpLevel() - 1);
    pTargetSkillSlot->save();
    if (pTargetSkillSlot->getExpLevel() <= 0 )
    {
        pTargetSkillSlot->destroy(pOusters->getName());
        backPoint = pTargetSkillInfo->getSkillPoint();
        pOusters->removeSkill(targetSkillType);
    }

    pOusters->setSkillBonus(pOusters->getSkillBonus() + backPoint);
    char query[50];
    sprintf(query, "SkillBonus=%d", pOusters->getSkillBonus());
    pOusters->tinysave(query);

    GCDownSkillOK okpkt;
    okpkt.setSkillType(targetSkillType);
    pPlayer->sendPacket(&okpkt);

    GCModifyInformation gcMI;
    gcMI.addLongData(MODIFY_GOLD, pOusters->getGold());
    gcMI.addShortData(MODIFY_SKILL_BONUS_POINT, pOusters->getSkillBonus());

    switch (targetSkillType )
    {
    case SKILL_HIDE_SIGHT:
    {
        OUSTERS_RECORD prev;
        pOusters->getOustersRecord(prev);
        pOusters->initAllStat();
        pOusters->sendRealWearingInfo();
        pOusters->addModifyInfo(prev, gcMI);
    }
    break;

    default :
        break;
    }

    pPlayer->sendPacket(&gcMI);

#endif	// __GAME_SERVER__

    __END_DEBUG_EX __END_CATCH
}
//////////////////////////////////////////////////////////////////////////////
// 일반 아이템을 처리한다.
//////////////////////////////////////////////////////////////////////////////
void CGRequestRepairHandler::executeNormal (CGRequestRepair* pPacket , Player* pPlayer)
	 throw(ProtocolException , Error)
{
	__BEGIN_TRY __BEGIN_DEBUG_EX

#ifdef __GAME_SERVER__

	ObjectID_t    ITEMOID       = pPacket->getObjectID();
	Creature*     pPC           = dynamic_cast<GamePlayer*>(pPlayer)->getCreature();
	bool          bSlayer       = false;
	bool          bVampire      = false;
	bool          bOusters      = false;
	Gold_t        playerMoney   = 0;
	Price_t       repairPrice   = 0;
	Item*         pItem         = NULL;
	Slayer*       pSlayer       = NULL;
	Vampire*      pVampire      = NULL;
	Ousters*      pOusters      = NULL;
	int           storage       = 0;
	int           X             = 0;
	int           Y             = 0;
	GCNPCResponse response;

	// 플레이어가 슬레이어인지 뱀파이어인지 구분.
	if (pPC->isSlayer())       bSlayer = true;
	else if (pPC->isVampire()) bVampire = true;
	else if (pPC->isOusters()) bOusters = true;

	// 플레이어가 수리하려고 하는 아이템을 가지고 있는지 검사
	if (bSlayer)
	{
		pSlayer     = dynamic_cast<Slayer*>(pPC);
		playerMoney = pSlayer->getGold();
		pItem       = pSlayer->findItemOID(ITEMOID, storage, X, Y);
	}
	else if (bVampire)
	{
		pVampire    = dynamic_cast<Vampire*>(pPC);
		playerMoney = pVampire->getGold();
		pItem       = pVampire->findItemOID(ITEMOID, storage, X, Y);
	}
	else if (bOusters)
	{
		pOusters    = dynamic_cast<Ousters*>(pPC);
		playerMoney = pOusters->getGold();
		pItem       = pOusters->findItemOID(ITEMOID, storage, X, Y);
	}

	// 플레이어가 수리하려고 하는 아이템을 가지고 있는지 
	// 상위에서 검사를 하기 때문에, pItem이 널일리는 없다.
	// 단, 수리할 수 없는 아이템인지를 검사한다.
	if (isRepairableItem(pItem) == false)
	{
		response.setCode(NPC_RESPONSE_REPAIR_FAIL_ITEM_TYPE);
		pPlayer->sendPacket(&response);
		return;
	}

	// 이전 내구도를 저장한다.
	Durability_t oldDurability = pItem->getDurability();

	repairPrice = g_pPriceManager->getRepairPrice(pItem);

	if (repairPrice > playerMoney)
	{
		response.setCode(NPC_RESPONSE_REPAIR_FAIL_MONEY);
		pPlayer->sendPacket(&response);
		return;
	}

	// 수리한다.
	repairItem(pItem);

	// 수리한 아이템이 기어창의 아이템이고 이전 내구도가 0 이었다면 정보를 새로 보내줘야한다.
	if (storage == STORAGE_GEAR && oldDurability == 0 )
	{
		if (bSlayer && pSlayer != NULL )
		{
			pSlayer->initAllStatAndSend();
			pSlayer->sendRealWearingInfo();
		}
		else if (bVampire && pVampire != NULL )
		{
			pVampire->initAllStatAndSend();
			pVampire->sendRealWearingInfo();
		}
		else if (bOusters && pOusters != NULL )
		{
			pOusters->initAllStatAndSend();
			pOusters->sendRealWearingInfo();
		}
	}

	// 돈을 줄인다.
	if (bSlayer)
	{
		//pSlayer->setGoldEx(playerMoney-repairPrice);
		// by sigi. 2002.9.4
		pSlayer->decreaseGoldEx(repairPrice);
		//log(LOG_REPAIR_ITEM, pSlayer->getName(), "", pItem->toString());
	}
	else if (bVampire)
	{
		// by sigi. 2002.9.4
		pVampire->decreaseGoldEx(repairPrice);
		//log(LOG_REPAIR_ITEM, pVampire->getName(), "", pItem->toString());
	}
	else if (bOusters)
	{
		// by sigi. 2002.9.4
		pOusters->decreaseGoldEx(repairPrice);
		//log(LOG_REPAIR_ITEM, pOusters->getName(), "", pItem->toString());
	}

	// 아이템을 수리했다는 정보를 DB에다가 저장해준다.
	// 단 분명히 STORAGE_STASH가 돌아올 수 있지만, 
	// 보관함에 있는 것을 수리한다는 것은 말이 안 되므로,
	// 저장하지 않는다.


	// item저장 최적화. by sigi. 2002.5.17
	if (repairPrice>0)
	{
		char pField[80];

		if (pItem->getItemClass()==Item::ITEM_CLASS_SLAYER_PORTAL_ITEM)
		{
			SlayerPortalItem* pSPItem = dynamic_cast<SlayerPortalItem*>(pItem);
			sprintf(pField, "Charge=%d", pSPItem->getCharge());
		}
		else if (pItem->getItemClass()==Item::ITEM_CLASS_OUSTERS_SUMMON_ITEM)
		{
			OustersSummonItem* pOSItem = dynamic_cast<OustersSummonItem*>(pItem);
			sprintf(pField, "Charge=%d", pOSItem->getCharge());
		}
		else
		{
			sprintf(pField, "Durability=%d", pItem->getDurability());
		}

		pItem->tinysave(pField);
	}

	/*
	// 뭐가 됐든.. durability만 바꾸면 된다.
	// 근데.. ItemObject에 Durability field가 없는 것도 있고
	// Charge를 저장해야 하는 것도 있다.
	// 그래서.. 일단은 모두 다 저장하는 save를 이용하도록 한다.
	switch (storage)
	{
		case STORAGE_INVENTORY:
		{
			pItem->save(pPC->getName(), STORAGE_INVENTORY, 0, X, Y);
		}
		break;

		case STORAGE_GEAR:
		{
			if (bSlayer) 
			{
				pItem->save(pSlayer->getName(),  STORAGE_GEAR, 0, X, 0);
			}
			else         
			{
				pItem->save(pVampire->getName(), STORAGE_GEAR, 0, X, 0);
			}
		}
		break;

		default:
			break;
	}
	*/

	// OK 패킷을 날려준다.
	response.setCode(NPC_RESPONSE_REPAIR_OK);
	response.setParameter(playerMoney-repairPrice);
	pPlayer->sendPacket(&response);

#endif

	__END_DEBUG_EX __END_CATCH
}
//////////////////////////////////////////////////////////////////////////////
// 모든 아이템 수리하기
//////////////////////////////////////////////////////////////////////////////
void CGRequestRepairHandler::executeAll(CGRequestRepair* pPacket , Player* pPlayer)
	 throw(ProtocolException , Error)
{
	__BEGIN_TRY __BEGIN_DEBUG_EX

#ifdef __GAME_SERVER__

	Creature*     pPC         = dynamic_cast<GamePlayer*>(pPlayer)->getCreature();
	Price_t       repairPrice = 0;
	GCNPCResponse response;

	bool bSendRealWearingInfo = false;

	if (pPC->isSlayer())
	{
		Slayer* pSlayer = dynamic_cast<Slayer*>(pPC);

		// 모든 아이템을 합한 수리비를 계산한다.
		for (int i=0; i<Slayer::WEAR_MAX; i++)
		{
			Item* pItem = pSlayer->getWearItem((Slayer::WearPart)i);
			if (pItem != NULL)
			{
				if (i == Slayer::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 오른손이고, 현재 들고 있는 무기가 양손 무기라면...
					// 수리 가격에 포함시킬 필요가 없다.
				}
				else
				{
					repairPrice += g_pPriceManager->getRepairPrice(pItem);
				}
			}
		}

		// 돈이 모자라다면 리턴한다.
		if (pSlayer->getGold() < repairPrice)
		{
			response.setCode(NPC_RESPONSE_REPAIR_FAIL_MONEY);
			pPlayer->sendPacket(&response);
			return;
		}

		// 각각의 아이템을 수리하고, DB에 저장한다.
		char pField[80];

		for (int i=0; i<Slayer::WEAR_MAX; i++)
		{
			Item* pItem = pSlayer->getWearItem((Slayer::WearPart)i);
			if (pItem != NULL)
			{
				if (i == Slayer::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 오른손이고, 현재 들고 있는 무기가 양손 무기라면...
					// 수리할 필요가 없다.
				}
				else if (isRepairableItem(pItem ) )
				{
					Durability_t oldDurability = pItem->getDurability();
					repairItem(pItem);
					if (pItem->getDurability() != oldDurability)
					{
						// DB 쿼리를 줄이기 위해서
						// 내구도의 변화가 생긴 경우에만 세이브한다.
						//pItem->save(pSlayer->getName(), STORAGE_GEAR, 0, i, 0);
						// item저장 최적화. by sigi. 2002.5.13
						sprintf(pField, "Durability=%d", pItem->getDurability());
						pItem->tinysave(pField);
					}

					if (oldDurability == 0 )
						bSendRealWearingInfo = true;
				}
			}
		}

		// 돈을 줄이고...
		//pSlayer->setGoldEx(pSlayer->getGold() - repairPrice);

		// by sigi.2002.9.4
		pSlayer->decreaseGoldEx(repairPrice);

		// 로그를 남긴다.
		//log(LOG_REPAIR_ITEM, pSlayer->getName(), "", "ALL");

		// OK 패킷을 날려준다.
		response.setCode(NPC_RESPONSE_REPAIR_OK);
		response.setParameter(pSlayer->getGold());
		pPlayer->sendPacket(&response);
	}
	else if (pPC->isVampire())
	{
		Vampire* pVampire = dynamic_cast<Vampire*>(pPC);

		// 모든 아이템을 합한 수리비를 계산한다.
		for (int i=0; i<Vampire::VAMPIRE_WEAR_MAX; i++)
		{
			Item* pItem = pVampire->getWearItem((Vampire::WearPart)i);
			if (pItem != NULL) 
			{
				if (i == Vampire::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 양손무기는 한쪽만 수리한다.
				}
				else
				{
					repairPrice += g_pPriceManager->getRepairPrice(pItem);
				}
			}
		}

		// 돈이 모자라다면 리턴한다.
		if (pVampire->getGold() < repairPrice)
		{
			response.setCode(NPC_RESPONSE_REPAIR_FAIL_MONEY);
			pPlayer->sendPacket(&response);
			return;
		}
 
		// 각각의 아이템을 수리하고, DB에 저장한다.
		char pField[80];

		for (int i=0; i<Vampire::VAMPIRE_WEAR_MAX; i++)
		{
			Item* pItem = pVampire->getWearItem((Vampire::WearPart)i);
			if (pItem != NULL)
			{
				if (i == Vampire::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 양손무기는 한쪽만 수리한다.
				}
				else
				{
					Durability_t oldDurability = pItem->getDurability();
					repairItem(pItem);
					if (pItem->getDurability() != oldDurability)
					{
						// DB 쿼리를 줄이기 위해서
						// 내구도의 변화가 생긴 경우에만 세이브한다.
						//pItem->save(pVampire->getName(), STORAGE_GEAR, 0, i, 0);
						// item저장 최적화. by sigi. 2002.5.13
						sprintf(pField, "Durability=%d", pItem->getDurability());
						pItem->tinysave(pField);

					}

					if (oldDurability == 0 )
						bSendRealWearingInfo = true;
				}
			}
		}

		// 돈을 줄이고...
		//pVampire->setGoldEx(pVampire->getGold() - repairPrice);
		// by sigi.2002.9.4
		pVampire->decreaseGoldEx(repairPrice);

		// 로그를 남긴다.
		//log(LOG_REPAIR_ITEM, pVampire->getName(), "", "ALL");

		// OK 패킷을 날려준다.
		response.setCode(NPC_RESPONSE_REPAIR_OK);
		response.setParameter(pVampire->getGold());
		pPlayer->sendPacket(&response);
	}
	else if (pPC->isOusters())
	{
		Ousters* pOusters = dynamic_cast<Ousters*>(pPC);

		// 모든 아이템을 합한 수리비를 계산한다.
		for (int i=0; i<Ousters::OUSTERS_WEAR_MAX; i++)
		{
			Item* pItem = pOusters->getWearItem((Ousters::WearPart)i);
			if (pItem != NULL) 
			{
				if (i == Ousters::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 양손무기는 한쪽만 수리한다.
				}
				else
				{
					repairPrice += g_pPriceManager->getRepairPrice(pItem);
				}
			}
		}

		// 돈이 모자라다면 리턴한다.
		if (pOusters->getGold() < repairPrice)
		{
			response.setCode(NPC_RESPONSE_REPAIR_FAIL_MONEY);
			pPlayer->sendPacket(&response);
			return;
		}
 
		// 각각의 아이템을 수리하고, DB에 저장한다.
		char pField[80];

		for (int i=0; i<Ousters::OUSTERS_WEAR_MAX; i++)
		{
			Item* pItem = pOusters->getWearItem((Ousters::WearPart)i);
			if (pItem != NULL)
			{
				if (i == Ousters::WEAR_RIGHTHAND && isTwohandWeapon(pItem))
				{
					// 양손무기는 한쪽만 수리한다.
				}
				else
				{
					Durability_t oldDurability = pItem->getDurability();
					repairItem(pItem);
					if (pItem->getDurability() != oldDurability)
					{
						// DB 쿼리를 줄이기 위해서
						// 내구도의 변화가 생긴 경우에만 세이브한다.
						//pItem->save(pOusters->getName(), STORAGE_GEAR, 0, i, 0);
						// item저장 최적화. by sigi. 2002.5.13
						sprintf(pField, "Durability=%d", pItem->getDurability());
						pItem->tinysave(pField);
					}

					if (oldDurability == 0 )
						bSendRealWearingInfo = true;
				}
			}
		}

		// 돈을 줄이고...
		//pOusters->setGoldEx(pOusters->getGold() - repairPrice);
		// by sigi.2002.9.4
		pOusters->decreaseGoldEx(repairPrice);

		// 로그를 남긴다.
		//log(LOG_REPAIR_ITEM, pOusters->getName(), "", "ALL");

		// OK 패킷을 날려준다.
		response.setCode(NPC_RESPONSE_REPAIR_OK);
		response.setParameter(pOusters->getGold());
		pPlayer->sendPacket(&response);
	}

	if (bSendRealWearingInfo )
	{
		if (pPC->isSlayer() )
		{
			Slayer* pSlayer = dynamic_cast<Slayer*>(pPC);
			Assert(pSlayer != NULL);

			pSlayer->initAllStatAndSend();
			pSlayer->sendRealWearingInfo();
		}
		else if (pPC->isVampire() )
		{
			Vampire* pVampire = dynamic_cast<Vampire*>(pPC);
			Assert(pVampire != NULL);

			pVampire->initAllStatAndSend();
			pVampire->sendRealWearingInfo();
		}
		else if (pPC->isOusters() )
		{
			Ousters* pOusters = dynamic_cast<Ousters*>(pPC);
			Assert(pOusters != NULL);

			pOusters->initAllStatAndSend();
			pOusters->sendRealWearingInfo();
		}
	}

#endif

	__END_DEBUG_EX __END_CATCH
}