@@ -306,13 +306,13 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
|
||||
"SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d AND lValue<>0",
|
||||
GetTablePostfix(), pTab->id);
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle,0,packet->account_id));
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle, 0, packet->account_id));
|
||||
|
||||
// Affect
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d",
|
||||
GetTablePostfix(), pTab->id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle));
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
|
||||
}
|
||||
/////////////////////////////////////////////
|
||||
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
|
||||
@@ -601,6 +601,22 @@ void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD
|
||||
|
||||
case QID_AFFECT:
|
||||
sys_log(0, "QID_AFFECT %u", info->dwHandle);
|
||||
|
||||
// MR-8: Fix "when_login" being loaded before character affects
|
||||
if (!mysql_num_rows(pSQLResult))
|
||||
{
|
||||
TPacketAffectElement pAffElem{};
|
||||
DWORD dwCount = 0;
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_AFFECT_LOAD, info->dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount);
|
||||
peer->Encode(&info->player_id, sizeof(DWORD));
|
||||
peer->Encode(&dwCount, sizeof(DWORD));
|
||||
peer->Encode(&pAffElem, sizeof(TPacketAffectElement) * dwCount);
|
||||
|
||||
break;
|
||||
}
|
||||
// MR-8: -- END OF -- Fix "when_login" being loaded before character affects
|
||||
|
||||
RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle);
|
||||
break;
|
||||
/*
|
||||
|
||||
@@ -272,7 +272,9 @@ struct FSkillEarthQuake
|
||||
|
||||
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE);
|
||||
|
||||
SkillAttackAffect( ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" );
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
SkillAttackAffect( pAttacker, ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" );
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
sys_log(0, "BlueDragon: EarthQuake to %s addPct(%d) dam(%d) sec(%d)", ch->GetName(), addPct, dam, sec);
|
||||
|
||||
|
||||
@@ -40,6 +40,16 @@ inline void AttackAffect(LPCHARACTER pkAttacker,
|
||||
int time,
|
||||
const char* name)
|
||||
{
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
if (pkVictim->IsDamageImmune() && (pkVictim->IsMonster() || pkVictim->IsStone() || pkVictim->IsDoor()))
|
||||
{
|
||||
if (!pkVictim->CheckDamageImmunityConditions(pkAttacker))
|
||||
{
|
||||
return; // Immunity prevents stun application
|
||||
}
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
if (pkAttacker->GetPoint(att_point) && !pkVictim->IsAffectFlag(affect_flag))
|
||||
{
|
||||
if (number(1, 100) <= pkAttacker->GetPoint(att_point) && !pkVictim->IsImmune(immune_flag))
|
||||
@@ -58,7 +68,9 @@ inline void AttackAffect(LPCHARACTER pkAttacker,
|
||||
}
|
||||
}
|
||||
|
||||
inline void SkillAttackAffect(LPCHARACTER pkVictim,
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
inline void SkillAttackAffect(LPCHARACTER pkAttacker,
|
||||
LPCHARACTER pkVictim,
|
||||
int success_pct,
|
||||
DWORD immune_flag,
|
||||
DWORD affect_idx,
|
||||
@@ -68,6 +80,15 @@ inline void SkillAttackAffect(LPCHARACTER pkVictim,
|
||||
int time,
|
||||
const char* name)
|
||||
{
|
||||
if (pkVictim->IsDamageImmune() && (pkVictim->IsMonster() || pkVictim->IsStone() || pkVictim->IsDoor()))
|
||||
{
|
||||
if (!pkVictim->CheckDamageImmunityConditions(pkAttacker))
|
||||
{
|
||||
return; // Immunity prevents skill affect application
|
||||
}
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
if (success_pct && !pkVictim->IsAffectFlag(affect_flag))
|
||||
{
|
||||
if (number(1, 1000) <= success_pct && !pkVictim->IsImmune(immune_flag))
|
||||
|
||||
@@ -376,6 +376,11 @@ void CHARACTER::Initialize()
|
||||
|
||||
memset(&m_tvLastSyncTime, 0, sizeof(m_tvLastSyncTime));
|
||||
m_iSyncHackCount = 0;
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
m_bDamageImmune = false;
|
||||
m_vecDamageImmunityConditions.clear();
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
|
||||
void CHARACTER::Create(const char * c_pszName, DWORD vid, bool isPC)
|
||||
@@ -1502,6 +1507,17 @@ bool CHARACTER::Show(long lMapIndex, long x, long y, long z, bool bShowSpawnMoti
|
||||
REMOVE_BIT(m_bAddChrState, ADD_CHARACTER_STATE_SPAWN);
|
||||
|
||||
SetValidComboInterval(0);
|
||||
|
||||
// MR-8: Resync affects when moving within same map (fixes dungeon relog affect icon bug)
|
||||
if (IsPC() && GetDesc() != NULL) {
|
||||
// Re-send all affect packets to client to ensure icons are synchronized
|
||||
itertype(m_list_pkAffect) it = m_list_pkAffect.begin();
|
||||
|
||||
while (it != m_list_pkAffect.end())
|
||||
SendAffectAddPacket(GetDesc(), *it++);
|
||||
}
|
||||
// MR-8: -- END OF -- Resync affects when moving within same map
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7400,5 +7416,184 @@ int CHARACTER::GetSkillPowerByLevel(int level, bool bMob) const
|
||||
|
||||
void CHARACTER::SetLastPMPulse(void)
|
||||
{
|
||||
m_iLastPMPulse = thecore_pulse() + passes_per_sec;
|
||||
m_iLastPMPulse = thecore_pulse() + passes_per_sec;
|
||||
}
|
||||
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
void CHARACTER::SetDamageImmunity(bool bImmune)
|
||||
{
|
||||
m_bDamageImmune = bImmune;
|
||||
|
||||
if (test_server)
|
||||
{
|
||||
DWORD vid = GetVID();
|
||||
|
||||
sys_log(0, "SetDamageImmunity: %s [%u] immune=%d conditions=%d",
|
||||
GetName(), vid, bImmune, m_vecDamageImmunityConditions.size());
|
||||
}
|
||||
}
|
||||
|
||||
void CHARACTER::AddDamageImmunityCondition(BYTE bType, DWORD dwValue, const std::string& strExtra)
|
||||
{
|
||||
SDamageImmunityCondition cond(bType, dwValue, strExtra);
|
||||
m_vecDamageImmunityConditions.push_back(cond);
|
||||
|
||||
if (test_server)
|
||||
{
|
||||
DWORD vid = GetVID();
|
||||
|
||||
sys_log(0, "AddDamageImmunityCondition: %s [%u] type=%d value=%u extra=%s",
|
||||
GetName(), vid, bType, dwValue, strExtra.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CHARACTER::ClearDamageImmunityConditions()
|
||||
{
|
||||
m_vecDamageImmunityConditions.clear();
|
||||
|
||||
if (test_server)
|
||||
{
|
||||
DWORD vid = GetVID();
|
||||
|
||||
sys_log(0, "ClearDamageImmunityConditions: %s [%u]", GetName(), vid);
|
||||
}
|
||||
}
|
||||
|
||||
bool CHARACTER::CheckDamageImmunityConditions(LPCHARACTER pAttacker) const
|
||||
{
|
||||
if (!pAttacker)
|
||||
return false;
|
||||
|
||||
// If no conditions are set, block all damage
|
||||
if (m_vecDamageImmunityConditions.empty())
|
||||
{
|
||||
if (test_server && m_bDamageImmune)
|
||||
{
|
||||
DWORD vid = GetVID();
|
||||
|
||||
sys_err("CheckDamageImmunityConditions: %s [%u] has immunity flag but NO conditions - blocking damage",
|
||||
GetName(), vid);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<BYTE> allowedJobs;
|
||||
|
||||
// All conditions must pass (AND logic)
|
||||
for (std::vector<SDamageImmunityCondition>::const_iterator it = m_vecDamageImmunityConditions.begin();
|
||||
it != m_vecDamageImmunityConditions.end(); ++it)
|
||||
{
|
||||
const SDamageImmunityCondition& cond = *it;
|
||||
|
||||
switch (cond.bType)
|
||||
{
|
||||
case DAMAGE_IMMUNITY_COND_AFFECT:
|
||||
if (!pAttacker->FindAffect(cond.dwValue))
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need affect %u", cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_LEVEL_MIN:
|
||||
if (pAttacker->GetLevel() < (int)cond.dwValue)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need level >= %u", cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_LEVEL_MAX:
|
||||
if (pAttacker->GetLevel() > (int)cond.dwValue)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need level <= %u", cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_QUEST_FLAG:
|
||||
if (!pAttacker->IsPC())
|
||||
return false;
|
||||
|
||||
if (pAttacker->GetQuestFlag(cond.strExtra) != (int)cond.dwValue)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need quest flag %s == %u",
|
||||
cond.strExtra.c_str(), cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_ITEM_EQUIPPED:
|
||||
if (!pAttacker->IsEquipUniqueItem(cond.dwValue))
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need item %u equipped", cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_EMPIRE:
|
||||
if (pAttacker->GetEmpire() != (BYTE)cond.dwValue)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need empire %u", cond.dwValue);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAMAGE_IMMUNITY_COND_JOB:
|
||||
if (!pAttacker->IsPC())
|
||||
return false;
|
||||
|
||||
if ((BYTE)cond.dwValue >= JOB_MAX_NUM)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: invalid job value");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
allowedJobs.push_back((BYTE)cond.dwValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
sys_err("Unknown damage immunity condition type: %d", cond.bType);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check job requirements (OR logic among jobs, AND with other conditions)
|
||||
if (!allowedJobs.empty())
|
||||
{
|
||||
bool bJobAllowed = false;
|
||||
BYTE bJob = pAttacker->GetJob();
|
||||
|
||||
for (std::vector<BYTE>::const_iterator jt = allowedJobs.begin(); jt != allowedJobs.end(); ++jt)
|
||||
{
|
||||
if (bJob == *jt)
|
||||
{
|
||||
bJobAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bJobAllowed)
|
||||
{
|
||||
if (test_server)
|
||||
pAttacker->ChatPacket(CHAT_TYPE_INFO, "Target immune: need job match (your job: %d)", bJob);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All conditions passed
|
||||
return true;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
@@ -1641,6 +1641,42 @@ class CHARACTER : public CEntity, public CFSM, public CHorseRider
|
||||
void ApplyMobAttribute(const TMobTable* table);
|
||||
// End of Resists & Proofs
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
public:
|
||||
enum EDamageImmunityConditionType
|
||||
{
|
||||
DAMAGE_IMMUNITY_COND_AFFECT = 0, // Has affect
|
||||
DAMAGE_IMMUNITY_COND_LEVEL_MIN = 1, // Level >= value
|
||||
DAMAGE_IMMUNITY_COND_LEVEL_MAX = 2, // Level <= value
|
||||
DAMAGE_IMMUNITY_COND_QUEST_FLAG = 3, // Quest flag == value
|
||||
DAMAGE_IMMUNITY_COND_ITEM_EQUIPPED = 4, // Has item vnum equipped
|
||||
DAMAGE_IMMUNITY_COND_EMPIRE = 5, // Is specific empire
|
||||
DAMAGE_IMMUNITY_COND_JOB = 6, // Is specific job
|
||||
};
|
||||
|
||||
struct SDamageImmunityCondition
|
||||
{
|
||||
BYTE bType;
|
||||
DWORD dwValue;
|
||||
std::string strExtra; // For quest flag names, etc.
|
||||
|
||||
SDamageImmunityCondition() : bType(0), dwValue(0) {}
|
||||
SDamageImmunityCondition(BYTE t, DWORD v, const std::string& e = "")
|
||||
: bType(t), dwValue(v), strExtra(e) {}
|
||||
};
|
||||
|
||||
void SetDamageImmunity(bool bImmune);
|
||||
bool IsDamageImmune() const { return m_bDamageImmune; }
|
||||
void AddDamageImmunityCondition(BYTE bType, DWORD dwValue, const std::string& strExtra = "");
|
||||
void ClearDamageImmunityConditions();
|
||||
bool CheckDamageImmunityConditions(LPCHARACTER pAttacker) const;
|
||||
|
||||
protected:
|
||||
bool m_bDamageImmune;
|
||||
std::vector<SDamageImmunityCondition> m_vecDamageImmunityConditions;
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// QUEST
|
||||
//
|
||||
|
||||
@@ -1769,6 +1769,39 @@ bool CHARACTER::Damage(LPCHARACTER pAttacker, int dam, EDamageType type) // retu
|
||||
|
||||
//PROF_UNIT puAttr("Attr");
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// Damage Immunity System - per-hit check (O(1) operation)
|
||||
// Check immunity for monsters/stones/doors with the flag set
|
||||
if (m_bDamageImmune && (IsMonster() || IsStone() || IsDoor()))
|
||||
{
|
||||
// Check if attacker meets all required conditions
|
||||
// NOTE: If flag is set but conditions are empty, this will return false (block all damage)
|
||||
if (!CheckDamageImmunityConditions(pAttacker))
|
||||
{
|
||||
// Attacker doesn't meet conditions - send MISS packet directly
|
||||
if (pAttacker->IsPC())
|
||||
{
|
||||
TPacketGCDamageInfo damageInfo;
|
||||
|
||||
memset(&damageInfo, 0, sizeof(TPacketGCDamageInfo));
|
||||
|
||||
damageInfo.header = HEADER_GC_DAMAGE_INFO;
|
||||
damageInfo.dwVID = (DWORD)GetVID();
|
||||
damageInfo.flag = DAMAGE_DODGE;
|
||||
damageInfo.damage = 0;
|
||||
|
||||
if (pAttacker->GetDesc() != NULL)
|
||||
{
|
||||
pAttacker->GetDesc()->Packet(&damageInfo, sizeof(TPacketGCDamageInfo));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
// All conditions met - allow damage to pass through
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
//
|
||||
// 마법형 스킬과, 레인지형 스킬은(궁자객) 크리티컬과, 관통공격 계산을 한다.
|
||||
// 원래는 하지 않아야 하는데 Nerf(다운밸런스)패치를 할 수 없어서 크리티컬과
|
||||
|
||||
@@ -42,6 +42,15 @@ bool CHARACTER::StartRiding()
|
||||
return false;
|
||||
}
|
||||
|
||||
// MR-8: Prevent mounting in Nemere's Watchtower
|
||||
long lMapIndex = GetMapIndex();
|
||||
|
||||
if (lMapIndex >= 352 * 10000 && lMapIndex < 353 * 10000)
|
||||
{
|
||||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride your horse in Nemere's Watchtower."));
|
||||
return false;
|
||||
}
|
||||
// MR-8: -- END OF -- Prevent mounting in Nemere's Watchtower
|
||||
|
||||
DWORD dwMountVnum = m_chHorse ? m_chHorse->GetRaceNum() : GetMyHorseVnum();
|
||||
|
||||
@@ -151,6 +160,16 @@ void CHARACTER::HorseSummon(bool bSummon, bool bFromFar, DWORD dwVnum, const cha
|
||||
if (IsRiding())
|
||||
return;
|
||||
|
||||
// MR-8: Prevent mounting in Nemere's Watchtower
|
||||
long lMapIndex = GetMapIndex();
|
||||
|
||||
if (lMapIndex >= 352 * 10000 && lMapIndex < 353 * 10000)
|
||||
{
|
||||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride your horse in Nemere's Watchtower."));
|
||||
return;
|
||||
}
|
||||
// MR-8: -- END OF -- Prevent mounting in Nemere's Watchtower
|
||||
|
||||
sys_log(0, "HorseSummon : %s lv:%d bSummon:%d fromFar:%d", GetName(), GetLevel(), bSummon, bFromFar);
|
||||
|
||||
long x = GetX();
|
||||
@@ -357,10 +376,12 @@ bool CHARACTER::CanUseHorseSkill()
|
||||
{
|
||||
if(IsRiding())
|
||||
{
|
||||
if (GetHorseGrade() == 3)
|
||||
// MR-8: CanUseHorseSkill() grade fix
|
||||
if (GetHorseGrade() >= 3)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
// MR-8: -- END OF -- CanUseHorseSkill() grade fix
|
||||
|
||||
if(GetMountVnum())
|
||||
{
|
||||
|
||||
@@ -7601,6 +7601,19 @@ bool CHARACTER::CanEquipNow(const LPITEM item, const TItemPos& srcCell, const TI
|
||||
return false;
|
||||
}
|
||||
|
||||
// MR-8: Prevent mounting in Nemere's Watchtower
|
||||
if (item->GetSpecialGroup() == UNIQUE_GROUP_SPECIAL_RIDE)
|
||||
{
|
||||
long lMapIndex = GetMapIndex();
|
||||
bool isInNemereDungeon = lMapIndex >= 352 * 10000 && lMapIndex < 353 * 10000;
|
||||
|
||||
if (isInNemereDungeon && !IsRiding())
|
||||
{
|
||||
ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride your horse in Nemere's Watchtower."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// MR-8: -- END OF -- Prevent mounting in Nemere's Watchtower
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -558,7 +558,82 @@ bool CHARACTER_MANAGER::SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, in
|
||||
}
|
||||
}
|
||||
|
||||
LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon)
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
LPCHARACTER CHARACTER_MANAGER::SpawnGroupWithVIDs(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon, std::vector<DWORD>& rVids)
|
||||
{
|
||||
CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum);
|
||||
|
||||
if (!pkGroup)
|
||||
{
|
||||
sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPCHARACTER pkChrMaster = NULL;
|
||||
LPPARTY pkParty = NULL;
|
||||
|
||||
const std::vector<DWORD> & c_rdwMembers = pkGroup->GetMemberVector();
|
||||
|
||||
bool bSpawnedByStone = false;
|
||||
bool bAggressive = bAggressive_;
|
||||
|
||||
if (m_pkChrSelectedStone)
|
||||
{
|
||||
bSpawnedByStone = true;
|
||||
|
||||
if (m_pkChrSelectedStone->GetDungeon())
|
||||
bAggressive = true;
|
||||
}
|
||||
|
||||
LPCHARACTER chLeader = NULL;
|
||||
|
||||
for (DWORD i = 0; i < c_rdwMembers.size(); ++i)
|
||||
{
|
||||
LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone);
|
||||
|
||||
if (!tch)
|
||||
{
|
||||
if (i == 0) // 못만든 몬스터가 대장일 경우에는 그냥 실패
|
||||
return NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
chLeader = tch;
|
||||
|
||||
rVids.push_back(tch->GetVID());
|
||||
|
||||
tch->SetDungeon(pDungeon);
|
||||
|
||||
sx = tch->GetX() - number(300, 500);
|
||||
sy = tch->GetY() - number(300, 500);
|
||||
ex = tch->GetX() + number(300, 500);
|
||||
ey = tch->GetY() + number(300, 500);
|
||||
|
||||
if (m_pkChrSelectedStone)
|
||||
tch->SetStone(m_pkChrSelectedStone);
|
||||
else if (pkParty)
|
||||
{
|
||||
pkParty->Join(tch->GetVID());
|
||||
pkParty->Link(tch);
|
||||
}
|
||||
else if (!pkChrMaster)
|
||||
{
|
||||
pkChrMaster = tch;
|
||||
pkChrMaster->SetRegen(pkRegen);
|
||||
|
||||
pkParty = CPartyManager::instance().CreateParty(pkChrMaster);
|
||||
}
|
||||
|
||||
if (bAggressive)
|
||||
tch->SetAggressive();
|
||||
}
|
||||
|
||||
return chLeader;
|
||||
}
|
||||
|
||||
LPCHARACTER CHARACTER_MANAGER::SpawnGroupWithImmunity(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions)
|
||||
{
|
||||
CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum);
|
||||
|
||||
@@ -603,6 +678,14 @@ LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx,
|
||||
|
||||
tch->SetDungeon(pDungeon);
|
||||
|
||||
// Apply damage immunity atomically before mob is attackable
|
||||
if (!conditions.empty())
|
||||
{
|
||||
tch->SetDamageImmunity(true);
|
||||
for (const auto& cond : conditions)
|
||||
tch->AddDamageImmunityCondition(cond.bType, cond.dwValue, cond.strExtra);
|
||||
}
|
||||
|
||||
sx = tch->GetX() - number(300, 500);
|
||||
sy = tch->GetY() - number(300, 500);
|
||||
ex = tch->GetX() + number(300, 500);
|
||||
@@ -630,6 +713,13 @@ LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx,
|
||||
return chLeader;
|
||||
}
|
||||
|
||||
LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon)
|
||||
{
|
||||
std::vector<DWORD> dummy;
|
||||
return SpawnGroupWithVIDs(dwVnum, lMapIndex, sx, sy, ex, ey, pkRegen, bAggressive_, pDungeon, dummy);
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
struct FuncUpdateAndResetChatCounter
|
||||
{
|
||||
void operator () (LPCHARACTER ch)
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
#include "common/length.h"
|
||||
|
||||
#include "vid.h"
|
||||
#include "char.h"
|
||||
|
||||
class CDungeon;
|
||||
class CHARACTER;
|
||||
// class CHARACTER;
|
||||
class CharacterVectorInteractor;
|
||||
|
||||
class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
|
||||
@@ -40,6 +41,10 @@ class CHARACTER_MANAGER : public singleton<CHARACTER_MANAGER>
|
||||
LPCHARACTER SpawnMob(DWORD dwVnum, long lMapIndex, long x, long y, long z, bool bSpawnMotion = false, int iRot = -1, bool bShow = true);
|
||||
LPCHARACTER SpawnMobRange(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, bool bIsException=false, bool bSpawnMotion = false , bool bAggressive = false);
|
||||
LPCHARACTER SpawnGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL);
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
LPCHARACTER SpawnGroupWithVIDs(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon, std::vector<DWORD>& rVids);
|
||||
LPCHARACTER SpawnGroupWithImmunity(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
bool SpawnGroupGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen = NULL, bool bAggressive_ = false, LPDUNGEON pDungeon = NULL);
|
||||
bool SpawnMoveGroup(DWORD dwVnum, long lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen = NULL, bool bAggressive_ = false);
|
||||
LPCHARACTER SpawnMobRandomPosition(DWORD dwVnum, long lMapIndex);
|
||||
|
||||
@@ -167,7 +167,17 @@ void CHARACTER::AttackedByFire(LPCHARACTER pkAttacker, int amount, int count)
|
||||
if (m_pkFireEvent)
|
||||
return;
|
||||
|
||||
AddAffect(AFFECT_FIRE, POINT_NONE, 0, AFF_FIRE, count*3+1, 0, true);
|
||||
// MR-8: Check damage immunity system - prevent fire application if conditions not met
|
||||
if (m_bDamageImmune && (IsMonster() || IsStone() || IsDoor()))
|
||||
{
|
||||
if (!CheckDamageImmunityConditions(pkAttacker))
|
||||
{
|
||||
return; // Immunity prevents fire application
|
||||
}
|
||||
}
|
||||
|
||||
AddAffect(AFFECT_FIRE, POINT_NONE, 0, AFF_FIRE, count * 3 + 1, 0, true);
|
||||
// MR-8: -- END OF -- Check damage immunity system - prevent fire application if conditions not met
|
||||
|
||||
TFireEventInfo* info = AllocEventInfo<TFireEventInfo>();
|
||||
|
||||
@@ -187,6 +197,16 @@ void CHARACTER::AttackedByPoison(LPCHARACTER pkAttacker)
|
||||
if (m_bHasPoisoned && !IsPC()) // 몬스터는 독이 한번만 걸린다.
|
||||
return;
|
||||
|
||||
// MR-8: Check damage immunity system - prevent poison application if conditions not met
|
||||
if (m_bDamageImmune && (IsMonster() || IsStone() || IsDoor()))
|
||||
{
|
||||
if (!CheckDamageImmunityConditions(pkAttacker))
|
||||
{
|
||||
return; // Immunity prevents poison application
|
||||
}
|
||||
}
|
||||
// MR-8: -- END OF -- Check damage immunity system - prevent poison application if conditions not met
|
||||
|
||||
if (pkAttacker && pkAttacker->GetLevel() < GetLevel())
|
||||
{
|
||||
int delta = GetLevel() - pkAttacker->GetLevel();
|
||||
|
||||
@@ -1441,13 +1441,14 @@ struct FuncSplashDamage
|
||||
|
||||
iDur += m_pkChr->GetPoint(POINT_PARTY_BUFFER_BONUS);
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_STUN))
|
||||
{
|
||||
SkillAttackAffect(pkChrVictim, iPct, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iDur, m_pkSk->szName);
|
||||
SkillAttackAffect(m_pkChr, pkChrVictim, iPct, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, iDur, m_pkSk->szName);
|
||||
}
|
||||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_SLOW))
|
||||
{
|
||||
SkillAttackAffect(pkChrVictim, iPct, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, iDur, m_pkSk->szName);
|
||||
SkillAttackAffect(m_pkChr, pkChrVictim, iPct, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, iDur, m_pkSk->szName);
|
||||
}
|
||||
else if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_FIRE_CONT))
|
||||
{
|
||||
@@ -1472,6 +1473,7 @@ struct FuncSplashDamage
|
||||
if (number(1, 100) <= iPct)
|
||||
pkChrVictim->AttackedByPoison(m_pkChr);
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
|
||||
if (IS_SET(m_pkSk->dwFlag, SKILL_FLAG_CRUSH | SKILL_FLAG_CRUSH_LONG) &&
|
||||
@@ -1510,11 +1512,13 @@ struct FuncSplashDamage
|
||||
|
||||
if (m_pkChr->IsPC() && m_pkChr->m_SkillUseInfo[m_pkSk->dwVnum].GetMainTargetVID() == (DWORD) pkChrVictim->GetVID())
|
||||
{
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
//if (!g_iUseLocale)
|
||||
if (LC_IsYMIR())
|
||||
SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 3, m_pkSk->szName);
|
||||
SkillAttackAffect(m_pkChr, pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 3, m_pkSk->szName);
|
||||
else
|
||||
SkillAttackAffect(pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName);
|
||||
SkillAttackAffect(m_pkChr, pkChrVictim, 1000, IMMUNE_STUN, m_pkSk->dwVnum, POINT_NONE, 0, AFF_STUN, 4, m_pkSk->szName);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1985,9 +1989,14 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
|
||||
CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum);
|
||||
const bool bCanUseHorseSkill = CanUseHorseSkill();
|
||||
|
||||
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return
|
||||
if (false == bCanUseHorseSkill && true == IsRiding())
|
||||
return BATTLE_NONE;
|
||||
// MR-8: Flame Ghost skill fix on mounting
|
||||
if (dwVnum != SKILL_MUYEONG)
|
||||
{
|
||||
// 말을 타고있지만 스킬은 사용할 수 없는 상태라면 return
|
||||
if (false == bCanUseHorseSkill && true == IsRiding())
|
||||
return BATTLE_NONE;
|
||||
}
|
||||
// MR-8: -- END OF -- Flame Ghost skill fix on mounting
|
||||
|
||||
if (IsPolymorphed())
|
||||
return BATTLE_NONE;
|
||||
@@ -1998,12 +2007,16 @@ int CHARACTER::ComputeSkill(DWORD dwVnum, LPCHARACTER pkVictim, BYTE bSkillLevel
|
||||
if (!pkSk)
|
||||
return BATTLE_NONE;
|
||||
|
||||
if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE)
|
||||
return BATTLE_NONE;
|
||||
// MR-8: Flame Ghost skill fix on mounting
|
||||
if (dwVnum != SKILL_MUYEONG)
|
||||
{
|
||||
if (bCanUseHorseSkill && pkSk->dwType != SKILL_TYPE_HORSE)
|
||||
return BATTLE_NONE;
|
||||
|
||||
if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE)
|
||||
return BATTLE_NONE;
|
||||
|
||||
if (!bCanUseHorseSkill && pkSk->dwType == SKILL_TYPE_HORSE)
|
||||
return BATTLE_NONE;
|
||||
}
|
||||
// MR-8: -- END OF -- Flame Ghost skill fix on mounting
|
||||
|
||||
// 상대방에게 쓰는 것이 아니면 나에게 써야 한다.
|
||||
if (IS_SET(pkSk->dwFlag, SKILL_FLAG_SELFONLY))
|
||||
|
||||
@@ -65,15 +65,17 @@ void Command_ApplyAffect(LPCHARACTER ch, const char* argument, const char* affec
|
||||
return;
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
switch (cmdAffect)
|
||||
{
|
||||
case COMMANDAFFECT_STUN:
|
||||
SkillAttackAffect(tch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, 30, "GM_STUN");
|
||||
SkillAttackAffect(ch, tch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, 30, "GM_STUN");
|
||||
break;
|
||||
case COMMANDAFFECT_SLOW:
|
||||
SkillAttackAffect(tch, 1000, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 30, "GM_SLOW");
|
||||
SkillAttackAffect(ch, tch, 1000, IMMUNE_SLOW, AFFECT_SLOW, POINT_MOV_SPEED, -30, AFF_SLOW, 30, "GM_SLOW");
|
||||
break;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
sys_log(0, "%s %s", arg1, affectName);
|
||||
|
||||
|
||||
@@ -763,6 +763,46 @@ LPCHARACTER CDungeon::SpawnMob_ac_dir(DWORD vnum, int x, int y, int dir)
|
||||
return ch;
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
LPCHARACTER CDungeon::SpawnMobWithImmunity(DWORD vnum, int x, int y, int dir, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions)
|
||||
{
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
if (pkSectreeMap == NULL) {
|
||||
sys_err("CDungeon: SECTREE_MAP not found for #%ld", m_lMapIndex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long ax = pkSectreeMap->m_setting.iBaseX + x * 100;
|
||||
long ay = pkSectreeMap->m_setting.iBaseY + y * 100;
|
||||
|
||||
int rot = (dir == 0 ? -1 : (dir - 1) * 45);
|
||||
|
||||
// Create but do NOT show yet
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().SpawnMob(vnum, m_lMapIndex, ax, ay, 0, false, rot, false);
|
||||
if (!ch)
|
||||
return NULL;
|
||||
|
||||
// Apply damage immunity atomically before showing
|
||||
if (!conditions.empty())
|
||||
{
|
||||
ch->SetDamageImmunity(true);
|
||||
for (const auto& c : conditions)
|
||||
ch->AddDamageImmunityCondition(c.bType, c.dwValue, c.strExtra);
|
||||
}
|
||||
|
||||
// Link to dungeon and show
|
||||
ch->SetDungeon(this);
|
||||
if (!ch->Show(m_lMapIndex, ax, ay, 0, false))
|
||||
{
|
||||
M2_DESTROY_CHARACTER(ch);
|
||||
sys_log(0, "SpawnMobWithImmunity: cannot show monster");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
void CDungeon::SpawnNameMob(DWORD vnum, int x, int y, const char* name)
|
||||
{
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
@@ -833,6 +873,67 @@ LPCHARACTER CDungeon::SpawnGroup(DWORD vnum, long x, long y, float radius, bool
|
||||
return ch;
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
bool CDungeon::SpawnGroupWithVIDs(DWORD vnum, long x, long y, float radius, bool bAggressive, int count, std::vector<DWORD>& rVids)
|
||||
{
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
if (pkSectreeMap == NULL) {
|
||||
sys_err("CDungeon: SECTREE_MAP not found for #%ld", m_lMapIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
int iRadius = (int) radius;
|
||||
|
||||
int sx = pkSectreeMap->m_setting.iBaseX + x - iRadius;
|
||||
int sy = pkSectreeMap->m_setting.iBaseY + y - iRadius;
|
||||
int ex = sx + iRadius;
|
||||
int ey = sy + iRadius;
|
||||
|
||||
bool bAny = false;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
std::vector<DWORD> localVids;
|
||||
LPCHARACTER chLeader = CHARACTER_MANAGER::instance().SpawnGroupWithVIDs(vnum, m_lMapIndex, sx, sy, ex, ey, NULL, bAggressive, this, localVids);
|
||||
if (chLeader)
|
||||
{
|
||||
bAny = true;
|
||||
if (!localVids.empty())
|
||||
rVids.insert(rVids.end(), localVids.begin(), localVids.end());
|
||||
}
|
||||
}
|
||||
|
||||
return bAny;
|
||||
}
|
||||
|
||||
LPCHARACTER CDungeon::SpawnGroupWithImmunity(DWORD vnum, long x, long y, float radius, bool bAggressive, int count, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions)
|
||||
{
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
if (pkSectreeMap == NULL) {
|
||||
sys_err("CDungeon: SECTREE_MAP not found for #%ld", m_lMapIndex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int iRadius = (int) radius;
|
||||
|
||||
int sx = pkSectreeMap->m_setting.iBaseX + x - iRadius;
|
||||
int sy = pkSectreeMap->m_setting.iBaseY + y - iRadius;
|
||||
int ex = sx + iRadius;
|
||||
int ey = sy + iRadius;
|
||||
|
||||
LPCHARACTER chLeader = NULL;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().SpawnGroupWithImmunity(vnum, m_lMapIndex, sx, sy, ex, ey, NULL, bAggressive, this, conditions);
|
||||
if (ch && !chLeader)
|
||||
chLeader = ch;
|
||||
}
|
||||
|
||||
return chLeader;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
void CDungeon::SpawnRegen(const char* filename, bool bOnce)
|
||||
{
|
||||
if (!filename)
|
||||
@@ -850,6 +951,44 @@ void CDungeon::SpawnRegen(const char* filename, bool bOnce)
|
||||
regen_do(filename, m_lMapIndex, pkSectreeMap->m_setting.iBaseX, pkSectreeMap->m_setting.iBaseY, this, bOnce);
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
bool CDungeon::SpawnRegenWithVIDs(const char* filename, bool bOnce, std::vector<DWORD>& rVids)
|
||||
{
|
||||
if (!filename)
|
||||
{
|
||||
sys_err("CDungeon::SpawnRegenWithVIDs(filename=NULL, bOnce=%d) - m_lMapIndex[%d]", bOnce, m_lMapIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
if (!pkSectreeMap)
|
||||
{
|
||||
sys_err("CDungeon::SpawnRegenWithVIDs(filename=%s, bOnce=%d) - m_lMapIndex[%d]", filename, bOnce, m_lMapIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return regen_do(filename, m_lMapIndex, pkSectreeMap->m_setting.iBaseX, pkSectreeMap->m_setting.iBaseY, this, bOnce, &rVids);
|
||||
}
|
||||
|
||||
void CDungeon::SpawnRegenWithImmunity(const char* filename, bool bOnce, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions)
|
||||
{
|
||||
if (!filename)
|
||||
{
|
||||
sys_err("CDungeon::SpawnRegenWithImmunity(filename=NULL, bOnce=%d) - m_lMapIndex[%d]", bOnce, m_lMapIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(m_lMapIndex);
|
||||
if (!pkSectreeMap)
|
||||
{
|
||||
sys_err("CDungeon::SpawnRegenWithImmunity(filename=%s, bOnce=%d) - m_lMapIndex[%d]", filename, bOnce, m_lMapIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
regen_do(filename, m_lMapIndex, pkSectreeMap->m_setting.iBaseX, pkSectreeMap->m_setting.iBaseY, this, bOnce, NULL, &conditions);
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
void CDungeon::AddRegen(LPREGEN regen)
|
||||
{
|
||||
regen->id = regen_id_++;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#ifndef __INC_METIN_II_GAME_DUNGEON_H
|
||||
#define __INC_METIN_II_GAME_DUNGEON_H
|
||||
|
||||
#include <vector>
|
||||
#include "sectree_manager.h"
|
||||
#include "char.h"
|
||||
|
||||
class CParty;
|
||||
|
||||
@@ -57,12 +59,21 @@ class CDungeon
|
||||
void Spawn(DWORD vnum, const char* pos);
|
||||
LPCHARACTER SpawnMob(DWORD vnum, int x, int y, int dir = 0);
|
||||
LPCHARACTER SpawnMob_ac_dir(DWORD vnum, int x, int y, int dir = 0);
|
||||
LPCHARACTER SpawnGroup(DWORD vnum, long x, long y, float radius, bool bAggressive=false, int count=1);
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
LPCHARACTER SpawnMobWithImmunity(DWORD vnum, int x, int y, int dir, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions);
|
||||
LPCHARACTER SpawnGroup(DWORD vnum, long x, long y, float radius, bool bAggressive = false, int count = 1);
|
||||
bool SpawnGroupWithVIDs(DWORD vnum, long x, long y, float radius, bool bAggressive, int count, std::vector<DWORD>& rVids);
|
||||
LPCHARACTER SpawnGroupWithImmunity(DWORD vnum, long x, long y, float radius, bool bAggressive, int count, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
void SpawnNameMob(DWORD vnum, int x, int y, const char* name);
|
||||
void SpawnGotoMob(long lFromX, long lFromY, long lToX, long lToY);
|
||||
|
||||
void SpawnRegen(const char* filename, bool bOnce = true);
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
bool SpawnRegenWithVIDs(const char* filename, bool bOnce, std::vector<DWORD>& rVids);
|
||||
void SpawnRegenWithImmunity(const char* filename, bool bOnce, const std::vector<CHARACTER::SDamageImmunityCondition>& conditions);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
void AddRegen(LPREGEN regen);
|
||||
void ClearRegen();
|
||||
bool IsValidRegen(LPREGEN regen, size_t regen_id);
|
||||
|
||||
@@ -1047,6 +1047,13 @@ EVENTFUNC(quest_login_event)
|
||||
}
|
||||
else if (d->IsPhase(PHASE_GAME))
|
||||
{
|
||||
// MR-8: Fix "when_login" being loaded before character affects
|
||||
if (!ch->IsLoadedAffect())
|
||||
{
|
||||
return PASSES_PER_SEC(1);
|
||||
}
|
||||
// MR-8: -- END OF -- Fix "when_login" being loaded before character affects
|
||||
|
||||
sys_log(0, "QUEST_LOAD: Login pc %d by event", ch->GetPlayerID());
|
||||
quest::CQuestManager::instance().Login(ch->GetPlayerID());
|
||||
return 0;
|
||||
@@ -1110,18 +1117,12 @@ void CInputDB::QuestLoad(LPDESC d, const char * c_pData)
|
||||
pkPC->SetLoaded();
|
||||
pkPC->Build();
|
||||
|
||||
if (ch->GetDesc()->IsPhase(PHASE_GAME))
|
||||
{
|
||||
sys_log(0, "QUEST_LOAD: Login pc %d", pQuestTable[0].dwPID);
|
||||
quest::CQuestManager::instance().Login(pQuestTable[0].dwPID);
|
||||
}
|
||||
else
|
||||
{
|
||||
quest_login_event_info* info = AllocEventInfo<quest_login_event_info>();
|
||||
info->dwPID = ch->GetPlayerID();
|
||||
// MR-8: Fix "when_login" being loaded before character affects
|
||||
quest_login_event_info* info = AllocEventInfo<quest_login_event_info>();
|
||||
info->dwPID = ch->GetPlayerID();
|
||||
|
||||
event_create(quest_login_event, info, PASSES_PER_SEC(1));
|
||||
}
|
||||
event_create(quest_login_event, info, PASSES_PER_SEC(1));
|
||||
// MR-8: -- END OF -- Fix "when_login" being loaded before character affects
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -701,17 +701,38 @@ void CInputLogin::Entergame(LPDESC d, const char * data)
|
||||
else if (marriage::WeddingManager::instance().IsWeddingMap(ch->GetMapIndex()))
|
||||
ch->SetWeddingMap(marriage::WeddingManager::instance().Find(ch->GetMapIndex()));
|
||||
else {
|
||||
// MR-8: Auto-unmount and unequip special ride seals in Nemere's Watchtower
|
||||
if (ch->GetMapIndex() >= 352 * 10000 && ch->GetMapIndex() < 353 * 10000)
|
||||
{
|
||||
ch->RemoveAffect(AFFECT_MOUNT);
|
||||
ch->RemoveAffect(AFFECT_MOUNT_BONUS);
|
||||
|
||||
if (ch->IsRiding() || ch->IsHorseRiding())
|
||||
{
|
||||
ch->StopRiding();
|
||||
ch->HorseSummon(false);
|
||||
}
|
||||
|
||||
// Check for inventory space
|
||||
int emptyCell = ch->GetEmptyInventory(1);
|
||||
|
||||
if (emptyCell != -1)
|
||||
ch->UnEquipSpecialRideUniqueItem();
|
||||
}
|
||||
// MR-8: -- END OF -- Auto-unmount and unequip special ride seals in Nemere's Watchtower
|
||||
|
||||
ch->SetDungeon(CDungeonManager::instance().FindByMapIndex(ch->GetMapIndex()));
|
||||
}
|
||||
}
|
||||
else if (CArenaManager::instance().IsArenaMap(ch->GetMapIndex()) == true)
|
||||
{
|
||||
int memberFlag = CArenaManager::instance().IsMember(ch->GetMapIndex(), ch->GetPlayerID());
|
||||
|
||||
if (memberFlag == MEMBER_OBSERVER)
|
||||
{
|
||||
ch->SetObserverMode(true);
|
||||
ch->SetArenaObserverMode(true);
|
||||
if (CArenaManager::instance().RegisterObserverPtr(ch, ch->GetMapIndex(), ch->GetX()/100, ch->GetY()/100))
|
||||
if (CArenaManager::instance().RegisterObserverPtr(ch, ch->GetMapIndex(), ch->GetX() / 100, ch->GetY() / 100))
|
||||
{
|
||||
sys_log(0, "ARENA : Observer add failed");
|
||||
}
|
||||
|
||||
@@ -221,44 +221,47 @@ bool ITEM_MANAGER::ReadSpecialDropItemFile(const char * c_pszFileName)
|
||||
const std::string& name = pTok->at(0);
|
||||
DWORD dwVnum = 0;
|
||||
|
||||
if (!GetVnumByOriginalName(name.c_str(), dwVnum))
|
||||
// MR-8: Special_Item_Group Fix for handling gold and experience
|
||||
if (name == "elk")
|
||||
{
|
||||
if (name == "exp")
|
||||
dwVnum = CSpecialItemGroup::GOLD;
|
||||
}
|
||||
else if (name == "exp")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::EXP;
|
||||
}
|
||||
else if (name == "mob")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::MOB;
|
||||
}
|
||||
else if (name == "slow")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::SLOW;
|
||||
}
|
||||
else if (name == "drain_hp")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::DRAIN_HP;
|
||||
}
|
||||
else if (name == "poison")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::POISON;
|
||||
}
|
||||
else if (name == "group")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::MOB_GROUP;
|
||||
}
|
||||
else if (!GetVnumByOriginalName(name.c_str(), dwVnum))
|
||||
{
|
||||
str_to_number(dwVnum, name.c_str());
|
||||
if (!ITEM_MANAGER::instance().GetTable(dwVnum))
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::EXP;
|
||||
}
|
||||
else if (name == "mob")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::MOB;
|
||||
}
|
||||
else if (name == "slow")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::SLOW;
|
||||
}
|
||||
else if (name == "drain_hp")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::DRAIN_HP;
|
||||
}
|
||||
else if (name == "poison")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::POISON;
|
||||
}
|
||||
else if (name == "group")
|
||||
{
|
||||
dwVnum = CSpecialItemGroup::MOB_GROUP;
|
||||
}
|
||||
else
|
||||
{
|
||||
str_to_number(dwVnum, name.c_str());
|
||||
if (!ITEM_MANAGER::instance().GetTable(dwVnum))
|
||||
{
|
||||
sys_err("ReadSpecialDropItemFile : there is no item %s : node %s", name.c_str(), stName.c_str());
|
||||
M2_DELETE(pkGroup);
|
||||
sys_err("ReadSpecialDropItemFile : there is no item %s : node %s", name.c_str(), stName.c_str());
|
||||
M2_DELETE(pkGroup);
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// MR-8: -- END OF -- Special_Item_Group Fix for handling gold and experience
|
||||
|
||||
int iCount = 0;
|
||||
str_to_number(iCount, pTok->at(1).c_str());
|
||||
@@ -286,7 +289,9 @@ bool ITEM_MANAGER::ReadSpecialDropItemFile(const char * c_pszFileName)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
loader.SetParentNode();
|
||||
|
||||
if (CSpecialItemGroup::QUEST == type)
|
||||
{
|
||||
m_map_pkQuestItemGroup.insert(std::make_pair(iVnum, pkGroup));
|
||||
@@ -309,6 +314,7 @@ bool ITEM_MANAGER::ConvSpecialDropItemFile()
|
||||
"%s/special_item_group.txt", LocaleService_GetBasePath().c_str());
|
||||
|
||||
FILE *fp = fopen("special_item_group_vnum.txt", "w");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
sys_err("could not open file (%s)", "special_item_group_vnum.txt");
|
||||
@@ -372,13 +378,16 @@ bool ITEM_MANAGER::ConvSpecialDropItemFile()
|
||||
|
||||
if (!GetVnumByOriginalName(name.c_str(), dwVnum))
|
||||
{
|
||||
// MR-8: Special_Item_Group Fix for handling gold and experience
|
||||
//if ( name == "<22><><EFBFBD><EFBFBD>ġ" ||
|
||||
if (name == "exp" ||
|
||||
if (name == "elk" ||
|
||||
name == "exp" ||
|
||||
name == "mob" ||
|
||||
name == "slow" ||
|
||||
name == "drain_hp" ||
|
||||
name == "poison" ||
|
||||
name == "group")
|
||||
// MR-8: -- END OF -- Special_Item_Group Fix for handling gold and experience
|
||||
{
|
||||
dwVnum = 0;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "desc_client.h"
|
||||
#include "desc_manager.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#undef sys_err
|
||||
#define sys_err(fmt, ...) quest::CQuestManager::instance().QuestError(std::source_location::current(), fmt __VA_OPT__(, __VA_ARGS__))
|
||||
|
||||
@@ -173,6 +175,40 @@ namespace quest
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
int dungeon_regen_file_with_vids(lua_State* L)
|
||||
{
|
||||
if (!lua_isstring(L,1))
|
||||
{
|
||||
sys_err("wrong filename");
|
||||
lua_newtable(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
CQuestManager& q = CQuestManager::instance();
|
||||
LPDUNGEON pDungeon = q.GetCurrentDungeon();
|
||||
|
||||
std::vector<DWORD> vids;
|
||||
|
||||
if (pDungeon)
|
||||
{
|
||||
const char* filename = lua_tostring(L, 1);
|
||||
pDungeon->SpawnRegenWithVIDs(filename, true, vids);
|
||||
}
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
for (size_t i = 0; i < vids.size(); ++i)
|
||||
{
|
||||
lua_pushnumber(L, i + 1);
|
||||
lua_pushnumber(L, vids[i]);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
int dungeon_set_regen_file(lua_State* L)
|
||||
{
|
||||
if (!lua_isstring(L,1))
|
||||
@@ -971,6 +1007,207 @@ namespace quest
|
||||
return 1;
|
||||
}
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
int dungeon_spawn_group_with_vids(lua_State* L)
|
||||
{
|
||||
//
|
||||
// argument: vnum,x,y,radius,aggressive,count
|
||||
// returns: table of all spawned VIDs
|
||||
//
|
||||
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 6))
|
||||
{
|
||||
sys_err("invalid argument");
|
||||
lua_newtable(L);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
CQuestManager& q = CQuestManager::instance();
|
||||
LPDUNGEON pDungeon = q.GetCurrentDungeon();
|
||||
|
||||
std::vector<DWORD> vids;
|
||||
|
||||
if (pDungeon)
|
||||
{
|
||||
DWORD group_vnum = (DWORD)lua_tonumber(L, 1);
|
||||
long local_x = (long) lua_tonumber(L, 2) * 100;
|
||||
long local_y = (long) lua_tonumber(L, 3) * 100;
|
||||
float radius = (float) lua_tonumber(L, 4) * 100;
|
||||
bool bAggressive = lua_toboolean(L, 5);
|
||||
DWORD count = (DWORD) lua_tonumber(L, 6);
|
||||
|
||||
pDungeon->SpawnGroupWithVIDs(group_vnum, local_x, local_y, radius, bAggressive, count, vids);
|
||||
}
|
||||
|
||||
// Push table of VIDs to Lua
|
||||
lua_newtable(L);
|
||||
|
||||
for (size_t i = 0; i < vids.size(); ++i)
|
||||
{
|
||||
lua_pushnumber(L, i + 1);
|
||||
lua_pushnumber(L, vids[i]);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dungeon_spawn_group_with_immunity(lua_State* L)
|
||||
{
|
||||
//
|
||||
// argument: vnum, x, y, radius, aggressive, count, conditions_table
|
||||
// conditions_table: { {type=6, value=3}, ... }
|
||||
// returns: vid of group leader
|
||||
//
|
||||
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ||
|
||||
!lua_isnumber(L, 4) || !lua_isnumber(L, 6) || !lua_istable(L, 7))
|
||||
{
|
||||
sys_err("invalid argument");
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
CQuestManager& q = CQuestManager::instance();
|
||||
LPDUNGEON pDungeon = q.GetCurrentDungeon();
|
||||
|
||||
if (!pDungeon)
|
||||
{
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD group_vnum = (DWORD)lua_tonumber(L, 1);
|
||||
long local_x = (long) lua_tonumber(L, 2) * 100;
|
||||
long local_y = (long) lua_tonumber(L, 3) * 100;
|
||||
float radius = (float) lua_tonumber(L, 4) * 100;
|
||||
bool bAggressive = lua_toboolean(L, 5);
|
||||
DWORD count = (DWORD) lua_tonumber(L, 6);
|
||||
|
||||
// Parse conditions table
|
||||
std::vector<CHARACTER::SDamageImmunityCondition> conditions;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 7) != 0)
|
||||
{
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushstring(L, "type");
|
||||
lua_gettable(L, -2);
|
||||
BYTE bType = (BYTE)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring(L, "value");
|
||||
lua_gettable(L, -2);
|
||||
DWORD dwValue = (DWORD)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
conditions.emplace_back(bType, dwValue);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
LPCHARACTER ch = pDungeon->SpawnGroupWithImmunity(group_vnum, local_x, local_y, radius, bAggressive, count, conditions);
|
||||
lua_pushnumber(L, ch ? ch->GetVID() : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dungeon_spawn_mob_with_immunity(lua_State* L)
|
||||
{
|
||||
// argument: vnum, x, y, conditions_table
|
||||
// returns: vid
|
||||
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) || !lua_istable(L, 4))
|
||||
{
|
||||
sys_err("invalid argument");
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
CQuestManager& q = CQuestManager::instance();
|
||||
LPDUNGEON pDungeon = q.GetCurrentDungeon();
|
||||
if (!pDungeon)
|
||||
{
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD mob_vnum = (DWORD)lua_tonumber(L, 1);
|
||||
int x = (int)lua_tonumber(L, 2);
|
||||
int y = (int)lua_tonumber(L, 3);
|
||||
|
||||
// Parse conditions
|
||||
std::vector<CHARACTER::SDamageImmunityCondition> conditions;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 4) != 0)
|
||||
{
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushstring(L, "type");
|
||||
lua_gettable(L, -2);
|
||||
BYTE bType = (BYTE)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring(L, "value");
|
||||
lua_gettable(L, -2);
|
||||
DWORD dwValue = (DWORD)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
conditions.emplace_back(bType, dwValue);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
LPCHARACTER ch = pDungeon->SpawnMobWithImmunity(mob_vnum, x, y, 0, conditions);
|
||||
lua_pushnumber(L, ch ? ch->GetVID() : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dungeon_regen_file_with_immunity(lua_State* L)
|
||||
{
|
||||
//
|
||||
// argument: filename, conditions_table
|
||||
// conditions_table: { {type=6, value=3}, ... }
|
||||
// returns: nothing
|
||||
//
|
||||
if (!lua_isstring(L, 1) || !lua_istable(L, 2))
|
||||
{
|
||||
sys_err("invalid argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
CQuestManager& q = CQuestManager::instance();
|
||||
LPDUNGEON pDungeon = q.GetCurrentDungeon();
|
||||
|
||||
if (!pDungeon)
|
||||
return 0;
|
||||
|
||||
const char* filename = lua_tostring(L, 1);
|
||||
|
||||
// Parse conditions table
|
||||
std::vector<CHARACTER::SDamageImmunityCondition> conditions;
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, 2) != 0)
|
||||
{
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
lua_pushstring(L, "type");
|
||||
lua_gettable(L, -2);
|
||||
BYTE bType = (BYTE)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring(L, "value");
|
||||
lua_gettable(L, -2);
|
||||
DWORD dwValue = (DWORD)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
conditions.emplace_back(bType, dwValue);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
pDungeon->SpawnRegenWithImmunity(filename, true, conditions);
|
||||
return 0;
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
int dungeon_join(lua_State* L)
|
||||
{
|
||||
if (lua_gettop(L) < 1 || !lua_isnumber(L, 1))
|
||||
@@ -1438,6 +1675,11 @@ namespace quest
|
||||
{ "spawn_name_mob", dungeon_spawn_name_mob },
|
||||
{ "spawn_goto_mob", dungeon_spawn_goto_mob },
|
||||
{ "spawn_group", dungeon_spawn_group },
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
{ "spawn_group_with_vids", dungeon_spawn_group_with_vids },
|
||||
{ "spawn_group_with_immunity", dungeon_spawn_group_with_immunity },
|
||||
{ "spawn_mob_with_immunity", dungeon_spawn_mob_with_immunity },
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
{ "spawn_unique", dungeon_spawn_unique },
|
||||
{ "spawn_move_unique", dungeon_spawn_move_unique},
|
||||
{ "spawn_move_group", dungeon_spawn_move_group},
|
||||
@@ -1463,6 +1705,10 @@ namespace quest
|
||||
{ "new_jump_party", dungeon_new_jump_party },
|
||||
{ "new_jump", dungeon_new_jump },
|
||||
{ "regen_file", dungeon_regen_file },
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
{ "regen_file_with_vids", dungeon_regen_file_with_vids },
|
||||
{ "regen_file_with_immunity", dungeon_regen_file_with_immunity },
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
{ "set_regen_file", dungeon_set_regen_file },
|
||||
{ "clear_regen", dungeon_clear_regen },
|
||||
{ "set_exit_all_at_eliminate", dungeon_set_exit_all_at_eliminate},
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "char_manager.h"
|
||||
#include "shop_manager.h"
|
||||
#include "guild.h"
|
||||
#include "mob_manager.h"
|
||||
|
||||
namespace quest
|
||||
{
|
||||
@@ -350,6 +351,214 @@ namespace quest
|
||||
return 0;
|
||||
}
|
||||
|
||||
// MR-8: Damage Immunity System - Lua Bindings
|
||||
int npc_set_damage_immunity(lua_State* L)
|
||||
{
|
||||
// npc.set_damage_immunity(vid, immune_bool)
|
||||
// Sets basic immunity on/off for a specific VID
|
||||
|
||||
if (!lua_isnumber(L, 1))
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD dwVID = (DWORD)lua_tonumber(L, 1);
|
||||
bool bImmune = lua_toboolean(L, 2);
|
||||
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(dwVID);
|
||||
if (!ch)
|
||||
{
|
||||
sys_err("npc.set_damage_immunity: VID %u not found", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ch->IsMonster() && !ch->IsStone() && !ch->IsDoor())
|
||||
{
|
||||
sys_err("npc.set_damage_immunity: VID %u is not a monster/stone/door", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch->SetDamageImmunity(bImmune);
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int npc_is_damage_immune(lua_State* L)
|
||||
{
|
||||
// npc.is_damage_immune(vid)
|
||||
// Checks if a VID has damage immunity enabled
|
||||
|
||||
if (!lua_isnumber(L, 1))
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find((DWORD)lua_tonumber(L, 1));
|
||||
lua_pushboolean(L, ch ? ch->IsDamageImmune() : false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int npc_add_damage_immunity_condition(lua_State* L)
|
||||
{
|
||||
// npc.add_damage_immunity_condition(vid, condition_type, value, [extra_string])
|
||||
// Adds a condition that must be met for attacker to damage this entity
|
||||
// Types: 0=affect, 1=level_min, 2=level_max, 3=quest_flag, 4=item_equipped, 5=empire, 6=job
|
||||
|
||||
if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3))
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD dwVID = (DWORD)lua_tonumber(L, 1);
|
||||
BYTE bType = (BYTE)lua_tonumber(L, 2);
|
||||
DWORD dwValue = (DWORD)lua_tonumber(L, 3);
|
||||
std::string strExtra = lua_isstring(L, 4) ? lua_tostring(L, 4) : "";
|
||||
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(dwVID);
|
||||
if (!ch)
|
||||
{
|
||||
sys_err("npc.add_damage_immunity_condition: VID %u not found", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ch->IsMonster() && !ch->IsStone() && !ch->IsDoor())
|
||||
{
|
||||
sys_err("npc.add_damage_immunity_condition: VID %u is not a monster/stone/door", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch->AddDamageImmunityCondition(bType, dwValue, strExtra);
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int npc_clear_damage_immunity_conditions(lua_State* L)
|
||||
{
|
||||
// npc.clear_damage_immunity_conditions(vid)
|
||||
// Removes all damage immunity conditions from a VID
|
||||
|
||||
if (!lua_isnumber(L, 1))
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find((DWORD)lua_tonumber(L, 1));
|
||||
if (!ch)
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch->ClearDamageImmunityConditions();
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int npc_set_damage_immunity_with_conditions(lua_State* L)
|
||||
{
|
||||
// npc.set_damage_immunity_with_conditions(vid, conditions_table)
|
||||
// High-level function that sets immunity and conditions in one call
|
||||
// Example: npc.set_damage_immunity_with_conditions(vid, {
|
||||
// {type=0, value=23}, -- Need affect 23
|
||||
// {type=1, value=50}, -- Need level >= 50
|
||||
// {type=3, value=1, extra="dungeon.flag"} -- Need quest flag
|
||||
// })
|
||||
|
||||
if (!lua_isnumber(L, 1) || !lua_istable(L, 2))
|
||||
{
|
||||
lua_pushboolean(L, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD dwVID = (DWORD)lua_tonumber(L, 1);
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find(dwVID);
|
||||
|
||||
if (!ch)
|
||||
{
|
||||
sys_err("npc.set_damage_immunity_with_conditions: VID %u not found", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ch->IsMonster() && !ch->IsStone() && !ch->IsDoor())
|
||||
{
|
||||
sys_err("npc.set_damage_immunity_with_conditions: VID %u is not a monster/stone/door", dwVID);
|
||||
lua_pushboolean(L, false);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// CRITICAL: Set immunity flag FIRST to close the race condition window
|
||||
// This ensures the mob is protected immediately, even before conditions are parsed
|
||||
ch->SetDamageImmunity(true);
|
||||
|
||||
// Clear existing conditions
|
||||
ch->ClearDamageImmunityConditions();
|
||||
|
||||
// Parse conditions table
|
||||
int condCount = 0;
|
||||
|
||||
lua_pushnil(L);
|
||||
|
||||
while (lua_next(L, 2) != 0)
|
||||
{
|
||||
if (lua_istable(L, -1))
|
||||
{
|
||||
BYTE bType = 0;
|
||||
DWORD dwValue = 0;
|
||||
std::string strExtra = "";
|
||||
|
||||
// Get 'type' field
|
||||
lua_pushstring(L, "type");
|
||||
lua_gettable(L, -2);
|
||||
|
||||
if (lua_isnumber(L, -1))
|
||||
bType = (BYTE)lua_tonumber(L, -1);
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Get 'value' field
|
||||
lua_pushstring(L, "value");
|
||||
lua_gettable(L, -2);
|
||||
|
||||
if (lua_isnumber(L, -1))
|
||||
dwValue = (DWORD)lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Get 'extra' field (optional)
|
||||
lua_pushstring(L, "extra");
|
||||
lua_gettable(L, -2);
|
||||
|
||||
if (lua_isstring(L, -1))
|
||||
strExtra = lua_tostring(L, -1);
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
ch->AddDamageImmunityCondition(bType, dwValue, strExtra);
|
||||
condCount++;
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// Note: Immunity flag was already set at the start to minimize race condition
|
||||
// If no conditions were added, the mob will block ALL damage (fail-safe)
|
||||
|
||||
lua_pushboolean(L, true);
|
||||
|
||||
return 1;
|
||||
}
|
||||
// MR-8: -- END OF -- Damage Immunity System - Lua Bindings
|
||||
|
||||
void RegisterNPCFunctionTable()
|
||||
{
|
||||
luaL_reg npc_functions[] =
|
||||
@@ -379,6 +588,15 @@ namespace quest
|
||||
{ "dec_remain_skill_book_count", npc_dec_remain_skill_book_count },
|
||||
{ "get_remain_hairdye_count", npc_get_remain_hairdye_count },
|
||||
{ "dec_remain_hairdye_count", npc_dec_remain_hairdye_count },
|
||||
|
||||
// MR-8: Damage Immunity System - Lua Bindings
|
||||
{ "set_damage_immunity", npc_set_damage_immunity },
|
||||
{ "is_damage_immune", npc_is_damage_immune },
|
||||
{ "add_damage_immunity_condition", npc_add_damage_immunity_condition },
|
||||
{ "clear_damage_immunity_conditions", npc_clear_damage_immunity_conditions },
|
||||
{ "set_damage_immunity_with_conditions", npc_set_damage_immunity_with_conditions },
|
||||
// MR-8: -- END OF -- Damage Immunity System - Lua Bindings
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -266,7 +266,8 @@ namespace quest
|
||||
int pc_in_dungeon(lua_State * L)
|
||||
{
|
||||
LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
|
||||
lua_pushboolean(L, ch->GetDungeon()?1:0);
|
||||
lua_pushboolean(L, ch->GetDungeon() ? 1 : 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1245,6 +1246,18 @@ namespace quest
|
||||
if (!lua_isnumber(L, 1))
|
||||
return 0;
|
||||
|
||||
LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
|
||||
|
||||
// MR-8: Prevent mounting in Nemere's Watchtower
|
||||
long lMapIndex = ch->GetMapIndex();
|
||||
|
||||
if (lMapIndex >= 352 * 10000 && lMapIndex < 353 * 10000)
|
||||
{
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("You cannot ride your horse in Nemere's Watchtower."));
|
||||
return 0;
|
||||
}
|
||||
// MR-8: -- END OF -- Prevent mounting in Nemere's Watchtower
|
||||
|
||||
int length = 60;
|
||||
|
||||
if (lua_isnumber(L, 2))
|
||||
@@ -1255,8 +1268,6 @@ namespace quest
|
||||
if (length < 0)
|
||||
length = 60;
|
||||
|
||||
LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
|
||||
|
||||
ch->RemoveAffect(AFFECT_MOUNT);
|
||||
ch->RemoveAffect(AFFECT_MOUNT_BONUS);
|
||||
|
||||
@@ -1310,11 +1321,16 @@ namespace quest
|
||||
|
||||
LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
|
||||
|
||||
if( NULL != ch )
|
||||
// MR-8: Prevent mount bonuses in Nemere's Watchtower
|
||||
long lMapIndex = ch->GetMapIndex();
|
||||
bool isInNemereDungeon = lMapIndex >= 352 * 10000 && lMapIndex < 353 * 10000;
|
||||
|
||||
if (NULL != ch && !isInNemereDungeon)
|
||||
{
|
||||
ch->RemoveAffect(AFFECT_MOUNT_BONUS);
|
||||
ch->AddAffect(AFFECT_MOUNT_BONUS, aApplyInfo[applyOn].bPointType, value, AFF_NONE, duration, 0, false);
|
||||
}
|
||||
// MR-8: -- END OF -- Prevent mount bonuses in Nemere's Watchtower
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1322,10 +1338,13 @@ namespace quest
|
||||
int pc_unmount(lua_State* L)
|
||||
{
|
||||
LPCHARACTER ch = CQuestManager::instance().GetCurrentCharacterPtr();
|
||||
|
||||
ch->RemoveAffect(AFFECT_MOUNT);
|
||||
ch->RemoveAffect(AFFECT_MOUNT_BONUS);
|
||||
|
||||
if (ch->IsHorseRiding())
|
||||
ch->StopRiding();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,7 +253,9 @@ bool is_regen_exception(long x, long y)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void regen_spawn_dungeon(LPREGEN regen, LPDUNGEON pDungeon, bool bOnce)
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
static void regen_spawn_dungeon(LPREGEN regen, LPDUNGEON pDungeon, bool bOnce, std::vector<DWORD>* pOutVids, const std::vector<CHARACTER::SDamageImmunityCondition>* pConditions)
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
{
|
||||
DWORD num;
|
||||
DWORD i;
|
||||
@@ -275,6 +277,20 @@ static void regen_spawn_dungeon(LPREGEN regen, LPDUNGEON pDungeon, bool bOnce)
|
||||
{
|
||||
++regen->count;
|
||||
ch->SetDungeon(pDungeon);
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// Apply damage immunity atomically before mob is attackable
|
||||
if (pConditions && !pConditions->empty())
|
||||
{
|
||||
ch->SetDamageImmunity(true);
|
||||
|
||||
for (const auto& cond : *pConditions)
|
||||
ch->AddDamageImmunityCondition(cond.bType, cond.dwValue, cond.strExtra);
|
||||
}
|
||||
|
||||
if (pOutVids)
|
||||
pOutVids->push_back(ch->GetVID());
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
}
|
||||
else if (regen->sx == regen->ex && regen->sy == regen->ey)
|
||||
@@ -291,6 +307,20 @@ static void regen_spawn_dungeon(LPREGEN regen, LPDUNGEON pDungeon, bool bOnce)
|
||||
{
|
||||
++regen->count;
|
||||
ch->SetDungeon(pDungeon);
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// Apply damage immunity atomically before mob is attackable
|
||||
if (pConditions && !pConditions->empty())
|
||||
{
|
||||
ch->SetDamageImmunity(true);
|
||||
|
||||
for (const auto& cond : *pConditions)
|
||||
ch->AddDamageImmunityCondition(cond.bType, cond.dwValue, cond.strExtra);
|
||||
}
|
||||
|
||||
if (pOutVids)
|
||||
pOutVids->push_back(ch->GetVID());
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -303,12 +333,53 @@ static void regen_spawn_dungeon(LPREGEN regen, LPDUNGEON pDungeon, bool bOnce)
|
||||
{
|
||||
++regen->count;
|
||||
ch->SetDungeon(pDungeon);
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// Apply damage immunity atomically before mob is attackable
|
||||
if (pConditions && !pConditions->empty())
|
||||
{
|
||||
ch->SetDamageImmunity(true);
|
||||
|
||||
for (const auto& cond : *pConditions)
|
||||
ch->AddDamageImmunityCondition(cond.bType, cond.dwValue, cond.strExtra);
|
||||
}
|
||||
|
||||
if (pOutVids)
|
||||
pOutVids->push_back(ch->GetVID());
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
}
|
||||
else if (regen->type == REGEN_TYPE_GROUP)
|
||||
{
|
||||
if (CHARACTER_MANAGER::Instance().SpawnGroup(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->ex, regen->ey, bOnce ? NULL : regen, regen->is_aggressive, pDungeon))
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
std::vector<DWORD> localVids;
|
||||
LPCHARACTER leader = CHARACTER_MANAGER::Instance().SpawnGroupWithVIDs(regen->vnum, regen->lMapIndex, regen->sx, regen->sy, regen->ex, regen->ey, bOnce ? NULL : regen, regen->is_aggressive, pDungeon, localVids);
|
||||
|
||||
if (leader)
|
||||
{
|
||||
++regen->count;
|
||||
|
||||
// Apply damage immunity to all spawned group members if requested.
|
||||
if (pConditions && !pConditions->empty())
|
||||
{
|
||||
for (DWORD vid : localVids)
|
||||
{
|
||||
LPCHARACTER member = CHARACTER_MANAGER::instance().Find(vid);
|
||||
|
||||
if (!member)
|
||||
continue;
|
||||
|
||||
member->SetDamageImmunity(true);
|
||||
|
||||
for (const auto& cond : *pConditions)
|
||||
member->AddDamageImmunityCondition(cond.bType, cond.dwValue, cond.strExtra);
|
||||
}
|
||||
}
|
||||
|
||||
if (pOutVids)
|
||||
pOutVids->insert(pOutVids->end(), localVids.begin(), localVids.end());
|
||||
}
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
}
|
||||
else if (regen->type == REGEN_TYPE_GROUP_GROUP)
|
||||
{
|
||||
@@ -403,11 +474,15 @@ EVENTFUNC(dungeon_regen_event)
|
||||
regen->event = NULL;
|
||||
}
|
||||
|
||||
regen_spawn_dungeon(regen, pDungeon, false);
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
regen_spawn_dungeon(regen, pDungeon, false, NULL, NULL);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
return PASSES_PER_SEC(regen->time);
|
||||
}
|
||||
|
||||
bool regen_do(const char* filename, long lMapIndex, int base_x, int base_y, LPDUNGEON pDungeon, bool bOnce)
|
||||
// MR-8:Snow dungeon - All-damage immunity with exceptions
|
||||
bool regen_do(const char* filename, long lMapIndex, int base_x, int base_y, LPDUNGEON pDungeon, bool bOnce, std::vector<DWORD>* pOutVids, const std::vector<CHARACTER::SDamageImmunityCondition>* pConditions)
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
{
|
||||
if (g_bNoRegen)
|
||||
return true;
|
||||
@@ -500,8 +575,10 @@ bool regen_do(const char* filename, long lMapIndex, int base_x, int base_y, LPDU
|
||||
// before the call to CHARACTER::SetRegen()
|
||||
}
|
||||
|
||||
// 처음엔 무조건 리젠 해준다.
|
||||
regen_spawn_dungeon(regen, pDungeon, bOnce);
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// 처음엔 무조건 리젠 해준다. Optionally collect VIDs for the initial spawn.
|
||||
regen_spawn_dungeon(regen, pDungeon, bOnce, pOutVids, pConditions);
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include "dungeon.h"
|
||||
#include <vector>
|
||||
#include "dungeon.h"
|
||||
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
// Forward declaration
|
||||
class CHARACTER;
|
||||
// MR-8: -- END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -84,7 +90,9 @@ EVENTINFO(dungeon_regen_event_info)
|
||||
};
|
||||
|
||||
extern bool regen_load(const char *filename, long lMapIndex, int base_x, int base_y);
|
||||
extern bool regen_do(const char* filename, long lMapIndex, int base_x, int base_y, LPDUNGEON pDungeon, bool bOnce = true );
|
||||
// MR-8: Snow dungeon - All-damage immunity with exceptions
|
||||
extern bool regen_do(const char* filename, long lMapIndex, int base_x, int base_y, LPDUNGEON pDungeon, bool bOnce = true, std::vector<DWORD>* pOutVids = NULL, const std::vector<CHARACTER::SDamageImmunityCondition>* pConditions = NULL);
|
||||
// MR-8: --END OF -- Snow dungeon - All-damage immunity with exceptions
|
||||
extern bool regen_load_in_file(const char* filename, long lMapIndex, int base_x, int base_y );
|
||||
extern void regen_free();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user