606 lines
14 KiB
C++
606 lines
14 KiB
C++
#include "stdafx.h"
|
|
#include "utils.h"
|
|
#include "config.h"
|
|
#include "questmanager.h"
|
|
#include "char.h"
|
|
#include "party.h"
|
|
#include "xmas_event.h"
|
|
#include "char_manager.h"
|
|
#include "shop_manager.h"
|
|
#include "guild.h"
|
|
#include "mob_manager.h"
|
|
|
|
namespace quest
|
|
{
|
|
//
|
|
// "npc" lua functions
|
|
//
|
|
int npc_open_shop(lua_State * L)
|
|
{
|
|
int iShopVnum = 0;
|
|
|
|
if (lua_gettop(L) == 1)
|
|
{
|
|
if (lua_isnumber(L, 1))
|
|
iShopVnum = (int) lua_tonumber(L, 1);
|
|
}
|
|
|
|
if (CQuestManager::instance().GetCurrentNPCCharacterPtr())
|
|
CShopManager::instance().StartShopping(CQuestManager::instance().GetCurrentCharacterPtr(), CQuestManager::instance().GetCurrentNPCCharacterPtr(), iShopVnum);
|
|
return 0;
|
|
}
|
|
|
|
int npc_is_pc(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
if (npc && npc->IsPC())
|
|
lua_pushboolean(L, 1);
|
|
else
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_empire(lua_State * L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
if (npc)
|
|
lua_pushnumber(L, npc->GetEmpire());
|
|
else
|
|
lua_pushnumber(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_race(lua_State * L)
|
|
{
|
|
lua_pushnumber(L, CQuestManager::instance().GetCurrentNPCRace());
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_guild(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
CGuild* pGuild = NULL;
|
|
if (npc)
|
|
pGuild = npc->GetGuild();
|
|
|
|
lua_pushnumber(L, pGuild ? pGuild->GetID() : 0);
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_remain_skill_book_count(lua_State* L)
|
|
{
|
|
LPCHARACTER npc = CQuestManager::instance().GetCurrentNPCCharacterPtr();
|
|
if (!npc || npc->IsPC() || npc->GetRaceNum() != xmas::MOB_SANTA_VNUM)
|
|
{
|
|
lua_pushnumber(L, 0);
|
|
return 1;
|
|
}
|
|
lua_pushnumber(L, MAX(0, npc->GetPoint(POINT_ATT_GRADE_BONUS)));
|
|
return 1;
|
|
}
|
|
|
|
int npc_dec_remain_skill_book_count(lua_State* L)
|
|
{
|
|
LPCHARACTER npc = CQuestManager::instance().GetCurrentNPCCharacterPtr();
|
|
if (!npc || npc->IsPC() || npc->GetRaceNum() != xmas::MOB_SANTA_VNUM)
|
|
{
|
|
return 0;
|
|
}
|
|
npc->SetPoint(POINT_ATT_GRADE_BONUS, MAX(0, npc->GetPoint(POINT_ATT_GRADE_BONUS)-1));
|
|
return 0;
|
|
}
|
|
|
|
int npc_get_remain_hairdye_count(lua_State* L)
|
|
{
|
|
LPCHARACTER npc = CQuestManager::instance().GetCurrentNPCCharacterPtr();
|
|
if (!npc || npc->IsPC() || npc->GetRaceNum() != xmas::MOB_SANTA_VNUM)
|
|
{
|
|
lua_pushnumber(L, 0);
|
|
return 1;
|
|
}
|
|
lua_pushnumber(L, MAX(0, npc->GetPoint(POINT_DEF_GRADE_BONUS)));
|
|
return 1;
|
|
}
|
|
|
|
int npc_dec_remain_hairdye_count(lua_State* L)
|
|
{
|
|
LPCHARACTER npc = CQuestManager::instance().GetCurrentNPCCharacterPtr();
|
|
if (!npc || npc->IsPC() || npc->GetRaceNum() != xmas::MOB_SANTA_VNUM)
|
|
{
|
|
return 0;
|
|
}
|
|
npc->SetPoint(POINT_DEF_GRADE_BONUS, MAX(0, npc->GetPoint(POINT_DEF_GRADE_BONUS)-1));
|
|
return 0;
|
|
}
|
|
|
|
int npc_is_quest(lua_State * L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
if (npc)
|
|
{
|
|
const std::string & r_st = q.GetCurrentQuestName();
|
|
|
|
if (q.GetQuestIndexByName(r_st) == npc->GetQuestBy())
|
|
{
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
int npc_kill(lua_State * L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = q.GetCurrentCharacterPtr();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
ch->SetQuestNPCID(0);
|
|
if (npc)
|
|
{
|
|
npc->Dead();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int npc_purge(lua_State * L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = q.GetCurrentCharacterPtr();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
ch->SetQuestNPCID(0);
|
|
if (npc)
|
|
{
|
|
M2_DESTROY_CHARACTER(npc);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int npc_is_near(lua_State * L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = q.GetCurrentCharacterPtr();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
lua_Number dist = 10;
|
|
|
|
if (lua_isnumber(L, 1))
|
|
dist = lua_tonumber(L, 1);
|
|
|
|
if (ch == NULL || npc == NULL)
|
|
{
|
|
lua_pushboolean(L, false);
|
|
}
|
|
else
|
|
{
|
|
lua_pushboolean(L, DISTANCE_APPROX(ch->GetX() - npc->GetX(), ch->GetY() - npc->GetY()) < dist*100);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_is_near_vid(lua_State* L)
|
|
{
|
|
if (!lua_isnumber(L, 1))
|
|
{
|
|
sys_err("invalid vid");
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().Find((DWORD)lua_tonumber(L, 1));
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
lua_Number dist = 10;
|
|
|
|
if (lua_isnumber(L, 2))
|
|
dist = lua_tonumber(L, 2);
|
|
|
|
if (ch == NULL || npc == NULL)
|
|
{
|
|
lua_pushboolean(L, false);
|
|
}
|
|
else
|
|
{
|
|
lua_pushboolean(L, DISTANCE_APPROX(ch->GetX() - npc->GetX(), ch->GetY() - npc->GetY()) < dist*100);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_unlock(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = q.GetCurrentCharacterPtr();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
if ( npc != NULL )
|
|
{
|
|
if (npc->IsPC())
|
|
return 0;
|
|
|
|
if (npc->GetQuestNPCID() == ch->GetPlayerID())
|
|
{
|
|
npc->SetQuestNPCID(0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int npc_lock(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER ch = q.GetCurrentCharacterPtr();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
if (!npc || npc->IsPC())
|
|
{
|
|
lua_pushboolean(L, TRUE);
|
|
return 1;
|
|
}
|
|
|
|
if (npc->GetQuestNPCID() == 0 || npc->GetQuestNPCID() == ch->GetPlayerID())
|
|
{
|
|
npc->SetQuestNPCID(ch->GetPlayerID());
|
|
lua_pushboolean(L, TRUE);
|
|
}
|
|
else
|
|
{
|
|
lua_pushboolean(L, FALSE);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_leader_vid(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
LPPARTY party = npc->GetParty();
|
|
LPCHARACTER leader = party->GetLeader();
|
|
|
|
if (leader)
|
|
lua_pushnumber(L, leader->GetVID());
|
|
else
|
|
lua_pushnumber(L, 0);
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_vid(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
LPCHARACTER npc = q.GetCurrentNPCCharacterPtr();
|
|
|
|
lua_pushnumber(L, npc->GetVID());
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_get_vid_attack_mul(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
|
|
lua_Number vid = lua_tonumber(L, 1);
|
|
LPCHARACTER targetChar = CHARACTER_MANAGER::instance().Find(vid);
|
|
|
|
if (targetChar)
|
|
lua_pushnumber(L, targetChar->GetAttMul());
|
|
else
|
|
lua_pushnumber(L, 0);
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_set_vid_attack_mul(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
|
|
lua_Number vid = lua_tonumber(L, 1);
|
|
lua_Number attack_mul = lua_tonumber(L, 2);
|
|
|
|
LPCHARACTER targetChar = CHARACTER_MANAGER::instance().Find(vid);
|
|
|
|
if (targetChar)
|
|
targetChar->SetAttMul(attack_mul);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int npc_get_vid_damage_mul(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
|
|
lua_Number vid = lua_tonumber(L, 1);
|
|
LPCHARACTER targetChar = CHARACTER_MANAGER::instance().Find(vid);
|
|
|
|
if (targetChar)
|
|
lua_pushnumber(L, targetChar->GetDamMul());
|
|
else
|
|
lua_pushnumber(L, 0);
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
int npc_set_vid_damage_mul(lua_State* L)
|
|
{
|
|
CQuestManager& q = CQuestManager::instance();
|
|
|
|
lua_Number vid = lua_tonumber(L, 1);
|
|
lua_Number damage_mul = lua_tonumber(L, 2);
|
|
|
|
LPCHARACTER targetChar = CHARACTER_MANAGER::instance().Find(vid);
|
|
|
|
if (targetChar)
|
|
targetChar->SetDamMul(damage_mul);
|
|
|
|
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[] =
|
|
{
|
|
{ "getrace", npc_get_race },
|
|
{ "get_race", npc_get_race },
|
|
{ "open_shop", npc_open_shop },
|
|
{ "get_empire", npc_get_empire },
|
|
{ "is_pc", npc_is_pc },
|
|
{ "is_quest", npc_is_quest },
|
|
{ "kill", npc_kill },
|
|
{ "purge", npc_purge },
|
|
{ "is_near", npc_is_near },
|
|
{ "is_near_vid", npc_is_near_vid },
|
|
{ "lock", npc_lock },
|
|
{ "unlock", npc_unlock },
|
|
{ "get_guild", npc_get_guild },
|
|
{ "get_leader_vid", npc_get_leader_vid },
|
|
{ "get_vid", npc_get_vid },
|
|
{ "get_vid_attack_mul", npc_get_vid_attack_mul },
|
|
{ "set_vid_attack_mul", npc_set_vid_attack_mul },
|
|
{ "get_vid_damage_mul", npc_get_vid_damage_mul },
|
|
{ "set_vid_damage_mul", npc_set_vid_damage_mul },
|
|
|
|
// X-mas santa special
|
|
{ "get_remain_skill_book_count", npc_get_remain_skill_book_count },
|
|
{ "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 }
|
|
};
|
|
|
|
CQuestManager::instance().AddLuaFunctionTable("npc", npc_functions);
|
|
}
|
|
};
|