Damage_t computeMaxDamage(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); Damage_t maxDamage = 0; double CSTR = pAttr->nSTR; double CLEVEL = pAttr->nLevel; if (CClass == Creature::CREATURE_CLASS_SLAYER) { maxDamage = (int)(CSTR/10.0); if (pAttr->pWeapon != NULL) { // ���� ���� ���� ���쿡�� ���� ���� ���ʽ��� �������� �ʴ´�. if (isArmsWeapon(pAttr->pWeapon)) { maxDamage = 2; } } maxDamage += g_pVariableManager->getCombatSlayerDamageBonus(); maxDamage = min((int)maxDamage, SLAYER_MAX_DAMAGE); } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { maxDamage = (int)(CSTR / 4.0 + CLEVEL / 5.0); maxDamage += g_pVariableManager->getCombatVampireDamageBonus(); maxDamage = min((int)maxDamage, VAMPIRE_MAX_DAMAGE); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS ) { maxDamage = (int)(CSTR / 6.0 + CLEVEL / 6.0); maxDamage = min((int)maxDamage, OUSTERS_MAX_DAMAGE); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { maxDamage = (int)(CSTR / (4.0 - CLEVEL/100.0)); maxDamage += getPercentValue(maxDamage, enhance); maxDamage = getPercentValue(maxDamage, g_pVariableManager->getVariable(MONSTER_DAMAGE_RATIO )); maxDamage = min((int)maxDamage, MONSTER_MAX_DAMAGE); } return maxDamage; }
ToHit_t computeToHit(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); ToHit_t toHit = 0; double CDEX = pAttr->nDEX; double CLEVEL = pAttr->nLevel; if (CClass == Creature::CREATURE_CLASS_SLAYER) { toHit = (int)(CDEX/2); if (pAttr->pWeapon != NULL) { // ���� ���⳪, ���� ���⸦ ���� ���� ����, // ������ ������ ���ؼ� ���ʽ� ����Ʈ�� �����Ѵ�. if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_SWORD) { toHit += (int)(pAttr->pDomainLevel[SKILL_DOMAIN_SWORD]*1.5); } else if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_BLADE) { toHit += (int)(pAttr->pDomainLevel[SKILL_DOMAIN_BLADE]*1.5); } else if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_CROSS) { toHit += (int)(pAttr->pDomainLevel[SKILL_DOMAIN_HEAL]*1.5); } else if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_MACE) { toHit += (int)(pAttr->pDomainLevel[SKILL_DOMAIN_ENCHANT]*1.5); } else if (isArmsWeapon(pAttr->pWeapon)) { toHit += (int)(pAttr->pDomainLevel[SKILL_DOMAIN_GUN]*1.5); } } toHit = min((int)toHit, SLAYER_MAX_TOHIT); } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { toHit = (int)(CDEX + CLEVEL/2.5); toHit = min((int)toHit, VAMPIRE_MAX_TOHIT); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS ) { toHit = (int)(CDEX + CLEVEL/2.5); toHit = min((int)toHit, OUSTERS_MAX_TOHIT); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { toHit = (int)((CDEX/2.0) * (1.0 + CLEVEL/100.0)); toHit += getPercentValue(toHit, enhance); toHit = min((int)toHit, MONSTER_MAX_TOHIT); } return toHit; }
Defense_t computeDefense(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); Defense_t Defense = 0; double CDEX = pAttr->nDEX; double CLEVEL = pAttr->nLevel; if (CClass == Creature::CREATURE_CLASS_SLAYER) { Defense = (int)(CDEX/2.0); Defense = min((int)Defense, SLAYER_MAX_DEFENSE); } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { Defense = (int)(CDEX/2.0 + CLEVEL/5.0); Defense = min((int)Defense, VAMPIRE_MAX_DEFENSE); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS ) { Defense = (int)(CDEX/2.0 + CLEVEL/5.0); Defense = min((int)Defense, OUSTERS_MAX_DEFENSE); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { Defense = (int)((CDEX/2.0) * (1.0 + CLEVEL/100.0)); Defense += getPercentValue(Defense, enhance); Defense = min((int)Defense, MONSTER_MAX_DEFENSE); } return Defense; }
// Mysterious Item 가격 // itemClass와 pCreature의 능력치에 따라서 가격이 달라진다. Price_t PriceManager::getMysteriousPrice(Item::ItemClass itemClass, Creature* pCreature) const { int multiplier = 1; if (pCreature->isSlayer()) { Slayer* pSlayer = dynamic_cast<Slayer*>(pCreature); Attr_t CSTR = pSlayer->getSTR(ATTR_BASIC); Attr_t CDEX = pSlayer->getDEX(ATTR_BASIC); Attr_t CINT = pSlayer->getINT(ATTR_BASIC); Attr_t CSUM = CSTR + CDEX + CINT; // 0~20 사이 multiplier = CSUM / 15; } else if (pCreature->isVampire()) { Vampire* pVampire = dynamic_cast<Vampire*>(pCreature); Level_t CLevel = pVampire->getLevel(); // 0~20 사이 multiplier = CLevel / 5; } else if (pCreature->isOusters()) { Ousters* pOusters = dynamic_cast<Ousters*>(pCreature); Level_t CLevel = pOusters->getLevel(); // 0~20 사이 multiplier = CLevel / 5; } // 1~20사이 multiplier = max(1, multiplier); // 가격 평균을 알아온다. InfoClassManager* pInfoClass = g_pItemInfoManager->getInfoManager(itemClass); Assert(pInfoClass!=NULL); // 가격 평균 * 능력치 비율? int finalPrice = (int)pInfoClass->getAveragePrice() * multiplier; // Blood Bible 보너스 적용 if (pCreature->isPC() ) { PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); int ratio = pPC->getGamblePriceRatio(); if (ratio != 0 ) { // ratio 값은 마이너스 값이다. finalPrice += getPercentValue(finalPrice, ratio); } } return finalPrice; }
////////////////////////////////////////////////////////////////////////////// // 주어진 숫자의 %값 알아내기 - 소수 첫재자리 보정 ////////////////////////////////////////////////////////////////////////////// int getPercentValueEx(int value, int percent) { static int tick = 0; int thousand = getPercentValue(value, percent * 10); int ret = (thousand + tick) / 10; tick++; if (tick > 9) tick = 0; return ret; }
int computeCriticalRatio(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); int CriticalRatio = 0; double CDEX = pAttr->nDEX; if (CClass == Creature::CREATURE_CLASS_SLAYER) { if (pAttr->pWeapon != NULL) { if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_SWORD) { CriticalRatio = (int)(pAttr->pDomainLevel[SKILL_DOMAIN_SWORD]/5.0); } else if (pAttr->pWeapon->getItemClass() == Item::ITEM_CLASS_BLADE) { CriticalRatio = (int)(pAttr->pDomainLevel[SKILL_DOMAIN_BLADE]/5.0); } else if (isArmsWeapon(pAttr->pWeapon)) { // ���� ������ ���쿡�� ������ ���� ���� ���ǵ尡 �����ȴ�. CriticalRatio = (int)(pAttr->pDomainLevel[SKILL_DOMAIN_GUN]/5.0); } } } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { CriticalRatio = (int)((CDEX-20.0)/30.0); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS ) { CriticalRatio = (int)((CDEX-20.0)/30.0); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { CriticalRatio = (int)((CDEX-20.0)/30.0); CriticalRatio += getPercentValue(CriticalRatio, enhance); } return CriticalRatio; }
Protection_t computeProtection(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); Protection_t Protection = 0; double CSTR = pAttr->nSTR; //double CSTRBonus = pAttr->nSTR/15.0; double CLEVEL = pAttr->nLevel; if (CClass == Creature::CREATURE_CLASS_SLAYER) { //Protection = (int)(CSTRBonus); Protection = (int)(CSTR); Protection = min((int)Protection, SLAYER_MAX_PROTECTION); } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { //Protection = (int)(CSTR/5.0 + CLEVEL/5.0); Protection = (int)(CSTR + CLEVEL / 5.0); Protection = min((int)Protection, VAMPIRE_MAX_PROTECTION); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS ) { Protection = (int)(CSTR + CLEVEL/10.0); Protection = min((int)Protection, OUSTERS_MAX_PROTECTION); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { Protection = (int)(CSTR / (5.0 - CLEVEL/100.0)); Protection += getPercentValue(Protection, enhance); Protection = min((int)Protection, MONSTER_MAX_PROTECTION); } return Protection; }
int Timer::getInversePercentValue() { return 100 - getPercentValue(); }
////////////////////////////////////////////////////////////////////////////// // getPrice() // 아이템 정보를 참조해 실제 물건값을 정한다. // nDiscount 변수(백분율)를 이용해 물건값을 컨트롤할 수 있다. ////////////////////////////////////////////////////////////////////////////// Price_t PriceManager::getPrice(Item* pItem, MarketCond_t nDiscount, ShopRackType_t shopType, Creature* pCreature) const { // 첨에 공짜로 준 아이템은 팔아도 1원밖에 못 얻는다. 2003. 1. 15. Sequoia if (pItem->getCreateType() == Item::CREATE_TYPE_GAME ) return (Price_t)1; // 퀘스트 아이템은 오디번~~~ 2003. 4. 14. Sequoia if (pItem->isTimeLimitItem() ) return (Price_t)50; if (pItem->getItemClass() == Item::ITEM_CLASS_MOON_CARD && pItem->getItemType() == 4 ) { return (Price_t)g_pVariableManager->getVariable(CROWN_PRICE); } // 아이템의 원래 가격을 얻어낸다. ItemInfo* pItemInfo = g_pItemInfoManager->getItemInfo(pItem->getItemClass(), pItem->getItemType()); double originalPrice = pItemInfo->getPrice(); double finalPrice = 0; if (pItem->getGrade() != -1 ) { double gradePercent = 80 + (5 * pItem->getGrade()); // originalPrice = getPercentValue(originalPrice, gradePercent); originalPrice *= (gradePercent/100.0); } // 슬레이어 포탈 같은 경우에는 원래 가격에다 현재 차지 수만큼의 가격을 더해야 한다. if (pItem->getItemClass() == Item::ITEM_CLASS_SLAYER_PORTAL_ITEM) { SlayerPortalItem* pSlayerPortalItem = dynamic_cast<SlayerPortalItem*>(pItem); originalPrice += (pSlayerPortalItem->getCharge() * PORTAL_ITEM_CHARGE_PRICE); } else if (pItem->getItemClass() == Item::ITEM_CLASS_VAMPIRE_PORTAL_ITEM) { VampirePortalItem* pVampirePortalItem = dynamic_cast<VampirePortalItem*>(pItem); originalPrice += (pVampirePortalItem->getCharge() * PORTAL_ITEM_CHARGE_PRICE); } else if (pItem->getItemClass() == Item::ITEM_CLASS_OUSTERS_SUMMON_ITEM) { OustersSummonItem* pOustersSummonItem = dynamic_cast<OustersSummonItem*>(pItem); originalPrice += (pOustersSummonItem->getCharge() * SUMMON_ITEM_CHARGE_PRICE); } // 옵션이 있다면 옵션만큼의 가격을 곱해야 한다. const list<OptionType_t>& optionTypes = pItem->getOptionTypeList(); if (!optionTypes.empty()) { finalPrice = 0; // 가격 = (원래 가격 * 옵션의 PriceMultiplier / 100) + .. double priceMultiplier = 0; list<OptionType_t>::const_iterator itr; for (itr=optionTypes.begin(); itr!=optionTypes.end(); itr++) { OptionInfo* pOptionInfo = g_pOptionInfoManager->getOptionInfo(*itr); Assert(pOptionInfo != NULL); priceMultiplier = (double)(pOptionInfo->getPriceMultiplier()); finalPrice += (originalPrice* priceMultiplier / 100); } originalPrice = finalPrice; } // 아이템이 손상되었다면 손상된 만큼의 가격을 깍아야 한다. double maxDurability = (double)computeMaxDurability(pItem); double curDurability = (double)(pItem->getDurability()); // 아이템 중에 내구도가 없는 것들이 존재하기 때문에 처리해준다. if (maxDurability > 1) finalPrice = originalPrice* curDurability / maxDurability; else finalPrice = originalPrice; // 상점 시세에 따라 가격을 다시 조정해준다. finalPrice = finalPrice* nDiscount / 100; // 상점의 종류에 따라 가격을 다시 조정해준다. if (shopType == SHOP_RACK_MYSTERIOUS) { finalPrice *= 10; } // 크리쳐의 변화 요소에 따라 가격을 다시 조정해준다. if (pCreature != NULL) { if (pCreature->isSlayer()) { Slayer* pSlayer = dynamic_cast<Slayer*>(pCreature); Attr_t CSTR = pSlayer->getSTR(ATTR_CURRENT); Attr_t CDEX = pSlayer->getDEX(ATTR_CURRENT); Attr_t CINT = pSlayer->getINT(ATTR_CURRENT); if ((CSTR + CDEX + CINT <= 40) && (pItem->getItemClass() == Item::ITEM_CLASS_POTION) && (pItem->getItemType() == 0 || pItem->getItemType() == 5)) { finalPrice = getPercentValue((int)finalPrice, 70); } } else if (pCreature->isVampire()) { // 뱀파이어가 해골을 팔 경우에는 해골의 가격을 반으로 줄여준다. if (pItem->getItemClass() == Item::ITEM_CLASS_SKULL) { finalPrice = finalPrice / 2.0; } } else if (pCreature->isOusters()) { // 아우스터즈가 해골을 팔 경우에는 해골의 가격의 75%. if (pItem->getItemClass() == Item::ITEM_CLASS_SKULL) { finalPrice *= 0.75; } } } // 유료 사용자이고 유료존 이면 if (g_pVariableManager->getVariable(PREMIUM_HALF_EVENT ) ) { if ( pItem->getItemClass() == Item::ITEM_CLASS_POTION || pItem->getItemClass() == Item::ITEM_CLASS_SERUM || pItem->getItemClass() == Item::ITEM_CLASS_LARVA || pItem->getItemClass() == Item::ITEM_CLASS_PUPA || pItem->getItemClass() == Item::ITEM_CLASS_COMPOS_MEI ) { if (pCreature->isPC() ) { PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPC->getPlayer()); if (pGamePlayer->isPayPlaying() ) { // 반 값. finalPrice = finalPrice / 2; } } } } // Blood Bible 보너스 적용 if (pItem->getItemClass() == Item::ITEM_CLASS_POTION || pItem->getItemClass() == Item::ITEM_CLASS_SERUM ) { if (pCreature->isPC() ) { PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pCreature); int ratio = pPC->getPotionPriceRatio(); if (ratio != 0 ) { // ratio 값이 마이너스 값이다. finalPrice += getPercentValue((int)finalPrice, ratio); } } } return max(1, (int)finalPrice); }
// �̰� �ٲ��� Slayer::load(), Vampire::load(), Ousters::load() ���� // maxHP�� �����ؼ� �������ִ� �κе� �ٲ����� �Ѵ�. by sigi. HP_t computeHP(Creature::CreatureClass CClass, BASIC_ATTR* pAttr, int enhance) { Assert(pAttr != NULL); HP_t maxHP = 0; double CSTR = pAttr->nSTR; double CDEX = pAttr->nDEX; double CINT = pAttr->nINT; double CLEVEL = pAttr->nLevel; Item* pWeapon = pAttr->pWeapon; if (CClass == Creature::CREATURE_CLASS_SLAYER) { maxHP = (int)(CSTR*2.00); #ifndef __CHINA_SERVER__ if (pWeapon != NULL ) { switch (pWeapon->getItemClass() ) { case Item::ITEM_CLASS_SWORD: maxHP += pAttr->pDomainLevel[SKILL_DOMAIN_SWORD]; break; case Item::ITEM_CLASS_BLADE: maxHP += pAttr->pDomainLevel[SKILL_DOMAIN_BLADE]; break; case Item::ITEM_CLASS_CROSS: maxHP += pAttr->pDomainLevel[SKILL_DOMAIN_HEAL]; break; case Item::ITEM_CLASS_MACE: maxHP += pAttr->pDomainLevel[SKILL_DOMAIN_ENCHANT]; break; case Item::ITEM_CLASS_AR: case Item::ITEM_CLASS_SMG: case Item::ITEM_CLASS_SR: case Item::ITEM_CLASS_SG: maxHP += pAttr->pDomainLevel[SKILL_DOMAIN_GUN]; break; default: break; } } #endif maxHP = getPercentValue(maxHP, g_pVariableManager->getVariable(SLAYER_HP_RATIO)); maxHP = min((int)maxHP, SLAYER_MAX_HP); } else if (CClass == Creature::CREATURE_CLASS_VAMPIRE) { maxHP = (int)(CSTR*2.00 + CINT + CDEX + CLEVEL); maxHP = getPercentValue(maxHP, g_pVariableManager->getVariable(VAMPIRE_HP_RATIO)); maxHP = min((int)maxHP, VAMPIRE_MAX_HP); } // �ƿ콺�ͽ� �߰� by bezz 2003.04.22 else if (CClass == Creature::CREATURE_CLASS_OUSTERS) { maxHP = (int)(CSTR*1.50 + CINT/2.00 + CDEX + CLEVEL); maxHP = getPercentValue(maxHP, g_pVariableManager->getVariable(OUSTERS_HP_RATIO)); maxHP = min((int)maxHP, OUSTERS_MAX_HP); } else if (CClass == Creature::CREATURE_CLASS_MONSTER) { maxHP = (int)(CSTR*(2.00 + CLEVEL/100.0)); maxHP += getPercentValue(maxHP, enhance); maxHP = getPercentValue(maxHP, g_pVariableManager->getVariable(MONSTER_HP_RATIO)); maxHP = min((int)maxHP, MONSTER_MAX_HP); // maxHP = min((int)maxHP, 20000); } if (g_pConfig->hasKey("Hardcore") && g_pConfig->getPropertyInt("Hardcore")!=0 ) { maxHP *= 3; } return maxHP; }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 타일 핸들러 - AR이나 SMG를 들고 있을 경우 ////////////////////////////////////////////////////////////////////////////// void MoleShot::ARSMGexecute(Slayer* pSlayer, ZoneCoord_t X, ZoneCoord_t Y, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " SGexecute Begin" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK3 _GCSkillToTileOK3; GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(getSkillType()); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); Level_t SkillLevel = pSkillSlot->getExpLevel(); Item* pWeapon = pSlayer->getWearItem(Slayer::WEAR_RIGHTHAND); Assert(pWeapon != NULL); bool bIncreaseExp = pSlayer->isRealWearingEx(Slayer::WEAR_RIGHTHAND); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, X, Y, pWeapon->getRange()); bool bBulletCheck = (getRemainBullet(pWeapon) > 0) ? true : false; // 총알 숫자는 무조건 떨어뜨린다. Bullet_t RemainBullet = 0; if (bBulletCheck) { decreaseBullet(pWeapon); // 한발쓸때마다 저장할 필요 없다. by sigi. 2002.5.9 // pWeapon->save(pSlayer->getName(), STORAGE_GEAR, 0, Slayer::WEAR_RIGHTHAND, 0); RemainBullet = getRemainBullet(pWeapon); } // 데미지, 투힛 보너스, 좌표와 방향을 구한다. int ToHitBonus = 0; int DamageBonus = 0; int ToHitPenalty = 0; int DamagePenalty = 0; ZoneCoord_t myX = pSlayer->getX(); ZoneCoord_t myY = pSlayer->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); bool bHit = false; // 한번이라도 맞았는가를 저장하기 위한 변수 Damage_t Damage = 0; // 마지막으로 입힌 데미지를 저장하기 위한 변수 // AR이나 SMG일 경우에는 2부터 시작해서 4까지의 splash 데미지를 입힌다. int Splash = 1 + pSkillSlot->getExpLevel()/30 + 1; if (bManaCheck && bTimeCheck && bRangeCheck && bBulletCheck) { decreaseMana(pSlayer, RequiredMP, _GCSkillToTileOK1); SkillInput input(pSlayer, pSkillSlot); SkillOutput output; computeOutput(input, output); // 음수 값이 돌아온다. ToHitPenalty = getPercentValue(pSlayer->getToHit(), output.ToHit); list<Creature*> cList; list<Creature*> creatureList; getSplashVictims(pZone, X, Y, Creature::CREATURE_CLASS_MAX, creatureList, Splash); Level_t maxEnemyLevel = 0; uint EnemyNum = 0; list<Creature*>::iterator itr = creatureList.begin(); for (; itr != creatureList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); ToHitBonus = computeArmsWeaponToHitBonus(pWeapon, myX, myY, pTargetCreature->getX(), pTargetCreature->getY()); DamageBonus = computeArmsWeaponDamageBonus(pWeapon, myX, myY, pTargetCreature->getX(), pTargetCreature->getY()); bool bInvokerCheck = (pTargetCreature->getObjectID() == pSlayer->getObjectID()) ? true : false; bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isNPC(); bool bHitRoll = HitRoll::isSuccess(pSlayer, pTargetCreature, ToHitPenalty + ToHitBonus); bool bPK = verifyPK(pSlayer, pTargetCreature); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); if (pTargetCreature->isFlag(Effect::EFFECT_CLASS_NO_DAMAGE ) || pTargetCreature->isFlag(Effect::EFFECT_CLASS_COMA ) ) { bHitRoll = false; } if (!bInvokerCheck && !bRaceCheck && bHitRoll && bPK && bZoneLevelCheck) { bool bCriticalHit = false; // 데미지를 계산해서 페널티를 가한다. // 보너스는 멀티샷 페널티 때문에 음수가 될 수도 있다. Damage = computeDamage(pSlayer, pTargetCreature, SkillLevel/5, bCriticalHit); DamagePenalty = getPercentValue(Damage, output.Damage); Damage = max(0, Damage + DamagePenalty + DamageBonus); // 메인 타겟을 제외하고는, 스플래시 데미지를 입는데, // 스플래시 데미지는 일반 데미지의 50%다. if (pTargetCreature->getX() != X || pTargetCreature->getY() != Y) { Damage = Damage/2; } // 소드웨이브와는 달리 크로스 카운터 체크는 하지 않는다. ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); setDamage(pTargetCreature, Damage, pSlayer, getSkillType(), NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pSlayer, NULL, &_GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bCriticalHit) { knockbackCreature(pZone, pTargetCreature, pSlayer->getX(), pSlayer->getY()); } // 슬레이어가 아닐 경우에만 맞춘 걸로 간주한다. if (!pTargetCreature->isSlayer()) { bHit = true; if (maxEnemyLevel < pTargetCreature->getLevel() ) maxEnemyLevel = pTargetCreature->getLevel(); EnemyNum++; } } } if (bHit) { if (bIncreaseExp) { increaseDomainExp(pSlayer, DomainType , pSkillInfo->getPoint(), _GCSkillToTileOK1, maxEnemyLevel, EnemyNum); shareAttrExp(pSlayer, Damage , 1, 8, 1, _GCSkillToTileOK1); } increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCSkillToTileOK1); } _GCSkillToTileOK1.addShortData(MODIFY_BULLET, RemainBullet); decreaseDurability(pSlayer, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(getSkillType()); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK2.setSkillType(getSkillType()); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK3.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK3.setSkillType(getSkillType()); _GCSkillToTileOK3.setX(X); _GCSkillToTileOK3.setY(Y); _GCSkillToTileOK4.setSkillType(getSkillType()); _GCSkillToTileOK4.setX(X); _GCSkillToTileOK4.setY(Y); _GCSkillToTileOK4.setDuration(0); _GCSkillToTileOK4.setRange(dir); _GCSkillToTileOK5.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK5.setSkillType(getSkillType()); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(dir); _GCSkillToTileOK5.setDuration(0); pPlayer->sendPacket(&_GCSkillToTileOK1); // 이 기술에 의해 영향을 받는 놈들에게 패킷을 보내줘야 한다. for(list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature* pTargetCreature = *itr; Assert(pTargetCreature != NULL); if (pTargetCreature->isPC()) { _GCSkillToTileOK2.clearList(); HP_t targetHP = 0; if (pTargetCreature->isSlayer()) { targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(); } else if (pTargetCreature->isVampire()) { targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(); } _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. Player* pPlayer = pTargetCreature->getPlayer(); Assert(pPlayer != NULL); pPlayer->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pSlayer); } } cList.push_back(pSlayer); cList = pZone->broadcastSkillPacket(myX, myY, X, Y, &_GCSkillToTileOK5, cList); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK3 , cList); pZone->broadcastPacket(X, Y, &_GCSkillToTileOK4 , cList); pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormalWithGun(pSlayer, getSkillType(), NULL, RemainBullet); } } catch (Throwable & t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " SGexecute End" << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 오브젝트 ////////////////////////////////////////////////////////////////////////////// void JabbingVein::execute (Slayer* pSlayer, ObjectID_t TargetObjectID, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY __BEGIN_DEBUG //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Player* pPlayer = pSlayer->getPlayer(); Zone* pZone = pSlayer->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); Creature* pTargetCreature = pZone->getCreature(TargetObjectID); //Assert(pTargetCreature != NULL); // NoSuch제거. by sigi. 2002.5.2 if (pTargetCreature==NULL || !canAttack(pSlayer, pTargetCreature ) || pTargetCreature->isNPC()) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } GCAttackArmsOK1 _GCAttackArmsOK1; GCAttackArmsOK2 _GCAttackArmsOK2; GCAttackArmsOK3 _GCAttackArmsOK3; GCAttackArmsOK4 _GCAttackArmsOK4; GCAttackArmsOK5 _GCAttackArmsOK5; // 들고 있는 무기가 없거나, 총 계열 무기가 아니라면 기술을 쓸 수 없다. // 총 계열 무기 중에서도 SG나 SR은 JabbingVein를 쓸 수가 없다. // SG, SR 도 이제 쓸 수 있다. // 2003. 1. 14 by bezz Item* pWeapon = pSlayer->getWearItem(Slayer::WEAR_RIGHTHAND); if (pWeapon == NULL || isArmsWeapon(pWeapon) == false ) // pWeapon->getItemClass() == Item::ITEM_CLASS_SG || // pWeapon->getItemClass() == Item::ITEM_CLASS_SR) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } bool bIncreaseExp = pSlayer->isRealWearingEx(Slayer::WEAR_RIGHTHAND); SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); SkillLevel_t SkillLevel = pSkillSlot->getExpLevel(); SkillInput input(pSlayer, pSkillSlot); SkillOutput output; if (pTargetCreature->isPC() ) { input.TargetType = SkillInput::TARGET_PC; } else { input.TargetType = SkillInput::TARGET_MONSTER; } computeOutput(input, output); // 페널티 값을 계산한다. int ToHitPenalty = getPercentValue(pSlayer->getToHit(), output.ToHit); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, pTargetCreature, pWeapon->getRange()); bool bBulletCheck = (getRemainBullet(pWeapon) > 0) ? true : false; bool bHitRoll = HitRoll::isSuccess(pSlayer, pTargetCreature, ToHitPenalty); bool bPK = verifyPK(pSlayer, pTargetCreature); // 총알 숫자는 무조건 떨어뜨린다. Bullet_t RemainBullet = 0; if (bBulletCheck) { // 총알 숫자를 떨어뜨리고, 저장하고, 남은 총알 숫자를 받아온다. decreaseBullet(pWeapon); // 한발쓸때마다 저장할 필요 없다. by sigi. 2002.5.9 //pWeapon->save(pSlayer->getName(), STORAGE_GEAR, 0, Slayer::WEAR_RIGHTHAND, 0); RemainBullet = getRemainBullet(pWeapon); } if (bManaCheck && bTimeCheck && bRangeCheck && bBulletCheck && bHitRoll && bPK) { //cout << pSlayer->getName().c_str() << " Attack OK" << endl; decreaseMana(pSlayer, RequiredMP, _GCAttackArmsOK1); _GCAttackArmsOK5.setSkillSuccess(true); _GCAttackArmsOK1.setSkillSuccess(true); bool bCriticalHit = false; // 데미지를 계산하고, quickfire 페널티를 가한다. // output.Damage가 음수이기 때문에, %값을 구해 더하면 결국 빼는 것이 된다. int Damage = computeDamage(pSlayer, pTargetCreature, SkillLevel/5, bCriticalHit); Damage += getPercentValue(Damage, output.Damage); Damage = max(0, Damage); //cout << "JabbingVeinDamage:" << Damage << endl; // 데미지를 세팅한다. setDamage(pTargetCreature, Damage, pSlayer, SkillType, &_GCAttackArmsOK2, &_GCAttackArmsOK1); computeAlignmentChange(pTargetCreature, Damage, pSlayer, &_GCAttackArmsOK2, &_GCAttackArmsOK1); bool bAffect = true; if (pTargetCreature->isMonster()) { Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); if (pMonster->isMaster() ) bAffect = false; } if (bAffect && !pTargetCreature->isFlag(Effect::EFFECT_CLASS_JABBING_VEIN) && rand()%100 < output.Range ) { EffectJabbingVein* pEffect = new EffectJabbingVein(pTargetCreature); pEffect->setDeadline(output.Duration); pTargetCreature->addEffect(pEffect); pTargetCreature->setFlag(pEffect->getEffectClass()); GCAddEffect gcAddEffect; gcAddEffect.setObjectID(pTargetCreature->getObjectID()); gcAddEffect.setEffectID(pEffect->getSendEffectClass()); gcAddEffect.setDuration(output.Duration); pZone->broadcastPacket(pTargetCreature->getX(), pTargetCreature->getY(), &gcAddEffect); } // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bCriticalHit) { knockbackCreature(pZone, pTargetCreature, pSlayer->getX(), pSlayer->getY()); } if(!pTargetCreature->isSlayer() ) { if (bIncreaseExp) { shareAttrExp(pSlayer, Damage , 1, 8, 1, _GCAttackArmsOK1); increaseDomainExp(pSlayer, DomainType, pSkillInfo->getPoint(), _GCAttackArmsOK1, pTargetCreature->getLevel()); increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCAttackArmsOK1); } increaseAlignment(pSlayer, pTargetCreature, _GCAttackArmsOK1); } //} if (pTargetCreature->isPC()) { Player* pTargetPlayer = pTargetCreature->getPlayer(); if (pTargetPlayer != NULL) { _GCAttackArmsOK2.setObjectID(getSkillType()); _GCAttackArmsOK2.setObjectID(pSlayer->getObjectID()); pTargetPlayer->sendPacket(&_GCAttackArmsOK2); } } else if (pTargetCreature->isMonster()) { Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pSlayer); } // 공격자와 상대의 아이템 내구성 떨어트림. decreaseDurability(pSlayer, pTargetCreature, NULL, &_GCAttackArmsOK1, &_GCAttackArmsOK2); ZoneCoord_t targetX = pTargetCreature->getX(); ZoneCoord_t targetY = pTargetCreature->getY(); ZoneCoord_t myX = pSlayer->getX(); ZoneCoord_t myY = pSlayer->getY(); _GCAttackArmsOK1.setSkillType(getSkillType()); _GCAttackArmsOK1.setObjectID(TargetObjectID); _GCAttackArmsOK1.setBulletNum(RemainBullet); _GCAttackArmsOK3.setSkillType(getSkillType()); _GCAttackArmsOK3.setObjectID(pSlayer->getObjectID()); _GCAttackArmsOK3.setTargetXY (targetX, targetY); _GCAttackArmsOK4.setSkillType(getSkillType()); _GCAttackArmsOK4.setTargetObjectID (TargetObjectID); _GCAttackArmsOK5.setSkillType(getSkillType()); _GCAttackArmsOK5.setObjectID(pSlayer->getObjectID()); _GCAttackArmsOK5.setTargetObjectID (TargetObjectID); pPlayer->sendPacket(&_GCAttackArmsOK1); list<Creature *> cList; cList.push_back(pTargetCreature); cList.push_back(pSlayer); cList = pZone->broadcastSkillPacket(myX, myY, targetX, targetY, &_GCAttackArmsOK5, cList); pZone->broadcastPacket(myX, myY, &_GCAttackArmsOK3, cList); pZone->broadcastPacket(targetX, targetY, &_GCAttackArmsOK4, cList); pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormalWithGun(pSlayer, getSkillType(), pTargetCreature, RemainBullet); //cout << pSlayer->getName().c_str() << " Fail : " // << (int)bManaCheck << (int)bTimeCheck << (int)bRangeCheck // << (int)bBulletCheck << (int)bHitRoll << (int)bPK << endl; } } catch (Throwable &t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; __END_DEBUG __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 오브젝트 핸들러 ////////////////////////////////////////////////////////////////////////////// void Paralyze::execute(Vampire* pVampire, ObjectID_t TargetObjectID, VampireSkillSlot* pVampireSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << "begin " << endl; Assert(pVampire != NULL); Assert(pVampireSkillSlot != NULL); try { Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); Creature* pTargetCreature = pZone->getCreature(TargetObjectID); SkillType_t SkillType = pVampireSkillSlot->getSkillType(); // NPC는 공격할 수가 없다. // 면역이거나. by sigi. 2002.9.13 // NoSuch제거. by sigi. 2002.5.2 if (pTargetCreature==NULL || pTargetCreature->isFlag(Effect::EFFECT_CLASS_IMMUNE_TO_PARALYZE) || !canAttack(pVampire, pTargetCreature ) || pTargetCreature->isNPC()) { executeSkillFailException(pVampire, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; return; } GCSkillToObjectOK1 _GCSkillToObjectOK1; GCSkillToObjectOK2 _GCSkillToObjectOK2; GCSkillToObjectOK3 _GCSkillToObjectOK3; GCSkillToObjectOK4 _GCSkillToObjectOK4; GCSkillToObjectOK5 _GCSkillToObjectOK5; GCSkillToObjectOK6 _GCSkillToObjectOK6; SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); // Knowledge of Curse 가 있다면 hit bonus 10 int HitBonus = 0; if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_CURSE ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_CURSE); Assert(pRankBonus != NULL); HitBonus = pRankBonus->getPoint(); } Tile& rTile = pZone->getTile(pTargetCreature->getX(), pTargetCreature->getY()); int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pVampireSkillSlot); bool bRangeCheck = verifyDistance(pVampire, pTargetCreature, pSkillInfo->getRange()); bool bHitRoll = HitRoll::isSuccessVampireCurse(pSkillInfo->getLevel(), pTargetCreature->getResist(MAGIC_DOMAIN_CURSE)); bool bHitRoll2 = HitRoll::isSuccessMagic(pVampire, pSkillInfo, pVampireSkillSlot, HitBonus); bool bCanHit = canHit(pVampire, pTargetCreature, SkillType); bool bEffected = pTargetCreature->isFlag(Effect::EFFECT_CLASS_PARALYZE) || rTile.getEffect(Effect::EFFECT_CLASS_TRYING_POSITION)!=NULL; bool bPK = verifyPK(pVampire, pTargetCreature); ZoneCoord_t vampX = pVampire->getX(); ZoneCoord_t vampY = pVampire->getY(); ZoneCoord_t targetX = pTargetCreature->getX(); ZoneCoord_t targetY = pTargetCreature->getY(); //if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll && bHitRoll2 && bCanHit && !bEffected && bPK) if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll && bHitRoll2 && bCanHit && !bEffected && bPK && pTargetCreature->getCompetence() == 3) { decreaseMana(pVampire, RequiredMP, _GCSkillToObjectOK1); bool bCanSeeCaster = canSee(pTargetCreature, pVampire); SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); // Wisdom of Silence 이 있다면 지속시간 20% 증가 if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_WISDOM_OF_SILENCE ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_WISDOM_OF_SILENCE); Assert(pRankBonus != NULL); output.Duration += getPercentValue(output.Duration, pRankBonus->getPoint()); } // pTargetCreature가 저주마법을 반사하는 경우 if (CheckReflection(pVampire, pTargetCreature, getSkillType())) { pTargetCreature = (Creature*)pVampire; TargetObjectID = pVampire->getObjectID(); } Resist_t resist = pTargetCreature->getResist(MAGIC_DOMAIN_CURSE); if (resist > output.Duration ) resist = output.Duration; output.Duration -= resist; if (output.Duration < 20 ) output.Duration = 20; // 이펙트 오브젝트를 생성해서 붙인다. EffectParalyze* pEffectParalyze = new EffectParalyze(pTargetCreature); pEffectParalyze->setLevel(pSkillInfo->getLevel()/2); // pEffectParalyze->setDefensePenalty(output.Damage); pEffectParalyze->setDeadline(output.Duration); pTargetCreature->addEffect(pEffectParalyze); pTargetCreature->setFlag(Effect::EFFECT_CLASS_PARALYZE); if (pTargetCreature->isFlag(Effect::EFFECT_CLASS_BURNING_SOL_CHARGE_1 ) ) { Effect* pEffect = pTargetCreature->findEffect(Effect::EFFECT_CLASS_BURNING_SOL_CHARGE_1); if (pEffect != NULL ) pEffect->setDeadline(0); } // 저주에 걸리면 디펜스가 떨어진다. // 디펜스 페널티가 없어짐. 2002.05.09 - by bezz /* if (pTargetCreature->isSlayer()) { Slayer* pTargetSlayer = dynamic_cast<Slayer*>(pTargetCreature); SLAYER_RECORD prev; pTargetSlayer->getSlayerRecord(prev); pTargetSlayer->initAllStat(); pTargetSlayer->addModifyInfo(prev, _GCSkillToObjectOK2); } else if (pTargetCreature->isVampire()) { Vampire* pTargetVampire = dynamic_cast<Vampire*>(pTargetCreature); VAMPIRE_RECORD prev; pTargetVampire->getVampireRecord(prev); pTargetVampire->initAllStat(); pTargetVampire->addModifyInfo(prev, _GCSkillToObjectOK2); } else if (pTargetCreature->isMonster()) { Monster* pTargetMonster = dynamic_cast<Monster*>(pTargetCreature); pTargetMonster->initAllStat(); } else Assert(false); */ // 이펙트가 붙었다는 것을 브로드캐스팅해준다. GCAddEffect gcAddEffect; gcAddEffect.setObjectID(pTargetCreature->getObjectID()); gcAddEffect.setEffectID(Effect::EFFECT_CLASS_PARALYZE); gcAddEffect.setDuration(output.Duration); pZone->broadcastPacket(targetX, targetY, &gcAddEffect); _GCSkillToObjectOK1.setSkillType(SkillType); _GCSkillToObjectOK1.setCEffectID(CEffectID); _GCSkillToObjectOK1.setTargetObjectID(TargetObjectID); _GCSkillToObjectOK1.setDuration(output.Duration); _GCSkillToObjectOK2.setObjectID(pVampire->getObjectID()); _GCSkillToObjectOK2.setSkillType(SkillType); _GCSkillToObjectOK2.setDuration(output.Duration); _GCSkillToObjectOK3.setObjectID(pVampire->getObjectID()); _GCSkillToObjectOK3.setSkillType(SkillType); _GCSkillToObjectOK3.setTargetXY(targetX, targetY); _GCSkillToObjectOK4.setSkillType(SkillType); _GCSkillToObjectOK4.setTargetObjectID(TargetObjectID); _GCSkillToObjectOK5.setObjectID(pVampire->getObjectID()); _GCSkillToObjectOK5.setTargetObjectID(TargetObjectID); _GCSkillToObjectOK5.setSkillType(SkillType); _GCSkillToObjectOK5.setDuration(output.Duration); _GCSkillToObjectOK6.setXY(vampX, vampY); _GCSkillToObjectOK6.setSkillType(SkillType); _GCSkillToObjectOK6.setDuration(output.Duration); if (bCanSeeCaster) // 10은 땜빵 수치다. { computeAlignmentChange(pTargetCreature, 10, pVampire, &_GCSkillToObjectOK2, &_GCSkillToObjectOK1); } else // 10은 땜빵 수치다. { computeAlignmentChange(pTargetCreature, 10, pVampire, &_GCSkillToObjectOK6, &_GCSkillToObjectOK1); } pPlayer->sendPacket(&_GCSkillToObjectOK1); if (pTargetCreature->isPC()) { Player* pTargetPlayer = pTargetCreature->getPlayer(); Assert(pTargetPlayer != NULL); if (bCanSeeCaster) pTargetPlayer->sendPacket(&_GCSkillToObjectOK2); else pTargetPlayer->sendPacket(&_GCSkillToObjectOK6); } else { Monster* pTargetMonster = dynamic_cast<Monster*>(pTargetCreature); pTargetMonster->addEnemy(pVampire); } list<Creature*> cList; cList.push_back(pVampire); cList.push_back(pTargetCreature); cList = pZone->broadcastSkillPacket(vampX, vampY, targetX, targetY, &_GCSkillToObjectOK5, cList); pZone->broadcastPacket(vampX, vampY, &_GCSkillToObjectOK3 , cList); pZone->broadcastPacket(targetX, targetY, &_GCSkillToObjectOK4 , cList); pVampireSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, getSkillType(), pTargetCreature); } } catch(Throwable & t) { executeSkillFailException(pVampire, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 타일 핸들러 ////////////////////////////////////////////////////////////////////////////// void Darkness::execute(Vampire* pVampire, ZoneCoord_t X, ZoneCoord_t Y, VampireSkillSlot* pVampireSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pVampire != NULL); Assert(pVampireSkillSlot != NULL); try { Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK3 _GCSkillToTileOK3; GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; GCSkillToTileOK6 _GCSkillToTileOK6; SkillType_t SkillType = pVampireSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); ZoneCoord_t myX = pVampire->getX(); ZoneCoord_t myY = 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(pVampireSkillSlot); bool bRangeCheck = verifyDistance(pVampire, X, Y, pSkillInfo->getRange()); bool bHitRoll = HitRoll::isSuccessMagic(pVampire, pSkillInfo, pVampireSkillSlot, HitBonus); bool bSlayerSafeZone = pZone->getZoneLevel(X, Y ) & SLAYER_SAFE_ZONE; bool bTileCheck = false; VSRect rect(0, 0, pZone->getWidth()-1, pZone->getHeight()-1); if (rect.ptInRect(X, Y)) bTileCheck = true; if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll && bTileCheck && !bSlayerSafeZone) { decreaseMana(pVampire, RequiredMP, _GCSkillToTileOK1); // 이펙트의 지속시간을 계산한다. SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); // Wisdom of Darkness 이 있다면 지속시간 30% 증가 if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_WISDOM_OF_DARKNESS ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_WISDOM_OF_DARKNESS); Assert(pRankBonus != NULL); output.Duration += getPercentValue(output.Duration, pRankBonus->getPoint()); } Range_t Range = 3; int oX, oY; list<Creature*> cList; // denier list int edge = 1; // Wide Darkness 이 있다면 범위가 5*5 로 수정. skill type 을 수정한다. if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_WIDE_DARKNESS ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_WIDE_DARKNESS); Assert(pRankBonus != NULL); Range = pRankBonus->getPoint(); edge = (pRankBonus->getPoint() - 1 ) / 2; SkillType = SKILL_DARKNESS_WIDE; } // map<int, uint> canAddMap; for(oY = -edge; oY <= edge; oY++) for(oX = -edge; oX <= edge; oX++) { int tileX = X+oX; int tileY = Y+oY; if (rect.ptInRect(tileX, tileY)) { Tile& tile = pZone->getTile(tileX, tileY); // if (canAddMap[normalizeCoord_DARKNESS(oX, oY, edge )] == 1 ) continue; if (tile.hasItem() ) { Item* pItem = tile.getItem(); if (pItem != NULL && pItem->getItemClass() == Item::ITEM_CLASS_CORPSE && pItem->getItemType() == MONSTER_CORPSE ) { MonsterCorpse* pMonsterCorpse = dynamic_cast<MonsterCorpse*>(pItem); if (g_pFlagManager->isFlagPole(pMonsterCorpse ) ) { // canAddMap[normalizeCoord_DARKNESS(oX+1, oY, edge )] = 1; // canAddMap[normalizeCoord_DARKNESS(oX+1, oY+1, edge )] = 1; // canAddMap[normalizeCoord_DARKNESS(oX, oY+1, edge )] = 1; continue; } } } if (tile.getEffect(Effect::EFFECT_CLASS_TRYING_POSITION ) != NULL ) continue; // 현재 타일에다 이펙트를 추가할 수 있다면... if (tile.canAddEffect()) { // 머시 그라운드 있음 추가 못한당. if (tile.getEffect(Effect::EFFECT_CLASS_MERCY_GROUND) != NULL ) continue; if (tile.getEffect(Effect::EFFECT_CLASS_DARKNESS_FORBIDDEN) != NULL ) continue; // 같은 effect가 있으면 지운다. Effect* pOldEffect = tile.getEffect(Effect::EFFECT_CLASS_DARKNESS); if (pOldEffect != NULL) { ObjectID_t effectID = pOldEffect->getObjectID(); pZone->deleteEffect(effectID);// fix me } // 이펙트 클래스를 생성한다. EffectDarkness* pEffect = new EffectDarkness(pZone , tileX, tileY); pEffect->setDeadline(output.Duration); pEffect->setLevel(pVampire->getINT()); pEffect->setDuration(output.Duration); pEffect->setStartTime(); // Tile에 붙이는 Effect는 ObjectID를 등록받아야 한다. ObjectRegistry & objectregister = pZone->getObjectRegistry(); objectregister.registerObject(pEffect); pZone->addEffect(pEffect); tile.addEffect(pEffect); const list<Object*>& oList = tile.getObjectList(); for(list<Object*>::const_iterator itr = oList.begin(); itr != oList.end(); itr++) { Object* pTarget = *itr; Creature* pTargetCreature = NULL; if (pTarget->getObjectClass() == Object::OBJECT_CLASS_CREATURE && ((pTargetCreature = dynamic_cast<Creature*>(pTarget))->isSlayer() || pTargetCreature->isOusters() ) ) { cList.push_back(pTargetCreature); _GCSkillToTileOK2.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK4.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK5.addCListElement(pTargetCreature->getObjectID()); } pEffect->affectObject(pTarget, false); } } } } _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setDuration(output.Duration); _GCSkillToTileOK1.setRange(Range); _GCSkillToTileOK2.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setDuration(output.Duration); _GCSkillToTileOK2.setRange(Range); //_GCSkillToTileOK2.addShortData(MODIFY_VISION, DARKNESS_SIGHT); _GCSkillToTileOK3.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK3.setSkillType(SkillType); _GCSkillToTileOK3.setX(X); _GCSkillToTileOK3.setY(Y); _GCSkillToTileOK4.setSkillType(SkillType); _GCSkillToTileOK4.setX(X); _GCSkillToTileOK4.setY(Y); _GCSkillToTileOK4.setRange(Range); _GCSkillToTileOK4.setDuration(output.Duration); _GCSkillToTileOK5.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(Range); _GCSkillToTileOK5.setDuration(output.Duration); _GCSkillToTileOK6.setOrgXY(myX, myY); _GCSkillToTileOK6.setSkillType(SkillType); _GCSkillToTileOK6.setX(X); _GCSkillToTileOK6.setY(Y); _GCSkillToTileOK6.setDuration(output.Duration); _GCSkillToTileOK6.setRange(Range); //_GCSkillToTileOK6.addShortData(MODIFY_VISION, DARKNESS_SIGHT); for(list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature* pTargetCreature = *itr; if (canSee(pTargetCreature, pVampire)) pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); else pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK6); } pPlayer->sendPacket(&_GCSkillToTileOK1); cList.push_back(pVampire); list<Creature*> watcherList = pZone->getWatcherList(myX, myY, pVampire); // watcherList에서 cList에 속하지 않고, caster(pVampire)를 볼 수 없는 경우는 // OK4를 보내고.. cList에 추가한다. for(list<Creature*>::const_iterator itr = watcherList.begin(); itr != watcherList.end(); itr++) { bool bBelong = false; for(list<Creature*>::const_iterator tItr = cList.begin(); tItr != cList.end(); tItr++) if (*itr == *tItr) bBelong = true; Creature* pWatcher = (*itr); if (bBelong == false && canSee(pWatcher, pVampire) == false) { //Assert(pWatcher->isPC()); // 당연 PC다.. Zone::getWatcherList는 PC만 return한다 if (!pWatcher->isPC()) { //cout << "Darkness : 왓처 리스트가 PC가 아닙니다." << endl; GCSkillFailed1 _GCSkillFailed1; _GCSkillFailed1.setSkillType(getSkillType()); pVampire->getPlayer()->sendPacket(&_GCSkillFailed1); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; return; } pWatcher->getPlayer()->sendPacket(&_GCSkillToTileOK4); cList.push_back(*itr); } } cList = pZone->broadcastSkillPacket(myX, myY, X, Y, &_GCSkillToTileOK5, cList, false); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK3 , cList); pZone->broadcastPacket(X, Y, &_GCSkillToTileOK4 , cList); pVampireSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, getSkillType(), NULL); } } catch (Throwable & t) { executeSkillFailException(pVampire, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; __END_CATCH }