MR-8: Nemere Dungeon

This commit is contained in:
Mind Rapist
2026-01-18 09:01:56 +02:00
parent 94a2e35129
commit 681a8cae1b
24 changed files with 1317 additions and 90 deletions

View File

@@ -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 }
};