void EffectEvade::unaffect(Creature* pCreature) throw(Error) { __BEGIN_TRY //cout << "EffectEvade" << "unaffect BEGIN" << endl; Assert(pCreature != NULL); Assert(pCreature->isOusters()); // 플래그를 끈다. pCreature->removeFlag(Effect::EFFECT_CLASS_EVADE); Zone* pZone = pCreature->getZone(); Assert(pZone != NULL); Ousters* pTargetOusters = dynamic_cast<Ousters*>(pCreature); Assert(pTargetOusters != NULL); OUSTERS_RECORD prev; pTargetOusters->getOustersRecord(prev); pTargetOusters->initAllStat(); pTargetOusters->sendRealWearingInfo(); pTargetOusters->sendModifyInfo(prev); // 이펙트를 삭제하라고 알려준다. GCRemoveEffect gcRemoveEffect; gcRemoveEffect.setObjectID(pCreature->getObjectID()); gcRemoveEffect.addEffectList(Effect::EFFECT_CLASS_EVADE); pZone->broadcastPacket(pCreature->getX(), pCreature->getY(), &gcRemoveEffect); //cout << "EffectEvade" << "unaffect END" << endl; __END_CATCH }
////////////////////////////////////////////////////////////////////////////// // 뱀파이어 오브젝트 핸들러 ////////////////////////////////////////////////////////////////////////////// void Death::execute(Vampire* pVampire, ObjectID_t TargetObjectID, VampireSkillSlot* pSkillSlot, CEffectID_t CEffectID) 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(); Assert(pPlayer != NULL); Assert(pZone != NULL); Creature* pTargetCreature = pZone->getCreature(TargetObjectID); //Assert(pTargetCreature != NULL); // NPC는 공격할 수 없다. // 저주 면역. by sigi. 2002.9.13 // NoSuch제거. by sigi. 2002.5.2 if (pTargetCreature==NULL || pTargetCreature->isFlag(Effect::EFFECT_CLASS_IMMUNE_TO_CURSE) || !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; SkillType_t SkillType = pSkillSlot->getSkillType(); 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(); } int RequiredMP = decreaseConsumeMP(pVampire, pSkillInfo); bool bManaCheck = hasEnoughMana(pVampire, RequiredMP); bool bTimeCheck = verifyRunTime(pSkillSlot); bool bRangeCheck = verifyDistance(pVampire, pTargetCreature, pSkillInfo->getRange()); bool bHitRoll = HitRoll::isSuccessVampireCurse(pSkillInfo->getLevel(), pTargetCreature->getResist(MAGIC_DOMAIN_CURSE)); bool bHitRoll2 = HitRoll::isSuccessMagic(pVampire, pSkillInfo, pSkillSlot, HitBonus); bool bCanHit = canHit(pVampire, pTargetCreature, SkillType); bool bEffected = pTargetCreature->isFlag(Effect::EFFECT_CLASS_DEATH); bool bPK = verifyPK(pVampire, pTargetCreature); ZoneCoord_t targetX = pTargetCreature->getX(); ZoneCoord_t targetY = pTargetCreature->getY(); ZoneCoord_t myX = pVampire->getX(); ZoneCoord_t myY = pVampire->getY(); if (bManaCheck && bTimeCheck && bRangeCheck && bHitRoll && bHitRoll2 && bCanHit && !bEffected && bPK) { decreaseMana(pVampire, RequiredMP, _GCSkillToObjectOK1); bool bCanSeeCaster = canSee(pTargetCreature, pVampire); SkillInput input(pVampire); SkillOutput output; computeOutput(input, output); // pTargetCreature가 저주마법을 반사하는 경우 if (CheckReflection(pVampire, pTargetCreature, getSkillType())) { pTargetCreature = (Creature*)pVampire; TargetObjectID = pVampire->getObjectID(); } Resist_t resist = pTargetCreature->getResist(MAGIC_DOMAIN_CURSE); if ((resist*10/3) > output.Duration ) output.Duration=0; else output.Duration -= resist*10/3; if (output.Duration < 20 ) output.Duration = 20; // 이펙트 오브젝트를 생성해 붙인다. EffectDeath* pEffect = new EffectDeath(pTargetCreature); pEffect->setDeadline(output.Duration); pEffect->setLevel(pSkillInfo->getLevel()/2); pEffect->setResistPenalty(output.Damage); pTargetCreature->addEffect(pEffect); pTargetCreature->setFlag(Effect::EFFECT_CLASS_DEATH); // 능력치를 계산해서 보내준다. if (pTargetCreature->isSlayer()) { Slayer* pTargetSlayer = dynamic_cast<Slayer*>(pTargetCreature); if (bCanSeeCaster) { SLAYER_RECORD prev; pTargetSlayer->getSlayerRecord(prev); pTargetSlayer->initAllStat(); pTargetSlayer->addModifyInfo(prev, _GCSkillToObjectOK2); } else { SLAYER_RECORD prev; pTargetSlayer->getSlayerRecord(prev); pTargetSlayer->initAllStat(); pTargetSlayer->addModifyInfo(prev, _GCSkillToObjectOK6); } } else if (pTargetCreature->isVampire()) { Vampire* pTargetVampire = dynamic_cast<Vampire*>(pTargetCreature); VAMPIRE_RECORD prev; pTargetVampire->getVampireRecord(prev); pTargetVampire->initAllStat(); if (bCanSeeCaster) { pTargetVampire->addModifyInfo(prev, _GCSkillToObjectOK2); } else { pTargetVampire->addModifyInfo(prev, _GCSkillToObjectOK6); } } else if (pTargetCreature->isOusters()) { Ousters* pTargetOusters = dynamic_cast<Ousters*>(pTargetCreature); OUSTERS_RECORD prev; pTargetOusters->getOustersRecord(prev); pTargetOusters->initAllStat(); if (bCanSeeCaster) { pTargetOusters->addModifyInfo(prev, _GCSkillToObjectOK2); } else { pTargetOusters->addModifyInfo(prev, _GCSkillToObjectOK6); } } else if (pTargetCreature->isMonster()) { Monster* pTargetMonster = dynamic_cast<Monster*>(pTargetCreature); pTargetMonster->initAllStat(); } else Assert(false); _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); _GCSkillToObjectOK4.setDuration(output.Duration); _GCSkillToObjectOK5.setObjectID(pVampire->getObjectID()); _GCSkillToObjectOK5.setSkillType(SkillType); _GCSkillToObjectOK5.setTargetObjectID (TargetObjectID); _GCSkillToObjectOK5.setDuration(output.Duration); _GCSkillToObjectOK6.setXY(myX, myY); _GCSkillToObjectOK6.setSkillType(SkillType); _GCSkillToObjectOK6.setDuration(output.Duration); if (bCanSeeCaster) // 10은 땜빵 수치다. { computeAlignmentChange(pTargetCreature, 10, pVampire, &_GCSkillToObjectOK2, &_GCSkillToObjectOK1); } else // 10은 땜빵 수치다. { computeAlignmentChange(pTargetCreature, 10, pVampire, &_GCSkillToObjectOK6, &_GCSkillToObjectOK1); } list<Creature *> cList; cList.push_back(pTargetCreature); cList.push_back(pVampire); cList = pZone->broadcastSkillPacket(myX, myY, targetX, targetY, &_GCSkillToObjectOK5, cList); pZone->broadcastPacket(myX, myY, &_GCSkillToObjectOK3, cList); pZone->broadcastPacket(targetX, targetY, &_GCSkillToObjectOK4, cList); // Send Packet pPlayer->sendPacket(&_GCSkillToObjectOK1); if (pTargetCreature->isPC()) { Player* pTargetPlayer = pTargetCreature->getPlayer(); if (pTargetPlayer == NULL) { //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; return; } if (bCanSeeCaster) pTargetPlayer->sendPacket(&_GCSkillToObjectOK2); else pTargetPlayer->sendPacket(&_GCSkillToObjectOK6); } else if (pTargetCreature->isMonster()) { Monster* pTargetMonster = dynamic_cast<Monster*>(pTargetCreature); pTargetMonster->addEnemy(pVampire); } GCAddEffect gcAddEffect; gcAddEffect.setObjectID(TargetObjectID); gcAddEffect.setEffectID(Effect::EFFECT_CLASS_DEATH); gcAddEffect.setDuration(output.Duration); pZone->broadcastPacket(targetX, targetY, &gcAddEffect); //cout << pTargetCreature->getName() << "에게 Death를 " << output.Duration << " duration 동안 건다." << endl; pSkillSlot->setRunTime(output.Delay); } else { executeSkillFailNormal(pVampire, getSkillType(), pTargetCreature); } } catch (Throwable & t) { executeSkillFailException(pVampire, getSkillType()); } //cout << "TID[" << Thread::self() << "]" << getSkillHandlerName() << " end " << endl; __END_CATCH }
void CGUseBonusPointHandler::execute (CGUseBonusPoint* pPacket , Player* pPlayer) throw(Error) { __BEGIN_TRY __BEGIN_DEBUG_EX __BEGIN_DEBUG #ifdef __GAME_SERVER__ Assert(pPacket != NULL); Assert(pPlayer != NULL); // 정상적인 상태가 아니라면 리턴 GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); if (pGamePlayer->getPlayerStatus() != GPS_NORMAL) return; Creature* pCreature = pGamePlayer->getCreature(); BYTE which = pPacket->getWhich(); Attr_t cur = 0; if (pCreature->isSlayer() ) { Slayer* pSlayer = dynamic_cast<Slayer*>(pCreature); if (pSlayer->getBonus() <= 0 ) { GCUseBonusPointFail failPkt; pPlayer->sendPacket(&failPkt); return; } bool success = false; if (which == INC_INT ) { success = pSlayer->putAdvancedBonusToINT(); } else if (which == INC_STR) { success = pSlayer->putAdvancedBonusToSTR(); } else if (which == INC_DEX) { success = pSlayer->putAdvancedBonusToDEX(); } if (success ) { GCUseBonusPointOK okpkt; pGamePlayer->sendPacket (&okpkt); pSlayer->saveExps(); pSlayer->initAllStatAndSend(); } else { GCUseBonusPointFail failPkt; pPlayer->sendPacket(&failPkt); return; } } else if (pCreature->isVampire() ) { // 보너스 포인트가 없다면 리턴 Vampire* pVampire = dynamic_cast<Vampire*>(pCreature); if (pVampire->getBonus() <= 0) { GCUseBonusPointFail failPkt; pPlayer->sendPacket(&failPkt); return; } VAMPIRE_RECORD oldRecord; // 능력치를 올리기 전에 기존의 능력치를 저장한다. pVampire->getVampireRecord(oldRecord); if (which == INC_INT) { cur = pVampire->getINT(ATTR_BASIC) + 1; pVampire->setINT(cur, ATTR_BASIC); StringStream sst; sst << "INTE = " << (int)cur; pVampire->tinysave(sst.toString()); /* // INT가 증가하면 새로운 기술을 배울 수 있는 가능성이 있다. SkillType_t lastSkill = pVampire->findLastSkill(); // lastSkill의 다음 level의 기술을 찾는다. // 못찾았다면 더이상 배울것이 없다는 것. for(int i = SKILL_BLOOD_DRAIN + 1 ; i < SKILL_MAX; i++) { SkillParentInfo* pParentInfo = g_pSkillParentInfoManager->getSkillParentInfo(i); if (pParentInfo->hasParent(lastSkill))// 찾았다! { SkillInfo* pNewSkillInfo = g_pSkillInfoManager->getSkillInfo(i); if (pNewSkillInfo->getEXP() <= cur && pVampire->hasSkill(i) == NULL) { //cout << "(" << pVampire->getName() << ") can learn new skill >> "; // 새로운 기술을 배울 수 있다. GCLearnSkillReady gcLSR; gcLSR.setSkillDomainType(SKILL_DOMAIN_VAMPIRE); pVampire->getPlayer()->sendPacket(&gcLSR); break; } } } */ //log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "INT"); } else if (which == INC_STR) { cur = pVampire->getSTR(ATTR_BASIC) + 1; pVampire->setSTR(cur, ATTR_BASIC); StringStream sst; sst << "STR = " << (int)cur; pVampire->tinysave(sst.toString()); //log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "STR"); } else if (which == INC_DEX) { cur = pVampire->getDEX(ATTR_BASIC) + 1; pVampire->setDEX(cur, ATTR_BASIC); StringStream sst; sst << "DEX = " << (int)cur; pVampire->tinysave(sst.toString()); //log(LOG_USE_BONUS_POINT, pVampire->getName(), "", "DEX"); } // 바뀐 보너스 포인트를 저장한다. Bonus_t OldBonus = pVampire->getBonus(); pVampire->setBonus(OldBonus - 1); StringStream sst; sst << "Bonus = " << (int)(OldBonus - 1); pVampire->tinysave(sst.toString()); // 능력치가 변화되었으니, stat을 새로 고친다. pVampire->initAllStat(); // 클라이언트의 계산 순서 때문에 생기는 버그로 인하여, // 먼저 인증 패킷을 날려준 후에, 바뀐 능력치에 대한 정보를 보낸다. // 나중에 CGUseBonusPointOK에다 바로 바뀐 능력치에 대한 정보를 // 실어보내도록 해야 한다. // OK 패킷을 보내준다. GCUseBonusPointOK okpkt; pGamePlayer->sendPacket (&okpkt); // 바뀐 능력치에 관한 정보를 보내준다. pVampire->sendModifyInfo(oldRecord); pVampire->sendRealWearingInfo(); } else if (pCreature->isOusters() ) { // 보너스 포인트가 없다면 리턴 Ousters* pOusters = dynamic_cast<Ousters*>(pCreature); if (pOusters->getBonus() <= 0) { GCUseBonusPointFail failPkt; pPlayer->sendPacket(&failPkt); return; } OUSTERS_RECORD oldRecord; // 능력치를 올리기 전에 기존의 능력치를 저장한다. pOusters->getOustersRecord(oldRecord); if (which == INC_INT) { cur = pOusters->getINT(ATTR_BASIC) + 1; pOusters->setINT(cur, ATTR_BASIC); StringStream sst; sst << "INTE = " << (int)cur; pOusters->tinysave(sst.toString()); } else if (which == INC_STR) { cur = pOusters->getSTR(ATTR_BASIC) + 1; pOusters->setSTR(cur, ATTR_BASIC); StringStream sst; sst << "STR = " << (int)cur; pOusters->tinysave(sst.toString()); } else if (which == INC_DEX) { cur = pOusters->getDEX(ATTR_BASIC) + 1; pOusters->setDEX(cur, ATTR_BASIC); StringStream sst; sst << "DEX = " << (int)cur; pOusters->tinysave(sst.toString()); } // 바뀐 보너스 포인트를 저장한다. Bonus_t OldBonus = pOusters->getBonus(); pOusters->setBonus(OldBonus - 1); StringStream sst; sst << "Bonus = " << (int)(OldBonus - 1); pOusters->tinysave(sst.toString()); // 능력치가 변화되었으니, stat을 새로 고친다. pOusters->initAllStat(); // OK 패킷을 보내준다. GCUseBonusPointOK okpkt; pGamePlayer->sendPacket(&okpkt); // 바뀐 능력치에 관한 정보를 보내준다. pOusters->sendModifyInfo(oldRecord); pOusters->sendRealWearingInfo(); } else { GCUseBonusPointFail failPkt; pPlayer->sendPacket(&failPkt); return; } #endif // __GAME_SERVER__ __END_DEBUG __END_DEBUG_EX __END_CATCH }
void EffectDoom::unaffect(Creature* pCreature) throw(Error) { __BEGIN_TRY __BEGIN_DEBUG //cout << "EffectDoom" << "unaffect BEGIN" << endl; Assert(pCreature != NULL); // 능력치를 정상적으로 되돌리기 위해서는 플래그를 끄고, // initAllStat을 불러야 한다. pCreature->removeFlag(Effect::EFFECT_CLASS_DOOM); if (pCreature->isVampire()) { Vampire* pTargetVampire = dynamic_cast<Vampire*>(pCreature); VAMPIRE_RECORD prev; pTargetVampire->getVampireRecord(prev); pTargetVampire->initAllStat(); pTargetVampire->sendRealWearingInfo(); pTargetVampire->sendModifyInfo(prev); } else if (pCreature->isMonster()) { Monster* pMonster = dynamic_cast<Monster*>(pCreature); pMonster->initAllStat(); } else if (pCreature->isSlayer()) { Slayer* pTargetSlayer = dynamic_cast<Slayer*>(pCreature); SLAYER_RECORD prev; pTargetSlayer->getSlayerRecord(prev); pTargetSlayer->initAllStat(); pTargetSlayer->sendRealWearingInfo(); pTargetSlayer->sendModifyInfo(prev); } else if (pCreature->isOusters()) { Ousters* pTargetOusters = dynamic_cast<Ousters*>(pCreature); OUSTERS_RECORD prev; pTargetOusters->getOustersRecord(prev); pTargetOusters->initAllStat(); pTargetOusters->sendRealWearingInfo(); pTargetOusters->sendModifyInfo(prev); } else Assert(false); Zone* pZone = pCreature->getZone(); Assert(pZone != NULL); GCRemoveEffect gcRemoveEffect; gcRemoveEffect.setObjectID(pCreature->getObjectID()); gcRemoveEffect.addEffectList(Effect::EFFECT_CLASS_DOOM); pZone->broadcastPacket(pCreature->getX(), pCreature->getY(), &gcRemoveEffect); //cout << "EffectDoom" << "unaffect END" << endl; __END_DEBUG __END_CATCH }
void CGDownSkillHandler::execute (CGDownSkill* pPacket , Player* pPlayer) throw(ProtocolException, Error) { __BEGIN_TRY __BEGIN_DEBUG_EX #ifdef __GAME_SERVER__ Assert(pPacket != NULL); Assert(pPlayer != NULL); GamePlayer* pGamePlayer = dynamic_cast<GamePlayer*>(pPlayer); Assert(pGamePlayer != NULL); PlayerCreature* pPC = dynamic_cast<PlayerCreature*>(pGamePlayer->getCreature()); Assert(pPC != NULL); SkillType_t targetSkillType = pPacket->getSkillType(); GCDownSkillFailed failpkt; failpkt.setSkillType(targetSkillType); if (!pPC->isOusters() ) { failpkt.setDesc(NOT_OUSTERS); pPlayer->sendPacket(&failpkt); return; } Ousters* pOusters = dynamic_cast<Ousters*>(pPC); Assert(pOusters != NULL); OustersSkillSlot* pTargetSkillSlot = pOusters->getSkill(targetSkillType); if (pTargetSkillSlot == NULL ) { failpkt.setDesc(HAVE_NOT_SKILL); pPlayer->sendPacket(&failpkt); return; } SkillInfo* pTargetSkillInfo = NULL; try { pTargetSkillInfo = g_pSkillInfoManager->getSkillInfo(targetSkillType); } catch(Exception& e) { failpkt.setDesc(INVALID_SKILL); pPlayer->sendPacket(&failpkt); return; } if (pTargetSkillSlot->getExpLevel() <= 1 ) { if (!pTargetSkillInfo->canDelete() ) { failpkt.setDesc(TOO_LOW); pPlayer->sendPacket(&failpkt); return; } list<SkillType_t>& rRequiredSkills = pTargetSkillInfo->getRequiredSkills(); list<SkillType_t>::iterator itr = rRequiredSkills.begin(); for (; itr != rRequiredSkills.end(); ++itr ) { if (pOusters->hasSkill(*itr) != NULL ) { bool canDrop = false; SkillInfo* pFollowingSkillInfo = g_pSkillInfoManager->getSkillInfo(*itr); list<SkillType_t>& rRequireSkills = pFollowingSkillInfo->getRequireSkills(); list<SkillType_t>::iterator itr2 = rRequireSkills.begin(); for (; itr2 != rRequireSkills.end(); ++itr2 ) { if ((*itr2) != targetSkillType && pOusters->hasSkill(*itr2) != NULL ) { SkillInfo* pAlternativeSkillInfo = g_pSkillInfoManager->getSkillInfo(*itr2); if (getSkillMapID((ElementalDomain)pAlternativeSkillInfo->getElementalDomain()) == getSkillMapID((ElementalDomain)pTargetSkillInfo->getElementalDomain()) ) canDrop = true; } } if (!canDrop ) { failpkt.setDesc(CANNOT_DROP_SKILL); pPlayer->sendPacket(&failpkt); return; } } } } /* if (pTargetSkillSlot->getExpLevel() >= 30 ) { failpkt.setDesc(TOO_HIGH); pPlayer->sendPacket(&failpkt); return; }*/ Assert(pTargetSkillInfo != NULL); int backPoint = pTargetSkillInfo->getLevelUpPoint(); Price_t downPrice = (int)(backPoint * pow(pOusters->getLevel(),1.3) * 200); if (pTargetSkillSlot->getExpLevel() <= 1 ) { downPrice *= 5; if (downPrice == 0 ) downPrice = 1000000; } if (pOusters->getGold() < downPrice ) { failpkt.setDesc(NOT_ENOUGH_MONEY); pPlayer->sendPacket(&failpkt); return; } pOusters->decreaseGoldEx(downPrice); pTargetSkillSlot->setExpLevel(pTargetSkillSlot->getExpLevel() - 1); pTargetSkillSlot->save(); if (pTargetSkillSlot->getExpLevel() <= 0 ) { pTargetSkillSlot->destroy(pOusters->getName()); backPoint = pTargetSkillInfo->getSkillPoint(); pOusters->removeSkill(targetSkillType); } pOusters->setSkillBonus(pOusters->getSkillBonus() + backPoint); char query[50]; sprintf(query, "SkillBonus=%d", pOusters->getSkillBonus()); pOusters->tinysave(query); GCDownSkillOK okpkt; okpkt.setSkillType(targetSkillType); pPlayer->sendPacket(&okpkt); GCModifyInformation gcMI; gcMI.addLongData(MODIFY_GOLD, pOusters->getGold()); gcMI.addShortData(MODIFY_SKILL_BONUS_POINT, pOusters->getSkillBonus()); switch (targetSkillType ) { case SKILL_HIDE_SIGHT: { OUSTERS_RECORD prev; pOusters->getOustersRecord(prev); pOusters->initAllStat(); pOusters->sendRealWearingInfo(); pOusters->addModifyInfo(prev, gcMI); } break; default : break; } pPlayer->sendPacket(&gcMI); #endif // __GAME_SERVER__ __END_DEBUG_EX __END_CATCH }