////////////////////////////////////////////////////////////////////////////// // 뱀파이어 타일 핸들러 ////////////////////////////////////////////////////////////////////////////// void IceField::execute(Ousters* pOusters, ZoneCoord_t X, ZoneCoord_t Y, OustersSkillSlot* pOustersSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " Begin" << endl; Assert(pOusters != NULL); Assert(pOustersSkillSlot != NULL); try { Player* pPlayer = pOusters->getPlayer(); Zone* pZone = pOusters->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); Item* pWeapon = pOusters->getWearItem(Ousters::WEAR_RIGHTHAND); if (pWeapon == NULL || pWeapon->getItemClass() != Item::ITEM_CLASS_OUSTERS_WRISTLET || !pOusters->isRealWearingEx(Ousters::WEAR_RIGHTHAND)) { executeSkillFailException(pOusters, pOustersSkillSlot->getSkillType()); return; } GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK3 _GCSkillToTileOK3; GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; GCSkillToTileOK6 _GCSkillToTileOK6; SkillType_t SkillType = pOustersSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); ZoneCoord_t myX = pOusters->getX(); ZoneCoord_t myY = pOusters->getY(); // 이펙트의 지속시간을 계산한다. SkillInput input(pOusters, pOustersSkillSlot); SkillOutput output; computeOutput(input, output); int RequiredMP = (int)pSkillInfo->getConsumeMP() + pOustersSkillSlot->getExpLevel(); bool bManaCheck = hasEnoughMana(pOusters, RequiredMP); bool bTimeCheck = verifyRunTime(pOustersSkillSlot); bool bRangeCheck = verifyDistance(pOusters, X, Y, pSkillInfo->getRange()); bool bHitRoll = HitRoll::isSuccessMagic(pOusters, pSkillInfo, pOustersSkillSlot); bool bSatisfyRequire = pOusters->satisfySkillRequire(pSkillInfo); 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 && bSatisfyRequire) { decreaseMana(pOusters, RequiredMP, _GCSkillToTileOK1); int grade = 0; if (input.SkillLevel <= 10 ) grade = 0; else if (input.SkillLevel <= 20 ) grade = 1; else if (input.SkillLevel < 30 ) grade = 2; else grade = 3; list<Creature*> cList; // denier list for (int i=0; i<m_MaskNum[grade]; i++) { POINT& pt = m_IceFieldMask[grade][i]; int tileX = X+pt.x; int tileY = Y+pt.y; if (rect.ptInRect(tileX, tileY)) { Tile& tile = pZone->getTile(tileX, tileY); if (tile.getEffect(Effect::EFFECT_CLASS_TRYING_POSITION) ) continue; // 현재 타일에다 이펙트를 추가할 수 있다면... if (tile.canAddEffect()) { // 같은 effect가 있으면 지운다. Effect* pOldEffect = tile.getEffect(Effect::EFFECT_CLASS_ICE_FIELD); if (pOldEffect != NULL) { ObjectID_t effectID = pOldEffect->getObjectID(); pZone->deleteEffect(effectID);// fix me } // 이펙트 클래스를 생성한다. EffectIceField* pEffect = new EffectIceField(pZone , tileX, tileY); pEffect->setCasterName(pOusters->getName()); pEffect->setCasterID(pOusters->getObjectID()); pEffect->setDeadline(output.Duration); pEffect->setDuration(output.Range); pEffect->setNextTime(0); pEffect->setTick(output.Tick); // 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->isVampire() ) && !checkZoneLevelToHitTarget(pTargetCreature ) ) { cList.push_back(pTargetCreature); _GCSkillToTileOK2.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK4.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK5.addCListElement(pTargetCreature->getObjectID()); pEffect->affect(pTargetCreature); } // pEffect->affect(pTarget); } } } } _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setDuration(output.Duration); _GCSkillToTileOK1.setRange(grade); _GCSkillToTileOK2.setObjectID(pOusters->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setDuration(output.Duration); _GCSkillToTileOK2.setRange(grade); //_GCSkillToTileOK2.addShortData(MODIFY_VISION, ICE_FIELD_SIGHT); _GCSkillToTileOK3.setObjectID(pOusters->getObjectID()); _GCSkillToTileOK3.setSkillType(SkillType); _GCSkillToTileOK3.setX(X); _GCSkillToTileOK3.setY(Y); _GCSkillToTileOK4.setSkillType(SkillType); _GCSkillToTileOK4.setX(X); _GCSkillToTileOK4.setY(Y); _GCSkillToTileOK4.setRange(grade); _GCSkillToTileOK4.setDuration(output.Duration); _GCSkillToTileOK5.setObjectID(pOusters->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(grade); _GCSkillToTileOK5.setDuration(output.Duration); _GCSkillToTileOK6.setOrgXY(myX, myY); _GCSkillToTileOK6.setSkillType(SkillType); _GCSkillToTileOK6.setX(X); _GCSkillToTileOK6.setY(Y); _GCSkillToTileOK6.setDuration(output.Duration); _GCSkillToTileOK6.setRange(grade); //_GCSkillToTileOK6.addShortData(MODIFY_VISION, ICE_FIELD_SIGHT); for(list<Creature*>::const_iterator itr = cList.begin(); itr != cList.end(); itr++) { Creature* pTargetCreature = *itr; if (canSee(pTargetCreature, pOusters)) pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); else pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK6); } pPlayer->sendPacket(&_GCSkillToTileOK1); cList.push_back(pOusters); list<Creature*> watcherList = pZone->getWatcherList(myX, myY, pOusters); // watcherList에서 cList에 속하지 않고, caster(pOusters)를 볼 수 없는 경우는 // 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, pOusters) == false) { //Assert(pWatcher->isPC()); // 당연 PC다.. Zone::getWatcherList는 PC만 return한다 if (!pWatcher->isPC()) { //cout << "IceField : 왓처 리스트가 PC가 아닙니다." << endl; // GCSkillFailed1 _GCSkillFailed1; // _GCSkillFailed1.setSkillType(getSkillType()); // pOusters->getPlayer()->sendPacket(&_GCSkillFailed1); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; // return; continue; } 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); pOustersSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pOusters, getSkillType(), NULL); } } catch (Throwable & t) { executeSkillFailException(pOusters, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " End" << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 타일 핸들러 ////////////////////////////////////////////////////////////////////////////// void BloodyWall::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 Blood 가 있다면 hit bonus 10 int HitBonus = 0; if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD); 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 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) { decreaseMana(pVampire, RequiredMP, _GCSkillToTileOK1); // 이펙트의 지속시간을 계산한다. SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); Dir_t Dir = getDirectionToPosition(myX, myY, X, Y); list<Creature*> cList; // denier list for (int i=0; i<5; i++) { POINT& pt = m_BloodyWallMask[Dir][i]; int tileX = X+pt.x; int tileY = Y+pt.y; if (rect.ptInRect(tileX, tileY)) { Tile& tile = pZone->getTile(tileX, tileY); if (tile.getEffect(Effect::EFFECT_CLASS_TRYING_POSITION )!=NULL ) continue; // 현재 타일에다 이펙트를 추가할 수 있다면... if (tile.canAddEffect()) { // 같은 effect가 있으면 지운다. Effect* pOldEffect = tile.getEffect(Effect::EFFECT_CLASS_BLOODY_WALL); if (pOldEffect != NULL) { ObjectID_t effectID = pOldEffect->getObjectID(); pZone->deleteEffect(effectID);// fix me } // 이펙트 클래스를 생성한다. EffectBloodyWall* pEffect = new EffectBloodyWall(pZone , tileX, tileY); pEffect->setCasterName(pVampire->getName()); pEffect->setCasterID(pVampire->getObjectID()); pEffect->setClan(Creature::CREATURE_CLASS_VAMPIRE, pVampire->getClanType()); pEffect->setDamage(output.Damage); pEffect->setDeadline(output.Duration); pEffect->setLevel(pVampire->getINT()); pEffect->setNextTime(0); pEffect->setTick(output.Tick); // 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() ) && !checkZoneLevelToHitTarget(pTargetCreature ) ) { cList.push_back(pTargetCreature); _GCSkillToTileOK2.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK4.addCListElement(pTargetCreature->getObjectID()); _GCSkillToTileOK5.addCListElement(pTargetCreature->getObjectID()); pEffect->affect(pTargetCreature); } // pEffect->affect(pTarget); } } } } _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setDuration(output.Duration); _GCSkillToTileOK1.setRange(Dir); _GCSkillToTileOK2.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setDuration(output.Duration); _GCSkillToTileOK2.setRange(Dir); //_GCSkillToTileOK2.addShortData(MODIFY_VISION, BLOODY_WALL_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(Dir); _GCSkillToTileOK4.setDuration(output.Duration); _GCSkillToTileOK5.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _GCSkillToTileOK5.setX(X); _GCSkillToTileOK5.setY(Y); _GCSkillToTileOK5.setRange(Dir); _GCSkillToTileOK5.setDuration(output.Duration); _GCSkillToTileOK6.setOrgXY(myX, myY); _GCSkillToTileOK6.setSkillType(SkillType); _GCSkillToTileOK6.setX(X); _GCSkillToTileOK6.setY(Y); _GCSkillToTileOK6.setDuration(output.Duration); _GCSkillToTileOK6.setRange(Dir); //_GCSkillToTileOK6.addShortData(MODIFY_VISION, BLOODY_WALL_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 << "BloodyWall : 왓처 리스트가 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 }
void EffectSpiritGuard::affect(Creature* pCastCreature) throw(Error) { __BEGIN_TRY Assert(pCastCreature != NULL); if (!pCastCreature->isSlayer() ) return; Player* pPlayer = dynamic_cast<Player*>(pCastCreature->getPlayer()); Assert(pPlayer != NULL); Slayer* pSlayer = dynamic_cast<Slayer*>(pCastCreature); Assert(pSlayer != NULL); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SKILL_SPIRIT_GUARD); if (pSkillInfo == NULL ) { return; } GCModifyInformation gcAttackerMI; Zone* pZone = pCastCreature->getZone(); Assert(pZone != NULL); VSRect rect(0, 0, pZone->getWidth()-1, pZone->getHeight()-1); ZoneCoord_t Cx = pCastCreature->getX(); ZoneCoord_t Cy = pCastCreature->getY(); bool isHit = false; Level_t maxEnemyLevel = 0; uint EnemyNum = 0; for (int x=-1; x<=1; x++ ) { for (int y=-1; y<=1; y++ ) { if (x == 0 && y == 0 ) continue; int X = Cx + x; int Y = Cy + y; if (!rect.ptInRect(X, Y ) ) continue; // 타일안에 존재하는 오브젝트를 가져온다. Tile& tile = pZone->getTile(X, Y); if(tile.hasCreature(Creature::MOVE_MODE_WALKING) ) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); Assert(pCreature != NULL); // 자신은 맞지 않는다. 무적도 안 맞는다. 슬레이어도 안 맞느다. // 안전지대 체크 // 2003.1.10 by bezz, Sequoia if (pCreature == m_pTarget || !canAttack(pCastCreature, pCreature ) || pCreature->isFlag(Effect::EFFECT_CLASS_COMA ) || pCreature->isSlayer() || pCreature->isNPC() || !checkZoneLevelToHitTarget(pCreature) ) { continue; } isHit = true; if (maxEnemyLevel < pCreature->getLevel() ) maxEnemyLevel = pCreature->getLevel(); EnemyNum++; if (pCreature->isVampire() || pCreature->isOusters() ) { // Vampire* pVampire = dynamic_cast<Vampire*>(pCreature); GCModifyInformation gcMI; ::setDamage(pCreature, m_Damage, pCastCreature, SKILL_SPIRIT_GUARD, &gcMI, &gcAttackerMI); pCreature->getPlayer()->sendPacket(&gcMI); // 맞는 동작을 보여준다. GCSkillToObjectOK2 gcSkillToObjectOK2; gcSkillToObjectOK2.setObjectID(1); // 의미 없다. gcSkillToObjectOK2.setSkillType(SKILL_ATTACK_MELEE); gcSkillToObjectOK2.setDuration(0); pCreature->getPlayer()->sendPacket(&gcSkillToObjectOK2); } else if (pCreature->isMonster() ) { Monster* pMonster = dynamic_cast<Monster*>(pCreature); ::setDamage(pMonster, m_Damage, pCastCreature, SKILL_SPIRIT_GUARD, NULL, &gcAttackerMI); pMonster->addEnemy(pCastCreature); } else Assert(false); GCSkillToObjectOK4 gcSkillToObjectOK4; gcSkillToObjectOK4.setSkillType(SKILL_ATTACK_MELEE); gcSkillToObjectOK4.setTargetObjectID(pCreature->getObjectID()); gcSkillToObjectOK4.setDuration(0); pZone->broadcastPacket(X, Y, &gcSkillToObjectOK4, pCreature); } } } if (isHit ) { SkillDomainType_t DomainType = pSkillInfo->getDomainType(); SkillSlot* pSkillSlot = pSlayer->getSkill(SKILL_SPIRIT_GUARD); if (pSkillSlot != NULL ) { increaseDomainExp(pSlayer, DomainType, pSkillInfo->getPoint(), gcAttackerMI, maxEnemyLevel, EnemyNum); increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, gcAttackerMI); } } setNextTime(m_Delay); __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 오브젝트 핸들러 ////////////////////////////////////////////////////////////////////////////// void TurnUndead::execute(Slayer * pSlayer, SkillSlot * pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << "begin " << endl; // Slayer Object Assertion Assert(pSlayer != NULL); Assert(pSkillSlot != NULL); try { Zone* pZone = pSlayer->getZone(); Assert(pZone != NULL); Player* pPlayer = pSlayer->getPlayer(); Assert(pPlayer != NULL); GCSkillToSelfOK1 _GCSkillToSelfOK1; GCSkillToSelfOK2 _GCSkillToSelfOK2; SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); ZoneCoord_t X = pSlayer->getX(); ZoneCoord_t Y = pSlayer->getY(); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, X, Y, pSkillInfo->getRange()) && checkZoneLevelToUseSkill(pSlayer); bool bHitRoll = HitRoll::isSuccessMagic(pSlayer, pSkillInfo, pSkillSlot); VSRect rect(0, 0, pZone->getWidth()-1, pZone->getHeight()-1); if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll) { decreaseMana(pSlayer, RequiredMP, _GCSkillToSelfOK1); // calculate damage and duration time SkillInput input(pSlayer, pSkillSlot); SkillOutput output; computeOutput(input, output); bool bHit = false; Level_t maxEnemyLevel = 0; uint EnemyNum = 0; int oX, oY; for(oX = -2; oX <= 2; oX++) for(oY = -2; oY <= 2; oY++) { int tileX = X+oX; int tileY = Y+oY; if (oX == 0 && oY == 0 ) continue; if (!rect.ptInRect(tileX, tileY)) continue; Tile& tile = pZone->getTile(tileX, tileY); // 타일에 있는 크리처들을 리스트로 만든다. list<Creature*> targetList; if(tile.hasCreature(Creature::MOVE_MODE_WALKING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); targetList.push_back(pCreature); } if(tile.hasCreature(Creature::MOVE_MODE_FLYING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_FLYING); targetList.push_back(pCreature); } if(tile.hasCreature(Creature::MOVE_MODE_BURROWING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_BURROWING); targetList.push_back(pCreature); } list<Creature*>::iterator itr = targetList.begin(); for (; itr != targetList.end(); itr++ ) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); if(checkZoneLevelToHitTarget(pTargetCreature ) && !pTargetCreature->isSlayer() && !pTargetCreature->isFlag(Effect::EFFECT_CLASS_COMA) && canAttack(pSlayer, pTargetCreature ) ) { if(pTargetCreature->isVampire() || pTargetCreature->isOusters()) { Player* pTargetPlayer = pTargetCreature->getPlayer(); bHit = true; // 데미지를 적용시킨다. GCModifyInformation gcMI; ::setDamage(pTargetCreature, output.Damage, pSlayer, pSkillSlot->getSkillType(), &gcMI); // HP 가 변했다고 당사자에게 보낸다. pTargetPlayer->sendPacket(&gcMI); GCSkillToObjectOK2 gcSkillToObjectOK2; gcSkillToObjectOK2.setObjectID(1); // 의미 없다. gcSkillToObjectOK2.setSkillType(SKILL_ATTACK_MELEE); gcSkillToObjectOK2.setDuration(0); } else if(pTargetCreature->isMonster()) { Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); bHit = true; ::setDamage(pMonster, output.Damage, pSlayer, pSkillSlot->getSkillType()); pMonster->addEnemy(pSlayer); } else { continue; } if (maxEnemyLevel < pTargetCreature->getLevel() ) maxEnemyLevel = pTargetCreature->getLevel(); EnemyNum++; GCSkillToObjectOK4 gcSkillToObjectOK4; gcSkillToObjectOK4.setTargetObjectID(pTargetCreature->getObjectID()); gcSkillToObjectOK4.setSkillType(SKILL_ATTACK_MELEE); gcSkillToObjectOK4.setDuration(0); pZone->broadcastPacket(pTargetCreature->getX(), pTargetCreature->getY(), &gcSkillToObjectOK4); // 성향을 올린다. increaseAlignment(pSlayer, pTargetCreature, _GCSkillToSelfOK1); } } } if(bHit) { //cout << "Skill Succesfully Attacked(" << output.Damage << ")" << endl; shareAttrExp(pSlayer, output.Damage, 1, 1, 8, _GCSkillToSelfOK1); increaseDomainExp(pSlayer, DomainType, pSkillInfo->getPoint(), _GCSkillToSelfOK1, maxEnemyLevel, EnemyNum); increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCSkillToSelfOK1); } // 패킷을 만들어 보낸다. _GCSkillToSelfOK1.setSkillType(SkillType); _GCSkillToSelfOK1.setCEffectID(CEffectID); _GCSkillToSelfOK1.setDuration(0); _GCSkillToSelfOK2.setObjectID(pSlayer->getObjectID()); _GCSkillToSelfOK2.setSkillType(SkillType); _GCSkillToSelfOK2.setDuration(0); // 기술을 사용한 사람에게 packet 전달 pPlayer->sendPacket(&_GCSkillToSelfOK1); pZone->broadcastPacket(X, Y, &_GCSkillToSelfOK2, pSlayer); // 기술 delay setting pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pSlayer, getSkillType(), NULL); } } catch(Throwable& t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 타일 핸들러 ////////////////////////////////////////////////////////////////////////////// void BloodyBreaker::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; SkillType_t SkillType = getSkillType(); // Knowledge of Blood 가 있다면 hit bonus 10 int HitBonus = 0; if (pVampire->hasRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD ) ) { RankBonus* pRankBonus = pVampire->getRankBonus(RankBonus::RANK_BONUS_KNOWLEDGE_OF_BLOOD); Assert(pRankBonus != NULL); HitBonus = pRankBonus->getPoint(); } try { SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); Dir_t Dir = getDirectionToPosition(pVampire->getX(), pVampire->getY(), X, Y); // 강제로 knockback시킬 확률 // bool bForceKnockback = rand()%100 < output.ToHit; Player* pPlayer = pVampire->getPlayer(); Zone* pZone = pVampire->getZone(); Assert(pPlayer != NULL); Assert(pZone != NULL); VSRect rect(1, 1, pZone->getWidth()-2, pZone->getHeight()-2); if (!rect.ptInRect(X, Y )) { executeSkillFailException(pVampire, SkillType); return; } GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; // GCSkillToTileOK3 _GCSkillToTileOK3; // GCSkillToTileOK4 _GCSkillToTileOK4; GCSkillToTileOK5 _GCSkillToTileOK5; // GCSkillToTileOK6 _GCSkillToTileOK6; SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pVampireSkillSlot); bool bRangeCheck = verifyDistance(pVampire, X, Y, pSkillInfo->getRange()); if (bManaCheck && bTimeCheck && bRangeCheck ) { // 마나를 떨어뜨린다. decreaseMana(pVampire, RequiredMP, _GCSkillToTileOK1); // 좌표와 방향을 구한다. ZoneCoord_t myX = pVampire->getX(); ZoneCoord_t myY = pVampire->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); list<Creature*> cList; // knockback 때문에 recursive 하게 데미지를 먹는 경우가 있다. // 그래서 제일 먼쪽에 있는 마스크부터 체크한다. for (int i = 21; i >= 0; i-- ) { int tileX = myX + m_pBloodyBreakerMask[Dir][i].x; int tileY = myY + m_pBloodyBreakerMask[Dir][i].y; // 현재 타일이 존 내부이고, 안전지대가 아니라면 맞을 가능성이 있다. if (rect.ptInRect(tileX, tileY)) { // 타일을 받아온다. Tile& tile = pZone->getTile(tileX, tileY); list<Creature*> targetList; if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); targetList.push_back(pCreature); } if (tile.hasCreature(Creature::MOVE_MODE_FLYING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_FLYING); targetList.push_back(pCreature); } if (tile.hasCreature(Creature::MOVE_MODE_BURROWING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_BURROWING); targetList.push_back(pCreature); } list<Creature*>::iterator itr = targetList.begin(); for(; itr != targetList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); if (!canAttack(pVampire, pTargetCreature ) || pTargetCreature->isFlag(Effect::EFFECT_CLASS_COMA) ) { continue; } if (pTargetCreature != pVampire) { bool bPK = verifyPK(pVampire, pTargetCreature); bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isMonster() || pTargetCreature->isOusters(); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); bool bHitRoll = false;//HitRoll::isSuccessMagic(pVampire, pSkillInfo, pVampireSkillSlot, HitBonus); int EnemyLevel = 0; if (pTargetCreature->isSlayer() ) { Slayer* pSlayer = dynamic_cast<Slayer*>(pTargetCreature); EnemyLevel = pSlayer->getHighestSkillDomainLevel(); } else if (pTargetCreature->isOusters() ) { Ousters* pOusters = dynamic_cast<Ousters*>(pTargetCreature); EnemyLevel = pOusters->getLevel(); } else if (pTargetCreature->isMonster() ) { Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); EnemyLevel = pMonster->getLevel(); } // min : 20, max : 100 int hitRatio = max(20, 50 + pVampire->getLevel() - EnemyLevel + HitBonus); bHitRoll = (rand()%100) < hitRatio; if (bPK && bRaceCheck && bZoneLevelCheck && bHitRoll) { Damage_t Damage = 0; bool bForceKnockback = rand() & 1; Damage += computeMagicDamage(pTargetCreature, output.Damage, SkillType, true, pVampire); ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); // 일단 맞는 놈이 받을 패킷은 널 상태로 한 채로, 데미지를 준다. setDamage(pTargetCreature, Damage, pVampire, SkillType, NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pVampire, NULL, &_GCSkillToTileOK1); increaseAlignment(pVampire, pTargetCreature, _GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bForceKnockback) { knockbackCreature(pZone, pTargetCreature, pVampire->getX(), pVampire->getY()); } if (pTargetCreature->isDead()) { int exp = computeCreatureExp(pTargetCreature, KILL_EXP); shareVampExp(pVampire, exp, _GCSkillToTileOK1); } } } } } } // 공격자의 아이템 내구성을 떨어뜨린다. decreaseDurability(pVampire, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(0); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK5.setObjectID(pVampire->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _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의 변경사항을 패킷에다 기록한다. HP_t targetHP = 0; if (pTargetCreature->isSlayer()) { targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(ATTR_CURRENT); } else if (pTargetCreature->isVampire()) { targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(ATTR_CURRENT); } else if (pTargetCreature->isOusters()) { targetHP = (dynamic_cast<Ousters*>(pTargetCreature))->getHP(ATTR_CURRENT); } _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pVampire); } } cList.push_back(pVampire); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK5 , cList); // set Next Run Time pVampireSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, SkillType, NULL); } } catch (Throwable & t) { executeSkillFailException(pVampire, SkillType); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; __END_CATCH }
bool EffectEnergyDrop::affectCreature(Creature* pTargetCreature, bool bAffectByMove) throw(Error) { __BEGIN_TRY //cout << "EffectEnergyDrop " << "affectCreature Begin " << endl; Assert(pTargetCreature != NULL); // 상대에게 이미 poison 이펙트가 걸려져 있는 경우에는 걸리지 않는다. if (pTargetCreature->isFlag(Effect::EFFECT_CLASS_ENERGY_DROP_TO_CREATURE)) { //cout << "EffectEnergyDrop " << "affectCreature End(Already Effected) " << endl; return false; } // 안전지대 체크 // 2003.1.10 by bezz, Sequoia if (!checkZoneLevelToHitTarget(pTargetCreature) ) { return false; } Zone* pZone = pTargetCreature->getZone(); // 상대방에게 미칠 독 데미지를 계산한다. int DropDamage = computeMagicDamage(pTargetCreature, m_Damage, SKILL_ENERGY_DROP); //cout << "EffectEnergyDrop(Damage:" << DropDamage << ") Affected" << endl; if (DropDamage > 0) { // 포이즌 이펙트를 생성해서, 타겟 크리쳐에 붙이고, 플래그를 켜준다. // 현제는 EnergyDrop Effect를 지속적으로 운영하지 않는다. 다른 Effeect기술의 // 경우 시전자의 레벨이 올라갈수록 Duration이 증가하게 되고 이에 대한 Effect를 붙이게 // 되지만, --Drop, --Storm류의 기술은 일단 성공이 되면 해당 구역의 모든 사람이 // 기술을 당하게 되고, 각각 시간으로 데미지를 주는 것이 아니라 일정량의 데미지를 // 3등분해서 주게 된다. 몇번의 데미지를 주는 함수는 아직 개발되지 않은 단계라서 // Deadline과 tick을 사용해서 임의로 구현을 하였다. // 1.6초 동안에 0.5초 간격으로 데미지를 주게 되면, 3번의 데미지를 줄 수 있게 된다. // 여기서는 하드코딩이 되어 있는데, 이는 다른 방법으로 교체되어야 할 거 같다. // EffectEnergyDrop Class에 member variable로 // m_Tick // m_Count // 를 두어서 이를 바탕으로 deadline을 계산해서 적용한다면 더욱 더 편할 것이다. EffectEnergyDropToCreature* pEffectEnergyDropToCreature = new EffectEnergyDropToCreature(pTargetCreature); // 우선권 시스템을 위하여 이름과 파티 아이디를 넣는다. // pEffectEnergyDropToCreature->setCasterName(m_CasterName); // pEffectEnergyDropToCreature->setPartyID(m_PartyID); pEffectEnergyDropToCreature->setUserObjectID(m_UserObjectID); pEffectEnergyDropToCreature->setLevel(m_Level); pEffectEnergyDropToCreature->setPoint(DropDamage/3); pEffectEnergyDropToCreature->setDeadline(16); // 이부분 바꿔야 한다. pEffectEnergyDropToCreature->setTick(5); // 이부분도 바꿔야 한다. pEffectEnergyDropToCreature->affect(pTargetCreature); pTargetCreature->addEffect(pEffectEnergyDropToCreature); pTargetCreature->setFlag(Effect::EFFECT_CLASS_ENERGY_DROP_TO_CREATURE); // 이펙트가 붙었다고 주변에 알려준다. GCAddEffect gcAddEffect; gcAddEffect.setObjectID(pTargetCreature->getObjectID()); gcAddEffect.setEffectID(Effect::EFFECT_CLASS_ENERGY_DROP_TO_CREATURE); gcAddEffect.setDuration(m_Duration); pZone->broadcastPacket(pTargetCreature->getX(), pTargetCreature->getY(), &gcAddEffect); } //cout << "EffectEnergyDrop " << "affectCreature End " << endl; return true; __END_CATCH }
////////////////////////////////////////////////////////////////////// // // ThunderFlash::execute() // ////////////////////////////////////////////////////////////////////// void ThunderFlash::execute(Slayer* pSlayer, ZoneCoord_t X, ZoneCoord_t Y, SkillSlot* pSkillSlot, CEffectID_t CEffectID) throw(Error) { __BEGIN_TRY //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); // 무장하고 있는 무기가 널이거나, SWORD가 아니라면 사용할 수 없다. Item* pItem = pSlayer->getWearItem(Slayer::WEAR_RIGHTHAND); if (pItem == NULL || pItem->getItemClass() != Item::ITEM_CLASS_SWORD) { executeSkillFailException(pSlayer, getSkillType()); //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end" << endl; return; } bool bIncreaseExp = pSlayer->isRealWearingEx(Slayer::WEAR_RIGHTHAND); GCSkillToTileOK1 _GCSkillToTileOK1; GCSkillToTileOK2 _GCSkillToTileOK2; GCSkillToTileOK5 _GCSkillToTileOK5; SkillType_t SkillType = pSkillSlot->getSkillType(); SkillInfo* pSkillInfo = g_pSkillInfoManager->getSkillInfo(SkillType); SkillDomainType_t DomainType = pSkillInfo->getDomainType(); SkillLevel_t SkillLevel = pSkillSlot->getExpLevel(); int RequiredMP = (int)pSkillInfo->getConsumeMP(); bool bManaCheck = hasEnoughMana(pSlayer, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pSlayer, X, Y, pSkillInfo->getRange()); // 마나가 있어야 하고, 시간과 거리 체크에 성공하고, if (bManaCheck && bTimeCheck && bRangeCheck) { // MP를 떨어뜨린다. decreaseMana(pSlayer, RequiredMP, _GCSkillToTileOK1); // 좌표와 방향을 구한다. ZoneCoord_t myX = pSlayer->getX(); ZoneCoord_t myY = pSlayer->getY(); Dir_t dir = calcDirection(myX, myY, X, Y); SkillInput input(pSlayer, pSkillSlot); SkillOutput output; computeOutput(input, output); Damage_t SkillDamage = output.Damage; Damage_t Damage = 0; bool bHit = false; Level_t maxEnemyLevel = 0; uint EnemyNum = 0; VSRect rect(1, 1, pZone->getWidth()-2, pZone->getHeight()-2); list<Creature*> cList; for (int count=0; count<4; count++) { int tileX = X + m_pThunderFlashMask[count].x; int tileY = Y + m_pThunderFlashMask[count].y; // 현재 타일이 존 내부이고, 안전지대가 아니라면, 맞을 확률이 있다. if (rect.ptInRect(tileX, tileY)) { // 타일을 받아온다. Tile& tile = pZone->getTile(tileX, tileY); list<Creature*> targetList; if (tile.hasCreature(Creature::MOVE_MODE_WALKING)) { Creature* pCreature = tile.getCreature(Creature::MOVE_MODE_WALKING); targetList.push_back(pCreature); } list<Creature*>::iterator itr = targetList.begin(); for(; itr != targetList.end(); itr++) { Creature* pTargetCreature = (*itr); Assert(pTargetCreature != NULL); bool bMoveModeCheck = (pTargetCreature->getMoveMode() == Creature::MOVE_MODE_WALKING) ? true : false; bool bHitRoll = HitRoll::isSuccess(pSlayer, pTargetCreature, SkillLevel/2); //bool bCanHit = canHit(pSlayer, pTargetCreature, SkillType); bool bCanHit = true; bool bPK = verifyPK(pSlayer, pTargetCreature); bool bRaceCheck = pTargetCreature->isSlayer() || pTargetCreature->isNPC(); bool bZoneLevelCheck = checkZoneLevelToHitTarget(pTargetCreature); if (bMoveModeCheck && bHitRoll && bCanHit && bPK && !bRaceCheck && bZoneLevelCheck) { CheckCrossCounter(pSlayer, pTargetCreature, Damage, pSkillInfo->getRange()); bool bCriticalHit = false; Damage = computeDamage(pSlayer, pTargetCreature, SkillLevel/5, bCriticalHit) + SkillDamage; ObjectID_t targetObjectID = pTargetCreature->getObjectID(); cList.push_back(pTargetCreature); _GCSkillToTileOK1.addCListElement(targetObjectID); _GCSkillToTileOK2.addCListElement(targetObjectID); _GCSkillToTileOK5.addCListElement(targetObjectID); // 일단 맞는 놈이 받을 패킷은 널 상태로 한 채로, 데미지를 준다. setDamage(pTargetCreature, Damage, pSlayer, SkillType, NULL, &_GCSkillToTileOK1); computeAlignmentChange(pTargetCreature, Damage, pSlayer, NULL, &_GCSkillToTileOK1); increaseAlignment(pSlayer, pTargetCreature, _GCSkillToTileOK1); // 크리티컬 히트라면 상대방을 뒤로 물러나게 한다. if (bCriticalHit) { knockbackCreature(pZone, pTargetCreature, pSlayer->getX(), pSlayer->getY()); } // 슬레이어가 아닐 경우에만 맞은 것으로 간주한다. if (!pTargetCreature->isSlayer()) { bHit = true; if (maxEnemyLevel < pTargetCreature->getLevel() ) maxEnemyLevel = pTargetCreature->getLevel(); EnemyNum++; } } } //for (; itr != objectList.end(); itr++) } // if (rect.ptInRect(tileX, tileY) && ... } // for (int count=0; count<3; count++) if (bHit) { if (bIncreaseExp) { shareAttrExp(pSlayer, Damage , 8, 1, 1, _GCSkillToTileOK1); increaseDomainExp(pSlayer, DomainType, pSkillInfo->getPoint(), _GCSkillToTileOK1, maxEnemyLevel, EnemyNum); increaseSkillExp(pSlayer, DomainType, pSkillSlot, pSkillInfo, _GCSkillToTileOK1); } } // 공격자 아이템 내구성 떨어트림. decreaseDurability(pSlayer, NULL, pSkillInfo, &_GCSkillToTileOK1, NULL); _GCSkillToTileOK1.setSkillType(SkillType); _GCSkillToTileOK1.setCEffectID(CEffectID); _GCSkillToTileOK1.setX(X); _GCSkillToTileOK1.setY(Y); _GCSkillToTileOK1.setRange(dir); _GCSkillToTileOK1.setDuration(0); _GCSkillToTileOK2.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK2.setSkillType(SkillType); _GCSkillToTileOK2.setX(X); _GCSkillToTileOK2.setY(Y); _GCSkillToTileOK2.setRange(dir); _GCSkillToTileOK2.setDuration(0); _GCSkillToTileOK5.setObjectID(pSlayer->getObjectID()); _GCSkillToTileOK5.setSkillType(SkillType); _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의 변경사항을 패킷에다 기록한다. HP_t targetHP = 0; if (pTargetCreature->isSlayer()) targetHP = (dynamic_cast<Slayer*>(pTargetCreature))->getHP(ATTR_CURRENT); else if (pTargetCreature->isVampire()) targetHP = (dynamic_cast<Vampire*>(pTargetCreature))->getHP(ATTR_CURRENT); _GCSkillToTileOK2.addShortData(MODIFY_CURRENT_HP, targetHP); // 아이템의 내구력을 떨어뜨린다. decreaseDurability(NULL, pTargetCreature, pSkillInfo, NULL, &_GCSkillToTileOK2); // 패킷을 보내준다. pTargetCreature->getPlayer()->sendPacket(&_GCSkillToTileOK2); } else if (pTargetCreature->isMonster()) { // 당근 적으로 인식한다. Monster* pMonster = dynamic_cast<Monster*>(pTargetCreature); pMonster->addEnemy(pSlayer); } } cList.push_back(pSlayer); pZone->broadcastPacket(myX, myY, &_GCSkillToTileOK5 , cList); // set Next Run Time pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pSlayer, getSkillType(), NULL); } } catch (Throwable & t) { executeSkillFailException(pSlayer, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end" << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 슬레이어 타일 핸들러 - 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 }