void ZoneGroupManager::outputLoadValue() throw(Error) { //------------------------------------------------------------------ // ZoneGroup load //------------------------------------------------------------------ ofstream file("loadBalance.txt", ios::app); VSDateTime current = VSDateTime::currentDateTime(); file << current.toString() << endl; map< ZoneGroupID_t , ZoneGroup* >::const_iterator itr; for (itr = m_ZoneGroups.begin() ; itr != m_ZoneGroups.end() ; itr ++) { ZoneGroup* pZoneGroup = itr->second; file << "[" << (int)pZoneGroup->getZoneGroupID() << "] "; const map< ZoneID_t, Zone* >& zones = pZoneGroup->getZones(); map< ZoneID_t, Zone* >::const_iterator iZone; // 각 Zone의 loadValue를 구한다. int totalLoad = 0; for (iZone=zones.begin(); iZone!=zones.end(); iZone++) { Zone* pZone = iZone->second; int load = pZone->getLoadValue(); int playerLoad = pZone->getPCCount(); file << (int)pZone->getZoneID() << "(" << load << ", " << playerLoad << ") "; totalLoad += load; } file << " = " << totalLoad << endl; } file << endl; file.close(); }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 인벤토리 핸들러 ////////////////////////////////////////////////////////////////////////////// void BloodyTunnel::execute(Vampire* pVampire, ObjectID_t InvenObjectID, ObjectID_t InventoryItemObjectID, CoordInven_t X, CoordInven_t Y, CoordInven_t TargetX, CoordInven_t TargetY, VampireSkillSlot* pSkillSlot) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pVampire != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Inventory* pInventory = pVampire->getInventory(); Assert(pPlayer != NULL); Assert(pZone != NULL); Assert(pInventory!= NULL); // 전쟁 존이라면 BloodyTunnel를 사용할 수 없다. // 일단은 ZoneID로 가는데.. ZoneInfo에 넣도록 해야한다. ///* //if (pZone->getZoneID()==1122 || pZone->getZoneID()==1123) // 이벤트 경기장/OX 막기. by sigi. 2002.8.31 //int zoneID = pZone->getZoneID(); //if (zoneID==1005 || zoneID==1006) if (pZone->isNoPortalZone() || pZone->isMasterLair() || pZone->isCastle() || pZone->isHolyLand()) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } //*/ SubInventory* pInventoryItem = NULL; int invenID = 0; if (InventoryItemObjectID != 0 ) { //cout << "서브 인벤토리에서 사용 : " << InventoryItemObjectID << endl; CoordInven_t X, Y; pInventoryItem = dynamic_cast<SubInventory*>(pInventory->findItemOID(InventoryItemObjectID, X, Y )); TradeManager* pTradeManager = pZone->getTradeManager(); Assert(pTradeManager != NULL); if (pInventoryItem == NULL || pTradeManager->hasTradeInfo(pVampire->getName()) ) { //cout << "근데 서브 인벤토리가 없다." <<endl; executeSkillFailException(pVampire, getSkillType()); return; } pInventory = pInventoryItem->getInventory(); invenID = pInventoryItem->getItemID(); } Item* pItem = pInventory->getItem(X, Y); // 아이템이 없거나, 뱀파이어 포탈 아이템이 아니거나, OID가 틀리다면 기술 사용 불가 if (pItem == NULL || pItem->getItemClass() != Item::ITEM_CLASS_VAMPIRE_PORTAL_ITEM || pItem->getObjectID() != InvenObjectID) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } VampirePortalItem* pVampirePortalItem = dynamic_cast<VampirePortalItem*>(pItem); Assert(pVampirePortalItem != NULL); // 뱀파이어 포탈 아이템에 기록된 위치가 없을 경우에는 실패다. ZoneID_t zoneid = pVampirePortalItem->getZoneID(); ZoneCoord_t tx = pVampirePortalItem->getX(); ZoneCoord_t ty = pVampirePortalItem->getY(); if (zoneid == 0) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } // 얻어온 목표 존과 좌표를 검증한다. Zone* pTargetZone = getZoneByZoneID(zoneid); // 아담의 성지와 다른 존과는 연결되지 않는다. if (pZone->isHolyLand() != pTargetZone->isHolyLand()) { executeSkillFailException(pVampire, getSkillType()); return; } //cout << "타겟 존 포인터 획득 성공" << endl; VSRect* pRect = pTargetZone->getOuterRect(); if (!pRect->ptInRect(tx, ty)) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } //cout << "좌표 검증 성공" << endl; GCSkillToInventoryOK1 _GCSkillToInventoryOK1; SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = checkZoneLevelToUseSkill(pVampire); //bool bHitRoll = HitRoll::isSuccessMagic(pVampire, pSkillInfo, pSkillSlot); bool bHitRoll = true; if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll) { decreaseMana(pVampire, RequiredMP, _GCSkillToInventoryOK1); SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); // 각각의 존에다가 포탈을 더한다. ZONE_COORD s_coord; ZONE_COORD t_coord; s_coord.id = pZone->getZoneID(); s_coord.x = pVampire->getX(); s_coord.y = pVampire->getY(); t_coord.id = pTargetZone->getZoneID(); t_coord.x = tx; t_coord.y = ty; pZone->addVampirePortal(s_coord.x, s_coord.y, pVampire, t_coord); pTargetZone->addVampirePortal(t_coord.x, t_coord.y, pVampire, s_coord); _GCSkillToInventoryOK1.setSkillType(SkillType); _GCSkillToInventoryOK1.setObjectID(InvenObjectID); _GCSkillToInventoryOK1.setCEffectID(0); _GCSkillToInventoryOK1.setDuration(0); pPlayer->sendPacket(&_GCSkillToInventoryOK1); // 차지 수를 줄인다. pVampirePortalItem->setCharge(pVampirePortalItem->getCharge()-1); if (pVampirePortalItem->getCharge() > 0) { // 아직 차지가 남았다면 살려둔다. pVampirePortalItem->save(pVampire->getName(), STORAGE_INVENTORY, invenID, X, Y); } else { // 포탈 아이템의 차지가 다 소모되었다면 삭제시킨다. pInventory->deleteItem(X, Y); pVampirePortalItem->destroy(); // 아이템 포인터 자체가 지워지고, NULL이 되면, // 이펙트 내부에서의 아이템 포인터도 NULL이 되겠지... SAFE_DELETE(pVampirePortalItem); } pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, getSkillType(), NULL); } } catch(Throwable & t) { executeSkillFailException(pVampire, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 몬스터 셀프 핸들러 ////////////////////////////////////////////////////////////////////////////// void SummonMonsters::execute(Monster* pMonster) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; //cout << "SummonMonsters" << endl; Assert(pMonster != NULL); Zone* pZone = pMonster->getZone(); Assert(pZone != NULL); try { if (pMonster->isFlag(Effect::EFFECT_CLASS_HIDE)) { //cout << "SummonMonsters: hide" << endl; return; } if (pMonster->isFlag(Effect::EFFECT_CLASS_INVISIBILITY)) { addVisibleCreature(pZone, pMonster, true); } //GCSkillToSelfOK2 _GCSkillToSelfOK2; ZoneCoord_t x = pMonster->getX(); ZoneCoord_t y = pMonster->getY(); bool bRangeCheck = checkZoneLevelToUseSkill(pMonster); //bool bMoveModeCheck = pMonster->isWalking(); if (bRangeCheck)// && bMoveModeCheck) { //-------------------------------------------------------- // 주위에 knockback되는맞는 애들을 체크해준다. //-------------------------------------------------------- //SkillInput input(pMonster); //SkillOutput output; //computeOutput(input, output); SUMMON_INFO2 summonInfo; bool hasInfo = pMonster->getMonsterSummonInfo(summonInfo); if (!hasInfo || summonInfo.pMonsters==NULL) { //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; // 소환할 몹이 없는 경우다. -_-; executeSkillFailNormal(pMonster, getSkillType(), NULL); // 마스터 레어에서 마스터가 몹을 소환할려고 한 경우 if (pZone->isMasterLair() && pMonster->isMaster()) { MasterLairManager* pMasterLairManager = pZone->getMasterLairManager(); Assert(pMasterLairManager!=NULL); // 더 이상 소환할게 없다면.. // 마스터가 직접 나서서 싸워야겠지.. pMasterLairManager->setMasterReady(); //cout << "no more SummonMonsters: set MasterReady" << endl; } } if (pMonster->isMaster() && pZone->isMasterLair()) { MasterLairManager* pMasterLairManager = pZone->getMasterLairManager(); Assert(pMasterLairManager!=NULL); // minion combat에서는 지정된 좌표에 소환한다. MasterLairInfo* pInfo = g_pMasterLairInfoManager->getMasterLairInfo(pZone->getZoneID()); Assert(pInfo!=NULL); if (!pMasterLairManager->isMasterReady()) { x = pInfo->getSummonX(); y = pInfo->getSummonY(); GCSay gcSay; gcSay.setObjectID(pMonster->getObjectID()); gcSay.setColor(MASTER_SAY_COLOR); gcSay.setMessage(pInfo->getRandomMasterSummonSay()); if (!gcSay.getMessage().empty()) pZone->broadcastPacket(pMonster->getX(), pMonster->getY(), &gcSay); } // 마스터 레어에서는 소환된 몬스터들이 아템 안 준다. // by sigi. 2002.11.21 summonInfo.hasItem = false; } summonInfo.scanEnemy = true; summonInfo.clanType = SUMMON_INFO::CLAN_TYPE_GROUP; summonInfo.clanID = pMonster->getClanType(); // 주인의 clan을 따른다. summonInfo.X = x; summonInfo.Y = y; summonInfo.regenType = REGENTYPE_PORTAL; // 몬스터를 존에 추가한다. addMonstersToZone(pZone, summonInfo); //cout << "SummonMonsters OK" << endl; GCSkillToTileOK5 _GCSkillToTileOK5; _GCSkillToTileOK5.setObjectID(pMonster->getObjectID()); _GCSkillToTileOK5.setSkillType(getSkillType()); _GCSkillToTileOK5.setX(x); _GCSkillToTileOK5.setY(y); _GCSkillToTileOK5.setDuration( 0); pZone->broadcastPacket(x, y, &_GCSkillToTileOK5); } else { executeSkillFailNormal(pMonster, getSkillType(), NULL); } } catch(Throwable & t) { executeSkillFailException(pMonster, getSkillType()); } __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 인벤토리 핸들러 ////////////////////////////////////////////////////////////////////////////// void TransformToWolf::execute(Vampire* pVampire, ObjectID_t InvenObjectID, ObjectID_t InventoryItemObjectID, CoordInven_t X, CoordInven_t Y, CoordInven_t TargetX, CoordInven_t TargetY, VampireSkillSlot* pSkillSlot) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pVampire != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Inventory* pInventory = pVampire->getInventory(); Assert(pPlayer != NULL); Assert(pZone != NULL); Assert(pInventory!= NULL); SubInventory* pInventoryItem = NULL; int invenID = 0; if (InventoryItemObjectID != 0 ) { //cout << "서브 인벤토리에서 사용 : " << InventoryItemObjectID << endl; CoordInven_t X, Y; pInventoryItem = dynamic_cast<SubInventory*>(pInventory->findItemOID(InventoryItemObjectID, X, Y )); TradeManager* pTradeManager = pZone->getTradeManager(); Assert(pTradeManager != NULL); if (pInventoryItem == NULL || pTradeManager->hasTradeInfo(pVampire->getName()) ) { //cout << "근데 서브 인벤토리가 없다." <<endl; executeSkillFailException(pVampire, getSkillType()); return; } pInventory = pInventoryItem->getInventory(); invenID = pInventoryItem->getItemID(); } Item* pItem = pInventory->getItem(X, Y); Assert(pItem != NULL); // 적당한 아이템이 아니라면 당연히 변신할 수 없다. // PK존에서는 변신할 수 없다. if (pItem->getItemClass() != Item::ITEM_CLASS_VAMPIRE_ETC || pItem->getItemType() != 0 || pVampire->hasRelicItem() || g_pPKZoneInfoManager->isPKZone(pZone->getZoneID() ) || pVampire->isFlag(Effect::EFFECT_CLASS_REFINIUM_TICKET ) || GDRLairManager::Instance().isGDRLairZone(pZone->getZoneID()) ) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } GCSkillToInventoryOK1 _GCSkillToInventoryOK1; SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); ZoneCoord_t x = pVampire->getX(); ZoneCoord_t y = pVampire->getY(); // Knowledge of Innate 가 있다면 hit bonus 10 int HitBonus = 0; if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_INNATE ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_INNATE); Assert(pRankBonus != NULL); HitBonus = pRankBonus->getPoint(); } int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = checkZoneLevelToUseSkill(pVampire); bool bHitRoll = HitRoll::isSuccessMagic(pVampire, pSkillInfo, pSkillSlot, HitBonus); bool bMoveModeCheck = pVampire->isWalking(); bool bEffected = pVampire->isFlag(Effect::EFFECT_CLASS_TRANSFORM_TO_WOLF) || pVampire->isFlag(Effect::EFFECT_CLASS_HAS_FLAG) || pVampire->isFlag(Effect::EFFECT_CLASS_HAS_SWEEPER); if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll && bMoveModeCheck && !bEffected) { decreaseMana(pVampire, RequiredMP, _GCSkillToInventoryOK1); SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); // 이펙트 클래스를 만들어 붙인다. EffectTransformToWolf* pEffectTTW = new EffectTransformToWolf(pVampire); pEffectTTW->setDeadline(999999999); pVampire->addEffect(pEffectTTW); pVampire->setFlag(Effect::EFFECT_CLASS_TRANSFORM_TO_WOLF); // 이로 인해서 변하는 능력치들을 보내준다. VAMPIRE_RECORD prev; pVampire->getVampireRecord(prev); pVampire->initAllStat(); pVampire->addModifyInfo(prev, _GCSkillToInventoryOK1); _GCSkillToInventoryOK1.setSkillType(SkillType); _GCSkillToInventoryOK1.setCEffectID(0); _GCSkillToInventoryOK1.setDuration(0); pPlayer->sendPacket(&_GCSkillToInventoryOK1); // 뱀파이어 대신 늑대를 더하라고 알려준다. GCAddWolf gcAddWolf; gcAddWolf.setObjectID(pVampire->getObjectID()); gcAddWolf.setName(pVampire->getName()); gcAddWolf.setXYDir(x, y, pVampire->getDir()); gcAddWolf.setItemType(pItem->getItemType()); gcAddWolf.setCurrentHP(pVampire->getHP()); gcAddWolf.setMaxHP(pVampire->getHP(ATTR_MAX)); gcAddWolf.setGuildID(pVampire->getGuildID()); pZone->broadcastPacket(x, y, &gcAddWolf, pVampire); decreaseItemNum(pItem, pInventory, pVampire->getName(), STORAGE_INVENTORY, invenID, X, Y); if (pVampire->getPetInfo() != NULL ) { pVampire->setPetInfo(NULL); sendPetInfo(dynamic_cast<GamePlayer*>(pVampire->getPlayer()), true); } pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, getSkillType(), NULL); } } catch(Throwable & t) { executeSkillFailException(pVampire, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; __END_CATCH }
Item* CastleShrineInfoManager::addShrineToZone(ShrineInfo& shrineInfo, ItemType_t itemType ) throw(Error) { __BEGIN_TRY // 성단을 넣을 존을 가져온다. Zone* pZone = getZoneByZoneID(shrineInfo.getZoneID()); Assert(pZone != NULL); MonsterCorpse* pShrine = new MonsterCorpse(shrineInfo.getMonsterType(), shrineInfo.getName(), 2); Assert(pShrine != NULL); pShrine->setShrine(true); pShrine->setZone(pZone); pZone->getObjectRegistry().registerObject(pShrine); shrineInfo.setObjectID(pShrine->getObjectID()); Item* pItem = NULL; /* if (shrineInfo.getShrineType() == ShrineInfo::SHRINE_GUARD ) { pShrine->setFlag(Effect::EFFECT_CLASS_SHRINE_GUARD); EffectShrineGuard* pEffect = new EffectShrineGuard(pShrine); pEffect->setShrineID(itemType); pEffect->setTick(60 * 10); pShrine->getEffectManager().addEffect(pEffect); } else if (shrineInfo.getShrineType() == ShrineInfo::SHRINE_HOLY ) { pShrine->setFlag(Effect::EFFECT_CLASS_SHRINE_HOLY); EffectShrineHoly* pEffect = new EffectShrineHoly(pShrine); pEffect->setShrineID(itemType); pEffect->setTick(60 * 10); pShrine->getEffectManager().addEffect(pEffect); }*/ TPOINT tp = pZone->addItem(pShrine, shrineInfo.getX(), shrineInfo.getY(), true); Assert(tp.x != -1); // 성의 상징을 추가할 필요가 있다면 추가한다. if (shrineInfo.getShrineType() == ShrineInfo::SHRINE_GUARD ) { //if (AddBible[ itemType ] ) { list<OptionType_t> optionNULL; pItem = g_pItemFactoryManager->createItem(Item::ITEM_CLASS_CASTLE_SYMBOL, itemType, optionNULL); Assert(pItem != NULL); char strZoneID[10]; sprintf(strZoneID, "%d", (int)pZone->getZoneID()); pZone->registerObject(pItem); pItem->create(strZoneID, STORAGE_CORPSE, pShrine->getObjectID(), 0, 0); pShrine->addTreasure(pItem); } // 수호성단이라는걸 표시해둔다. pShrine->setFlag(Effect::EFFECT_CLASS_CASTLE_SHRINE_GUARD); // 모든 수호성단에 Shield Effect 붙인다 pShrine->setFlag(Effect::EFFECT_CLASS_SHRINE_SHIELD); EffectShrineShield* pEffect = new EffectShrineShield(pShrine); pEffect->setShrineID(itemType); pEffect->setTick(60 * 10); pShrine->getEffectManager().addEffect(pEffect); } else { // 성지성단이라는걸 표시해둔다. pShrine->setFlag(Effect::EFFECT_CLASS_CASTLE_SHRINE_HOLY); } // 성단 좌표를 새로 세팅한다. shrineInfo.setX(tp.x); shrineInfo.setY(tp.y); return pItem; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 오브젝트 핸들러 ////////////////////////////////////////////////////////////////////////////// void Restore::execute(Slayer* pSlayer, ObjectID_t TargetObjectID, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; //cout << "Restore2 Start" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); Creature* pFromCreature = pZone->getCreature(TargetObjectID); // 뱀파이어만 건드릴 수가 있다. // NoSuch제거. by sigi. 2002.5.2 if (pFromCreature==NULL || !pFromCreature->isVampire()) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } GCSkillToObjectOK1 _GCSkillToObjectOK1; // 스킬 쓴 넘에게... GCMorph1 _GCMorph1; // 변신 당사자에게.. GCMorphSlayer2 _GCMorphSlayer2; // 변신 구경꾼들에게.. SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); bool bRangeCheck = verifyDistance(pSlayer, pFromCreature, pSkillInfo->getRange()); bool bHitRoll = true; if (bRangeCheck && bHitRoll) { ////////////////////////////////////////////////////////////////////// // 각종 존 레벨 정보를 삭제해야 한다. ////////////////////////////////////////////////////////////////////// // 파티 초대 중이라면 정보를 삭제해 준다. PartyInviteInfoManager* pPIIM = pZone->getPartyInviteInfoManager(); Assert(pPIIM != NULL); pPIIM->cancelInvite(pFromCreature); // 파티 관련 정보를 삭제해 준다. int PartyID = pFromCreature->getPartyID(); if (PartyID != 0) { // 먼저 로컬에서 삭제하고... LocalPartyManager* pLPM = pZone->getLocalPartyManager(); Assert(pLPM != NULL); pLPM->deletePartyMember(PartyID, pFromCreature); // 글로벌에서도 삭제해 준다. deleteAllPartyInfo(pFromCreature); } // 트레이드 중이었다면 트레이드 관련 정보를 삭제해준다. TradeManager* pTM = pZone->getTradeManager(); Assert(pTM != NULL); pTM->cancelTrade(pFromCreature); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// Slayer* pNewSlayer = new Slayer; Vampire* pVampire = dynamic_cast<Vampire*>(pFromCreature); // DB에서 혹시 남아있을 지 모르는 흡혈 정보를 삭제해준다. Statement* pStmt = NULL; BEGIN_DB { pStmt = g_pDatabaseManager->getConnection("DARKEDEN")->createStatement(); StringStream sql; sql << "DELETE FROM EffectBloodDrain WHERE OwnerID = '" + pFromCreature->getName() + "'"; pStmt->executeQuery(sql.toString()); SAFE_DELETE(pStmt); } END_DB(pStmt) pNewSlayer->setName(pFromCreature->getName()); // 크리쳐 안의 플레이어 포인터와 플레이어 안의 크리쳐 포인터를 갱신한다. Player* pFromPlayer = pFromCreature->getPlayer(); pNewSlayer->setPlayer(pFromPlayer); GamePlayer* pFromGamePlayer = dynamic_cast<GamePlayer*>(pFromPlayer); pFromGamePlayer->setCreature(pNewSlayer); pNewSlayer->setZone(pZone); pNewSlayer->load(); pNewSlayer->setObjectID(pFromCreature->getObjectID()); pNewSlayer->setMoveMode(Creature::MOVE_MODE_WALKING); ZoneCoord_t x = pFromCreature->getX(); ZoneCoord_t y = pFromCreature->getY(); Dir_t dir = pFromCreature->getDir(); Tile& tile = pZone->getTile(x, y); // 곧 pFromCreature 즉, 원래의 뱀파이어 객체는 지워질 것이므로, // PCFinder에 들어가 있는 값은 쓰레기 값이 될 것이다. // 그러므로 뱀파이어 포인터를 지워주고, 새로운 슬레이어 포인터를 더한다. g_pPCFinder->deleteCreature(pFromCreature->getName()); g_pPCFinder->addCreature(pNewSlayer); // 길드 현재 접속 멤버 리스트에서 삭제한다. if (pVampire->getGuildID() != 0 ) g_pGuildManager->getGuild(pVampire->getGuildID() )->deleteCurrentMember(pVampire->getName()); // 인벤토리 교체. Inventory* pInventory = pVampire->getInventory(); pNewSlayer->setInventory(pInventory); pVampire->setInventory(NULL); // 보관함 교체 pNewSlayer->deleteStash(); pNewSlayer->setStash(pVampire->getStash()); pNewSlayer->setStashNum(pVampire->getStashNum()); pNewSlayer->setStashStatus(false); pVampire->setStash(NULL); /* // 가비지 교체 while (true) { Item* pGarbage = pVampire->popItemFromGarbage(); // 더 이상 없다면 브레이크... if (pGarbage == NULL) break; pNewSlayer->addItemToGarbage(pGarbage); } */ // 플래그 셋 교체 pNewSlayer->deleteFlagSet(); pNewSlayer->setFlagSet(pVampire->getFlagSet()); pVampire->setFlagSet(NULL); Item* pItem = NULL; _TPOINT point; // 입고 있는 아이템들을 인벤토리 또는 바닥으로 옮긴다. for(int part = 0; part < (int)Vampire::VAMPIRE_WEAR_MAX; part++) { pItem = pVampire->getWearItem((Vampire::WearPart)part); if (pItem != NULL) { // 먼저 기어에서 삭제하고... pVampire->deleteWearItem((Vampire::WearPart)part); // 인벤토리에 자리가 있으면 인벤토리에 더하고... if (pInventory->getEmptySlot(pItem, point)) { pInventory->addItem(point.x, point.y, pItem); pItem->save(pNewSlayer->getName(), STORAGE_INVENTORY, 0, point.x, point.y); } // 자리가 없으면 바닥에 떨어뜨린다. else { ZoneCoord_t ZoneX = pVampire->getX(); ZoneCoord_t ZoneY = pVampire->getY(); TPOINT pt; pt = pZone->addItem(pItem, ZoneX , ZoneY); if (pt.x != -1) { pItem->save("", STORAGE_ZONE, pZone->getZoneID(), pt.x, pt.y); } else { pItem->destroy(); SAFE_DELETE(pItem); } } } } pItem = pVampire->getExtraInventorySlotItem(); if (pItem != NULL) { pVampire->deleteItemFromExtraInventorySlot(); // 인벤토리에 자리가 있으면 인벤토리에 더하고... if (pInventory->getEmptySlot(pItem, point)) { pInventory->addItem(point.x, point.y, pItem); pItem->save(pNewSlayer->getName(), STORAGE_INVENTORY, 0, point.x, point.y); } // 자리가 없으면 바닥에 떨어뜨린다. else { TPOINT pt; ZoneCoord_t ZoneX = pVampire->getX(); ZoneCoord_t ZoneY = pVampire->getY(); pt = pZone->addItem(pItem, ZoneX , ZoneY); if (pt.x != -1) { pItem->save("", STORAGE_ZONE, pZone->getZoneID(), pt.x, pt.y); } else { pItem->destroy(); SAFE_DELETE(pItem); } } } // 뱀파이어 가지고 있던 돈을 슬레이어로 옮겨준다. pNewSlayer->setGoldEx(pVampire->getGold()); // 스킬 정보를 전송한다. pNewSlayer->sendSlayerSkillInfo(); _GCMorph1.setPCInfo2(pNewSlayer->getSlayerInfo2()); _GCMorph1.setInventoryInfo(pNewSlayer->getInventoryInfo()); _GCMorph1.setGearInfo(pNewSlayer->getGearInfo()); _GCMorph1.setExtraInfo(pNewSlayer->getExtraInfo()); _GCMorphSlayer2.setSlayerInfo(pNewSlayer->getSlayerInfo3()); pFromPlayer->sendPacket(&_GCMorph1); //pFromGamePlayer->deleteEvent(Event::EVENT_CLASS_REGENERATION); pZone->broadcastPacket(x, y, &_GCMorphSlayer2, pNewSlayer); // 타일 및 존에서 기존 뱀파이어를 삭제하고, 새로운 슬레이어를 더한다. tile.deleteCreature(pFromCreature->getObjectID()); pZone->deletePC(pFromCreature); TPOINT pt = findSuitablePosition(pZone, x, y, Creature::MOVE_MODE_WALKING); Tile& newtile = pZone->getTile(pt.x, pt.y); newtile.addCreature(pNewSlayer); pNewSlayer->setXYDir(pt.x, pt.y, dir); pZone->addPC(pNewSlayer); pNewSlayer->tinysave("Race='SLAYER'"); SAFE_DELETE(pFromCreature); // 시야 update.. pZone->updateHiddenScan(pNewSlayer); _GCSkillToObjectOK1.setSkillType(SkillType); _GCSkillToObjectOK1.setCEffectID(CEffectID); _GCSkillToObjectOK1.setTargetObjectID(TargetObjectID); _GCSkillToObjectOK1.setDuration(0); pPlayer->sendPacket(&_GCSkillToObjectOK1); pSkillSlot->setRunTime(0); EffectRestore* pEffectRestore = new EffectRestore(pNewSlayer); pEffectRestore->setDeadline(60*60*24*7*10); // 7일 pNewSlayer->addEffect(pEffectRestore); pNewSlayer->setFlag(Effect::EFFECT_CLASS_RESTORE); pEffectRestore->create(pNewSlayer->getName()); } else { executeSkillFailNormal(pSlayer, getSkillType(), pFromCreature); } }
void CGUseMessageItemFromInventoryHandler::executeEventTree(CGUseMessageItemFromInventory* 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); Creature* pCreature = pGamePlayer->getCreature(); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); Inventory* pInventory = pPC->getInventory(); Zone* pZone = pPC->getZone(); CoordInven_t InvenX = pPacket->getX(); CoordInven_t InvenY = pPacket->getY(); Item* pItem = pInventory->getItem(InvenX, InvenY); ObjectID_t ItemObjectID = pItem->getObjectID(); MonsterType_t MType = 0; int time = 0; switch (pItem->getItemType() ) { case 12: MType = 482; time = g_pVariableManager->getVariable(CHRISTMAS_TREE_DECAY_TIME ) / 10; break; case 26: MType = 650; time = 21600; break; case 27: MType = 650; time = 43200; break; case 28: MType = 650; time = 86400; break; default: { filelog("EventTree.log", "이상한 아템을 썼다. : %s 가 %d", pPC->getName().c_str(), pItem->getItemType()); GCCannotUse _GCCannotUse; _GCCannotUse.setObjectID(pPacket->getObjectID()); pGamePlayer->sendPacket(&_GCCannotUse); } return; } // 타일에 스킬을 쓰는 것이라고 보고 쓸 수 있는지를 체크한다. // 안전지대에서는 사용할 수 없다. // 쓸 수 있는 아이템 타입인지 왁인한다. ItemType 이 12인 것만 사용할 수 있다. // 근처에(플레이어 주위의 5x5타일 이내) 다른 트리가 있다면 사용할 수 없다. if (!isAbleToUseTileSkill(pCreature ) || pZone->isMasterLair() || ItemObjectID != pPacket->getObjectID() || checkCorpse(pZone, MType, pPC->getX() - 2, pPC->getY() - 2, pPC->getX() + 2, pPC->getY() + 2 ) ) { GCCannotUse _GCCannotUse; _GCCannotUse.setObjectID(pPacket->getObjectID()); pGamePlayer->sendPacket(&_GCCannotUse); return; } // 성이면 성주 길드원만 쓸 수 있다. if (!pPC->isGOD() ) { if (pZone->isCastle() ) { if (!g_pCastleInfoManager->isCastleMember(pZone->getZoneID(), pPC ) ) { GCCannotUse _GCCannotUse; _GCCannotUse.setObjectID(pPacket->getObjectID()); pGamePlayer->sendPacket(&_GCCannotUse); return; } } // 성이 아닌 곳의 안전지대에선 절대 못 쓴다. else if (pZone->getZoneLevel(pCreature->getX(), pCreature->getY()) & SAFE_ZONE) { GCCannotUse _GCCannotUse; _GCCannotUse.setObjectID(pPacket->getObjectID()); pGamePlayer->sendPacket(&_GCCannotUse); return; } } /* // 트리를 존에 추가한다. (트리는 몬스터 시체를 이용한다) MonsterCorpse* pMonsterCorpse = new MonsterCorpse(482, pPacket->getMessage(), 2); Assert(pMonsterCorpse!=NULL); pZone->getObjectRegistry().registerObject(pMonsterCorpse); // 생성된 시체를 존에 추가한다. int delayTime = g_pVariableManager->getVariable(CHRISTMAS_TREE_DECAY_TIME); // by sigi. 2002.12.17 TPOINT pt = pZone->addItem(pMonsterCorpse, pPC->getX(), pPC->getY(), true, delayTime); // 6시간 뒤에 트리(시체)가 사라진다. if (pt.x == -1)*/ if (!createBulletinBoard(pZone, pPC->getX(), pPC->getY(), MType, pPacket->getMessage(), VSDateTime::currentDateTime().addSecs(time ) ) ) { GCCannotUse _GCCannotUse; _GCCannotUse.setObjectID(pPacket->getObjectID()); pGamePlayer->sendPacket(&_GCCannotUse); return; } // 사용한 아이템이므로 지워준다. pInventory->deleteItem(InvenX, InvenY); pItem->destroy(); SAFE_DELETE(pItem); // 아이템을 사용했다고 클라이언트에 알린다. GCUseOK gcUseOK; pGamePlayer->sendPacket(&gcUseOK); //pZone->broadcastPacket(pCreature->getX(), pCreature->getY(), &gcAddEffectToTile); #endif __END_DEBUG_EX __END_CATCH }
//--------------------------------------------------------------------------- // make Balanced LoadInfo //--------------------------------------------------------------------------- // // bForce : balacing할 필요가 없다고 판단되는 경우에도 // 강제로 ZoneGroup을 balancing할 경우에 사용된다. // // Zone마다의 10초간의 loop 처리 회수를 load값으로 한다. // 계산에 편의를 위해서 실제 load는 다음과 같의 정의한다. // // load = (loadLimit - load)*loadMultiplier; // //--------------------------------------------------------------------------- bool ZoneGroupManager::makeBalancedLoadInfo(LOAD_INFOS& loadInfos, bool bForce) throw(Error) { const int maxGroup = m_ZoneGroups.size(); // zoneGroup 수 //const int loadMultiplier = 5; // load 가중치 - 느린 애들을 더 느리다...라고 하기 위한 것. const int loadLimit = 500; // load 값 제한 - sleep에 의해서 제한돼서 루프 처리회수 500이 최고다. const int stableLoad = 120; // 안정적인 load - 이 정도면 balancing이 필요없다고 생각되는 수준 //const int minLoadGap = 20 * loadMultiplier; // load balancing을 하기 위한 load 차이 - 최고~최저의 차이가 일정 값 이상이어야지 balancing이 의미있다. const int minLoadGap = 20; // load balancing을 하기 위한 load 차이 - 최고~최저의 차이가 일정 값 이상이어야지 balancing이 의미있다. const int averageLoadPercent = 90; // 한 group의 load % 제한. 100으로 해도 되겠지만 90정도가 괜찮은거 같다. int i; //LOAD_INFOS loadInfos; GROUPS groups; map< ZoneGroupID_t , ZoneGroup* >::const_iterator itr; // 전체 load int totalLoad = 0; //------------------------------------------------------------------ // ZoneGroup마다 loadValue 조사 //------------------------------------------------------------------ int maxLoadValue = 0; int minLoadValue = loadLimit; for (itr = m_ZoneGroups.begin() ; itr != m_ZoneGroups.end() ; itr ++) { ZoneGroup* pZoneGroup = itr->second; const map< ZoneID_t, Zone* >& zones = pZoneGroup->getZones(); map< ZoneID_t, Zone* >::const_iterator iZone; // 각 Zone의 loadValue를 구한다. for (iZone=zones.begin(); iZone!=zones.end(); iZone++) { Zone* pZone = iZone->second; int load = pZone->getLoadValue(); load = min(load, loadLimit); // 10~500 maxLoadValue = max(maxLoadValue, load); minLoadValue = min(minLoadValue, load); // 숫자 적은게 느린 거다. // 계산의 편의를 위해서 숫자를 뒤집?는다. --> 큰 숫자 부하가 큰 걸로 바꾼다. // player숫자를 부하가중치로 사용한다. // playerLoad = 1 ~ 20정도? int playerLoad = pZone->getPCCount()/10; playerLoad = max(1, playerLoad); //load = (loadLimit - load)*loadMultiplier; // 부하 가중치 load = (loadLimit - load)*playerLoad; // 부하 가중치 LoadInfo* pInfo = new LoadInfo; pInfo->id = pZone->getZoneID(); pInfo->oldGroupID = itr->first; pInfo->groupID = -1; pInfo->load = load; // 부하와 zoneID로 이루어진 key DWORD key = (load << 8) | pInfo->id; loadInfos[key] = pInfo; totalLoad += load; } } //------------------------------------------------------------------ // // balancing이 필요한지 확인 // //------------------------------------------------------------------ if (!bForce) { int loadBoundary = stableLoad; //int loadBoundary = (loadLimit - stableLoad ) * loadMultiplier; // 부하 한계 수치보다 작거나 // min~max 부하 수치 차이가 일정수치 이하이면 // load balancing할 필요가 없다. //if (maxLoad <= loadBoundary if (minLoadValue >= loadBoundary || maxLoadValue-minLoadValue <= minLoadGap) { // load를 다시 조사해야 한다. for (itr = m_ZoneGroups.begin() ; itr != m_ZoneGroups.end() ; itr ++) { ZoneGroup* pZoneGroup = itr->second; // loadValue를 초기화 시켜준다. const map< ZoneID_t, Zone* >& zones = pZoneGroup->getZones(); map< ZoneID_t, Zone* >::const_iterator iZone; // 각 Zone의 loadValue를 구한다. for (iZone=zones.begin(); iZone!=zones.end(); iZone++) { Zone* pZone = iZone->second; pZone->initLoadValue(); } } return false; } } // 평균 load //int avgLoad = totalLoad / maxGroups; // average를 90%로 잡은 경우 int avgLoad = totalLoad * averageLoadPercent / maxGroup / 100; // 새로운 그룹의 load를 계산하기 위해서 groups.reserve(maxGroup); for (i=0; i<maxGroup; i++) { groups[i] = 0; } // balancing하기 전의 상태 출력 //outputLoadValue(); //------------------------------------------------------------------ // // load balancing // // 약간의 변화를 준? FirstFit 사용. //------------------------------------------------------------------ LOAD_INFOS::const_iterator iInfo = loadInfos.begin(); int index = 0; for (; iInfo!=loadInfos.end(); iInfo++) { LoadInfo* pInfo = iInfo->second; // 들어갈 새 group을 찾는다. int newGroupID = -1; for (int k=0; k<maxGroup; k++) { int groupLoad = groups[index]; if (groupLoad+pInfo->load <= avgLoad) { newGroupID = index; if (++index>=maxGroup) index = 0; break; } if (++index>=maxGroup) index = 0; } // 적절한 group을 못 찾았으면 젤 값이 적은 group에 넣는다. if (newGroupID==-1) { newGroupID = 0; for (int k=1; k<maxGroup; k++) { if (groups[k] < groups[newGroupID]) { newGroupID = k; } } } // newGroupID에다가 Info를 추가한다. pInfo->groupID = newGroupID + 1; // 1을 증가시켜줘야 한다. -_-; groups[newGroupID] += pInfo->load; } return true; }