mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-17 10:48:21 +00:00
Merge master in, with fixes
This commit is contained in:
@@ -88,7 +88,7 @@ jobs:
|
||||
- name: Configure
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 `
|
||||
cmake -S . -B build -G "Visual Studio 18 2026" -A x64 `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DEQEMU_BUILD_TESTS=ON `
|
||||
-DEQEMU_BUILD_LOGIN=ON `
|
||||
|
||||
@@ -5335,6 +5335,12 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
||||
if (ma_time) {
|
||||
switch (GetClass()) {
|
||||
case Class::Monk: {
|
||||
|
||||
if (!GetSkill(EQ::skills::SkillTigerClaw)) {
|
||||
monkattack_timer.Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1);
|
||||
|
||||
// Live AA - Technique of Master Wu
|
||||
|
||||
@@ -512,7 +512,7 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id)
|
||||
e.poison = b->GetBasePR();
|
||||
e.disease = b->GetBaseDR();
|
||||
e.corruption = b->GetBaseCorrup();
|
||||
e.expansion_bitmask = b->GetExpansionBitmask();
|
||||
e.expansion_bitmask = RuleI(Bots, BotExpansionSettings);
|
||||
|
||||
e = BotDataRepository::InsertOne(database, e);
|
||||
|
||||
|
||||
+39
-16
@@ -14361,9 +14361,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
sizeof(Merchant_Purchase_Struct), app->size);
|
||||
return;
|
||||
}
|
||||
RDTSC_Timer t1(true);
|
||||
|
||||
Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer;
|
||||
|
||||
Mob* vendor = entity_list.GetMob(mp->npcid);
|
||||
|
||||
if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != Class::Merchant)
|
||||
@@ -14373,35 +14372,51 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
if (DistanceSquared(m_Position, vendor->GetPosition()) > USE_NPC_RANGE2)
|
||||
return;
|
||||
|
||||
uint32 price = 0;
|
||||
uint32 itemid = GetItemIDAt(mp->itemslot);
|
||||
if (itemid == 0)
|
||||
return;
|
||||
|
||||
const EQ::ItemData* item = database.GetItem(itemid);
|
||||
EQ::ItemInstance* inst = GetInv().GetItem(mp->itemslot);
|
||||
if (!item || !inst) {
|
||||
Message(Chat::Red, "You seemed to have misplaced that item..");
|
||||
Message(Chat::Red, "You seem to have misplaced that item..");
|
||||
return;
|
||||
}
|
||||
if (mp->quantity > 1)
|
||||
{
|
||||
|
||||
if (!item->NoDrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mp->quantity > 1) {
|
||||
if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges()))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item->NoDrop) {
|
||||
//Message(Chat::Red,"%s tells you, 'LOL NOPE'", vendor->GetName());
|
||||
return;
|
||||
// Check for veto from script
|
||||
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_PRESELL)) {
|
||||
std::string export_string = fmt::format("{} {} {}", mp->itemslot, itemid, inst->GetItemType());
|
||||
std::vector<std::any> extra_pointers = { vendor, inst };
|
||||
|
||||
int result = parse->EventPlayer(EVENT_MERCHANT_PRESELL, this, export_string, 0, &extra_pointers);
|
||||
// CANCEL: If a script returns -1 for this event, the sale wil be cancelled. Sends a dummy packet sent to satisfy the client
|
||||
if (result == -1) {
|
||||
auto outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
|
||||
Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer;
|
||||
mco->npcid = vendor->GetID();
|
||||
mco->itemslot = -1; // Critical or the client will remove the item visually
|
||||
mco->quantity = 0;
|
||||
mco->price = 0;
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 cost_quantity = mp->quantity;
|
||||
if (inst->IsCharged())
|
||||
uint32 cost_quantity = 1;
|
||||
|
||||
uint32 i;
|
||||
uint32 cost_quantity = inst->IsCharged() ? 1 : mp->quantity;
|
||||
uint32 price = 0;
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
for (uint32 i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)(item->Price * i) * Client::CalcPriceMod(vendor, true);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
@@ -14419,7 +14434,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
for (uint32 i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
@@ -14431,6 +14446,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
|
||||
AddMoneyToPP(price);
|
||||
|
||||
// Update merchant stock and refresh client
|
||||
if (inst->IsStackable() || inst->IsCharged())
|
||||
{
|
||||
unsigned int i_quan = inst->GetCharges();
|
||||
@@ -14544,6 +14560,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
SendMoneyUpdate();
|
||||
|
||||
RDTSC_Timer t1(true);
|
||||
t1.start();
|
||||
Save(1);
|
||||
t1.stop();
|
||||
@@ -14665,6 +14683,11 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
if ((tabs_to_display & Parcel) == Parcel) {
|
||||
SendBulkParcels();
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_OPEN)) {
|
||||
std::vector<std::any> extra_pointers = { tmp };
|
||||
parse->EventPlayer(EVENT_MERCHANT_OPEN, this, "", 0, &extra_pointers);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -163,8 +163,10 @@ const char* QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_LANGUAGE_SKILL_UP",
|
||||
"EVENT_ALT_CURRENCY_MERCHANT_BUY",
|
||||
"EVENT_ALT_CURRENCY_MERCHANT_SELL",
|
||||
"EVENT_MERCHANT_OPEN",
|
||||
"EVENT_MERCHANT_BUY",
|
||||
"EVENT_MERCHANT_SELL",
|
||||
"EVENT_MERCHANT_PRESELL",
|
||||
"EVENT_INSPECT",
|
||||
"EVENT_TASK_BEFORE_UPDATE",
|
||||
"EVENT_AA_BUY",
|
||||
@@ -2289,6 +2291,33 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_MERCHANT_OPEN: {
|
||||
if (!extra_pointers || extra_pointers->size() < 1) break;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
if (!mob_ptr) break;
|
||||
|
||||
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_MERCHANT_PRESELL: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "slot_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "item_id", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "item_type", sep.arg[2]);
|
||||
|
||||
if (!extra_pointers || extra_pointers->size() < 2) break;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
|
||||
if (!mob_ptr || !inst_ptr) break;
|
||||
|
||||
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
|
||||
ExportVar(package_name.c_str(), "item", "ItemInstance", inst_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AA_BUY: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "aa_cost", sep.arg[0]);
|
||||
|
||||
@@ -116,8 +116,10 @@ enum QuestEventID {
|
||||
EVENT_LANGUAGE_SKILL_UP,
|
||||
EVENT_ALT_CURRENCY_MERCHANT_BUY,
|
||||
EVENT_ALT_CURRENCY_MERCHANT_SELL,
|
||||
EVENT_MERCHANT_OPEN,
|
||||
EVENT_MERCHANT_BUY,
|
||||
EVENT_MERCHANT_SELL,
|
||||
EVENT_MERCHANT_PRESELL,
|
||||
EVENT_INSPECT,
|
||||
EVENT_TASK_BEFORE_UPDATE,
|
||||
EVENT_AA_BUY,
|
||||
|
||||
@@ -6968,8 +6968,10 @@ luabind::scope lua_register_events() {
|
||||
luabind::value("language_skill_up", static_cast<int>(EVENT_LANGUAGE_SKILL_UP)),
|
||||
luabind::value("alt_currency_merchant_buy", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_BUY)),
|
||||
luabind::value("alt_currency_merchant_sell", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_SELL)),
|
||||
luabind::value("merchant_open", static_cast<int>(EVENT_MERCHANT_OPEN)),
|
||||
luabind::value("merchant_buy", static_cast<int>(EVENT_MERCHANT_BUY)),
|
||||
luabind::value("merchant_sell", static_cast<int>(EVENT_MERCHANT_SELL)),
|
||||
luabind::value("merchant_presell", static_cast<int>(EVENT_MERCHANT_PRESELL)),
|
||||
luabind::value("inspect", static_cast<int>(EVENT_INSPECT)),
|
||||
luabind::value("task_before_update", static_cast<int>(EVENT_TASK_BEFORE_UPDATE)),
|
||||
luabind::value("aa_buy", static_cast<int>(EVENT_AA_BUY)),
|
||||
|
||||
@@ -158,6 +158,11 @@ uint32 Lua_ItemInst::GetItemScriptID() {
|
||||
return self->GetItemScriptID();
|
||||
}
|
||||
|
||||
uint8 Lua_ItemInst::GetItemType() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetItemType();
|
||||
}
|
||||
|
||||
int Lua_ItemInst::GetCharges() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetCharges();
|
||||
@@ -497,6 +502,7 @@ luabind::scope lua_register_iteminst() {
|
||||
.def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID)
|
||||
.def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink)
|
||||
.def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID)
|
||||
.def("GetItemType", (uint8(Lua_ItemInst::*)(void)) & Lua_ItemInst::GetItemType)
|
||||
.def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl)
|
||||
.def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName)
|
||||
.def("GetSerialNumber", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetSerialNumber)
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
bool IsAmmo();
|
||||
uint32 GetID();
|
||||
uint32 GetItemScriptID();
|
||||
uint8 GetItemType();
|
||||
int GetCharges();
|
||||
void SetCharges(int charges);
|
||||
uint32 GetPrice();
|
||||
|
||||
@@ -160,8 +160,10 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_language_skill_up",
|
||||
"event_alt_currency_merchant_buy",
|
||||
"event_alt_currency_merchant_sell",
|
||||
"event_merchant_open",
|
||||
"event_merchant_buy",
|
||||
"event_merchant_sell",
|
||||
"event_merchant_presell",
|
||||
"event_inspect",
|
||||
"event_task_before_update",
|
||||
"event_aa_buy",
|
||||
@@ -335,8 +337,10 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_LANGUAGE_SKILL_UP] = handle_player_language_skill_up;
|
||||
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_BUY] = handle_player_alt_currency_merchant;
|
||||
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_SELL] = handle_player_alt_currency_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_OPEN] = handle_player_merchant_open;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_BUY] = handle_player_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_SELL] = handle_player_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_PRESELL] = handle_player_merchant_presell;
|
||||
PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect;
|
||||
PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy;
|
||||
PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain;
|
||||
|
||||
@@ -2340,6 +2340,53 @@ void handle_player_merchant(
|
||||
lua_setfield(L, -2, "item_cost");
|
||||
}
|
||||
|
||||
void handle_player_merchant_open(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
) {
|
||||
if (!extra_pointers || extra_pointers->size() < 1) return;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
if (!mob_ptr) return;
|
||||
|
||||
Lua_Mob l_mob(mob_ptr);
|
||||
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
|
||||
l_mob_o.push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
}
|
||||
|
||||
void handle_player_merchant_presell(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
) {
|
||||
Seperator sep(data.c_str());
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[0])); lua_setfield(L, -2, "slot_id");
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[1])); lua_setfield(L, -2, "item_id");
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[2])); lua_setfield(L, -2, "item_type");
|
||||
|
||||
if (!extra_pointers || extra_pointers->size() < 2) return;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
|
||||
if (!mob_ptr || !inst_ptr) return;
|
||||
|
||||
Lua_Mob l_mob(mob_ptr);
|
||||
luabind::adl::object(L, l_mob).push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
|
||||
Lua_ItemInst l_iteminst(inst_ptr);
|
||||
luabind::adl::object(L, l_iteminst).push(L);
|
||||
lua_setfield(L, -2, "item");
|
||||
}
|
||||
|
||||
void handle_player_augment_insert(
|
||||
QuestInterface *parse,
|
||||
lua_State* L,
|
||||
|
||||
@@ -671,6 +671,24 @@ void handle_player_merchant(
|
||||
std::vector<std::any> *extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_merchant_open(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_merchant_presell(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_inspect(
|
||||
QuestInterface *parse,
|
||||
lua_State* L,
|
||||
|
||||
+20
-32
@@ -711,48 +711,36 @@ void UpdateWindowTitle(char *iNewTitle)
|
||||
|
||||
bool CheckForCompatibleQuestPlugins()
|
||||
{
|
||||
const std::vector<std::pair<std::string, bool *>> directories = {
|
||||
{"lua_modules", nullptr},
|
||||
{"plugins", nullptr}
|
||||
};
|
||||
|
||||
bool lua_found = false;
|
||||
bool perl_found = false;
|
||||
|
||||
try {
|
||||
for (const auto &[directory, flag]: directories) {
|
||||
std::string dir_path = PathManager::Instance()->GetServerPath() + "/" + directory;
|
||||
if (!File::Exists(dir_path)) { continue; }
|
||||
|
||||
for (const auto &file: fs::directory_iterator(dir_path)) {
|
||||
auto check_dir = [&](const std::string& dir_path, bool& found) {
|
||||
if (!File::Exists(dir_path)) { return; }
|
||||
try {
|
||||
for (const auto& file : fs::directory_iterator(dir_path)) {
|
||||
if (!file.is_regular_file()) { continue; }
|
||||
|
||||
std::string file_path = file.path().string();
|
||||
if (!File::Exists(file_path)) { continue; }
|
||||
|
||||
auto r = File::GetContents(file_path);
|
||||
if (!Strings::Contains(r.contents, "CheckHandin")) { continue; }
|
||||
|
||||
if (directory == "lua_modules") {
|
||||
lua_found = true;
|
||||
auto r = File::GetContents(file.path().string());
|
||||
if (Strings::Contains(r.contents, "CheckHandin")) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
perl_found = true;
|
||||
}
|
||||
|
||||
if (lua_found && perl_found) { return true; }
|
||||
}
|
||||
}
|
||||
} catch (const fs::filesystem_error &ex) {
|
||||
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||
catch (const fs::filesystem_error& ex) {
|
||||
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& path : PathManager::Instance()->GetLuaModulePaths()) {
|
||||
check_dir(path, lua_found);
|
||||
}
|
||||
|
||||
if (!lua_found) {
|
||||
LogError("Failed to find CheckHandin in lua_modules");
|
||||
}
|
||||
if (!perl_found) {
|
||||
LogError("Failed to find CheckHandin in plugins");
|
||||
for (const auto& path : PathManager::Instance()->GetPluginPaths()) {
|
||||
check_dir(path, perl_found);
|
||||
}
|
||||
|
||||
if (!lua_found) { LogError("Failed to find CheckHandin in the Lua module quest directories"); }
|
||||
if (!perl_found) { LogError("Failed to find CheckHandin in the Perl plugins quest directories");}
|
||||
|
||||
return lua_found && perl_found;
|
||||
}
|
||||
|
||||
+24
-23
@@ -512,29 +512,6 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
|
||||
|
||||
bool found_skill = false;
|
||||
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
ca_atk->m_skill == EQ::skills::SkillKick &&
|
||||
can_use_kick
|
||||
) {
|
||||
if (GetTarget() != this) {
|
||||
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
|
||||
DoAnim(animKick, 0, false);
|
||||
|
||||
int hate_override = 0;
|
||||
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
|
||||
damage = -5;
|
||||
} else {
|
||||
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
|
||||
}
|
||||
|
||||
reuse_time = KickReuseTime - 1 - skill_reduction;
|
||||
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_id == Class::Monk) {
|
||||
reuse_time = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction;
|
||||
|
||||
@@ -596,6 +573,30 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
else {
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
ca_atk->m_skill == EQ::skills::SkillKick &&
|
||||
can_use_kick
|
||||
) {
|
||||
if (GetTarget() != this) {
|
||||
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
|
||||
DoAnim(animKick, 0, false);
|
||||
|
||||
int hate_override = 0;
|
||||
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
|
||||
damage = -5;
|
||||
} else {
|
||||
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
|
||||
}
|
||||
|
||||
reuse_time = KickReuseTime - 1 - skill_reduction;
|
||||
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
|
||||
Reference in New Issue
Block a user