mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-19 13:28:25 +00:00
Merge remote-tracking branch 'upstream/master' into spaupdate03
This commit is contained in:
+33
-6
@@ -5483,13 +5483,40 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
|
||||
CheckIncreaseSkill(EQ::skills::SkillDoubleAttack, target, -10);
|
||||
if (CheckDoubleAttack()) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
|
||||
// Modern AA description: Increases your chance of ... performing one additional hit with a 2-handed weapon when double attacking by 2%.
|
||||
|
||||
if (hand == EQ::invslot::slotPrimary) {
|
||||
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance +
|
||||
itembonuses.ExtraAttackChance;
|
||||
if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance))
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
|
||||
if (HasTwoHanderEquipped()) {
|
||||
auto extraattackchance = aabonuses.ExtraAttackChance[0] + spellbonuses.ExtraAttackChance[0] +
|
||||
itembonuses.ExtraAttackChance[0];
|
||||
if (extraattackchance && zone->random.Roll(extraattackchance)) {
|
||||
auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[1], spellbonuses.ExtraAttackChance[1], itembonuses.ExtraAttackChance[1] });
|
||||
for (int i = 0; i < extraattackamt; i++) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[0] + spellbonuses.ExtraAttackChancePrimary[0] +
|
||||
itembonuses.ExtraAttackChancePrimary[0];
|
||||
if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) {
|
||||
auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[1], spellbonuses.ExtraAttackChancePrimary[1], itembonuses.ExtraAttackChancePrimary[1] });
|
||||
for (int i = 0; i < extraattackamt_primary; i++) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hand == EQ::invslot::slotSecondary) {
|
||||
auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[0] + spellbonuses.ExtraAttackChanceSecondary[0] +
|
||||
itembonuses.ExtraAttackChanceSecondary[0];
|
||||
if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) {
|
||||
auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[1], spellbonuses.ExtraAttackChanceSecondary[1], itembonuses.ExtraAttackChanceSecondary[1] });
|
||||
for (int i = 0; i < extraattackamt_secondary; i++) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// you can only triple from the main hand
|
||||
|
||||
+122
-9
@@ -860,9 +860,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
case SE_MaxBindWound:
|
||||
newbon->MaxBindWound += base1;
|
||||
break;
|
||||
case SE_ExtraAttackChance:
|
||||
newbon->ExtraAttackChance += base1;
|
||||
break;
|
||||
case SE_SeeInvis:
|
||||
newbon->SeeInvis = base1;
|
||||
break;
|
||||
@@ -984,7 +981,6 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
case SE_BlockBehind:
|
||||
newbon->BlockBehind += base1;
|
||||
break;
|
||||
|
||||
case SE_StrikeThrough:
|
||||
case SE_StrikeThrough2:
|
||||
newbon->StrikeThrough += base1;
|
||||
@@ -1177,6 +1173,17 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Skill_Base_Damage_Mod: {
|
||||
// Bad data or unsupported new skill
|
||||
if (base2 > EQ::skills::HIGHEST_SKILL)
|
||||
break;
|
||||
if (base2 == ALL_SKILLS)
|
||||
newbon->DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] += base1;
|
||||
else
|
||||
newbon->DamageModifier3[base2] += base1;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SlayUndead: {
|
||||
if (newbon->SlayUndead[1] < base1)
|
||||
newbon->SlayUndead[0] = base1; // Rate
|
||||
@@ -1561,6 +1568,34 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
newbon->Pet_Add_Atk += base1;
|
||||
break;
|
||||
|
||||
case SE_ExtraAttackChance:
|
||||
{
|
||||
if (newbon->ExtraAttackChance[0] < base1) {
|
||||
newbon->ExtraAttackChance[0] = base1;
|
||||
newbon->ExtraAttackChance[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Primary:
|
||||
{
|
||||
if (newbon->ExtraAttackChancePrimary[0] < base1) {
|
||||
newbon->ExtraAttackChancePrimary[0] = base1;
|
||||
newbon->ExtraAttackChancePrimary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Secondary:
|
||||
{
|
||||
|
||||
if (newbon->ExtraAttackChanceSecondary[0] < base1) {
|
||||
newbon->ExtraAttackChanceSecondary[0] = base1;
|
||||
newbon->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// to do
|
||||
case SE_PetDiscipline:
|
||||
break;
|
||||
@@ -1715,7 +1750,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
|
||||
case SE_ChangeFrenzyRad:
|
||||
{
|
||||
// redundant to have level check here
|
||||
if (max != 0 && GetLevel() > max)
|
||||
break;
|
||||
|
||||
if(new_bonus->AggroRange == -1 || effect_value < new_bonus->AggroRange)
|
||||
{
|
||||
new_bonus->AggroRange = static_cast<float>(effect_value);
|
||||
@@ -1725,6 +1762,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
|
||||
case SE_Harmony:
|
||||
{
|
||||
if (max != 0 && GetLevel() > max)
|
||||
break;
|
||||
// Harmony effect as buff - kinda tricky
|
||||
// harmony could stack with a lull spell, which has better aggro range
|
||||
// take the one with less range in any case
|
||||
@@ -2323,6 +2362,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Skill_Base_Damage_Mod:
|
||||
{
|
||||
// Bad data or unsupported new skill
|
||||
if (base2 > EQ::skills::HIGHEST_SKILL)
|
||||
break;
|
||||
int skill = base2 == ALL_SKILLS ? EQ::skills::HIGHEST_SKILL + 1 : base2;
|
||||
if (effect_value < 0 && new_bonus->DamageModifier3[skill] > effect_value)
|
||||
new_bonus->DamageModifier3[skill] = effect_value;
|
||||
else if (effect_value > 0 && new_bonus->DamageModifier3[skill] < effect_value)
|
||||
new_bonus->DamageModifier3[skill] = effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MinDamageModifier:
|
||||
{
|
||||
// Bad data or unsupported new skill
|
||||
@@ -2358,8 +2410,45 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
}
|
||||
|
||||
case SE_ExtraAttackChance:
|
||||
new_bonus->ExtraAttackChance += effect_value;
|
||||
{
|
||||
if (AdditiveWornBonus) {
|
||||
new_bonus->ExtraAttackChance[0] += effect_value;
|
||||
new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1;
|
||||
}
|
||||
if (new_bonus->ExtraAttackChance[0] < effect_value) {
|
||||
new_bonus->ExtraAttackChance[0] = effect_value;
|
||||
new_bonus->ExtraAttackChance[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Primary:
|
||||
{
|
||||
if (AdditiveWornBonus) {
|
||||
new_bonus->ExtraAttackChancePrimary[0] += effect_value;
|
||||
new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
|
||||
if (new_bonus->ExtraAttackChancePrimary[0] < effect_value) {
|
||||
new_bonus->ExtraAttackChancePrimary[0] = effect_value;
|
||||
new_bonus->ExtraAttackChancePrimary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Secondary:
|
||||
{
|
||||
if (AdditiveWornBonus) {
|
||||
new_bonus->ExtraAttackChanceSecondary[0] += effect_value;
|
||||
new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
|
||||
if (new_bonus->ExtraAttackChanceSecondary[0] < effect_value) {
|
||||
new_bonus->ExtraAttackChanceSecondary[0] = effect_value;
|
||||
new_bonus->ExtraAttackChanceSecondary[1] = base2 ? base2 : 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_PercentXPIncrease:
|
||||
{
|
||||
@@ -4252,6 +4341,18 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Skill_Base_Damage_Mod:
|
||||
{
|
||||
for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++)
|
||||
{
|
||||
spellbonuses.DamageModifier3[e] = effect_value;
|
||||
aabonuses.DamageModifier3[e] = effect_value;
|
||||
itembonuses.DamageModifier3[e] = effect_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SE_MinDamageModifier:
|
||||
{
|
||||
for (int e = 0; e < EQ::skills::HIGHEST_SKILL + 1; e++)
|
||||
@@ -4276,9 +4377,21 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_ExtraAttackChance:
|
||||
spellbonuses.ExtraAttackChance = effect_value;
|
||||
aabonuses.ExtraAttackChance = effect_value;
|
||||
itembonuses.ExtraAttackChance = effect_value;
|
||||
spellbonuses.ExtraAttackChance[0] = effect_value;
|
||||
aabonuses.ExtraAttackChance[0] = effect_value;
|
||||
itembonuses.ExtraAttackChance[0] = effect_value;
|
||||
break;
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Primary:
|
||||
spellbonuses.ExtraAttackChancePrimary[0] = effect_value;
|
||||
aabonuses.ExtraAttackChancePrimary[0] = effect_value;
|
||||
itembonuses.ExtraAttackChancePrimary[0] = effect_value;
|
||||
break;
|
||||
|
||||
case SE_AddExtraAttackPct_1h_Secondary:
|
||||
spellbonuses.ExtraAttackChanceSecondary[0] = effect_value;
|
||||
aabonuses.ExtraAttackChanceSecondary[0] = effect_value;
|
||||
itembonuses.ExtraAttackChanceSecondary[0] = effect_value;
|
||||
break;
|
||||
|
||||
case SE_PercentXPIncrease:
|
||||
|
||||
+3
-1
@@ -3272,7 +3272,9 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
TEST_COMBATANTS();
|
||||
int32 ExtraAttackChanceBonus = (spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance);
|
||||
auto ExtraAttackChanceBonus =
|
||||
(spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] +
|
||||
aabonuses.ExtraAttackChance[0]);
|
||||
if (ExtraAttackChanceBonus) {
|
||||
|
||||
if (p_item && p_item->GetItem()->IsType2HWeapon()) {
|
||||
|
||||
+5
-1
@@ -10130,7 +10130,6 @@ void Client::SetAFK(uint8 afk_flag) {
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
|
||||
void Client::SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration) {
|
||||
uint32 zone_id = ZoneID(zone_short_name);
|
||||
std::string current_instance_type = str_tolower(instance_type);
|
||||
@@ -10241,3 +10240,8 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetGMStatus(int newStatus) {
|
||||
if (this->Admin() != newStatus)
|
||||
database.UpdateGMStatus(this->AccountID(), newStatus);
|
||||
}
|
||||
|
||||
@@ -1523,6 +1523,7 @@ public:
|
||||
void LoadAccountFlags();
|
||||
void SetAccountFlag(std::string flag, std::string val);
|
||||
std::string GetAccountFlag(std::string flag);
|
||||
void SetGMStatus(int newStatus);
|
||||
float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing);
|
||||
void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume);
|
||||
void PlayMP3(const char* fname);
|
||||
|
||||
@@ -12997,7 +12997,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
int16 charges = 0;
|
||||
if (item->Stackable || tmpmer_used)
|
||||
charges = mp->quantity;
|
||||
else if ( item->MaxCharges > 1)
|
||||
else if ( item->MaxCharges >= 1)
|
||||
charges = item->MaxCharges;
|
||||
|
||||
EQ::ItemInstance* inst = database.CreateItem(item, charges);
|
||||
|
||||
@@ -75,8 +75,6 @@
|
||||
#include "mob_movement_manager.h"
|
||||
#include "npc_scale_manager.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include "../common/http/httplib.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
|
||||
+4
-1
@@ -445,10 +445,13 @@ struct StatBonuses {
|
||||
int32 HitChanceEffect[EQ::skills::HIGHEST_SKILL + 2]; //Spell effect Chance to Hit, straight percent increase
|
||||
int32 DamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i
|
||||
int32 DamageModifier2[EQ::skills::HIGHEST_SKILL + 2]; //i
|
||||
int32 DamageModifier3[EQ::skills::HIGHEST_SKILL + 2]; //i
|
||||
int32 MinDamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i
|
||||
int32 ProcChance; // ProcChance/10 == % increase i = CombatEffects
|
||||
int32 ProcChanceSPA; // ProcChance from spell effects
|
||||
int32 ExtraAttackChance;
|
||||
int32 ExtraAttackChance[2]; // base chance(w/ 2H weapon)=0, amt of extra attacks=1
|
||||
int32 ExtraAttackChancePrimary[2]; // base chance=0, , amt of extra attacks=1
|
||||
int32 ExtraAttackChanceSecondary[2]; // base chance=0, , amt of extra attacks=1
|
||||
int32 DoTShielding;
|
||||
int32 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger)
|
||||
uint32 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt
|
||||
|
||||
+7
-1
@@ -2158,6 +2158,11 @@ void Lua_Client::RemoveItem(uint32 item_id, uint32 quantity) {
|
||||
return self->RemoveItem(item_id, quantity);
|
||||
}
|
||||
|
||||
void Lua_Client::SetGMStatus(uint32 newStatus) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetGMStatus(newStatus);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -2525,7 +2530,8 @@ luabind::scope lua_register_client() {
|
||||
.def("SendToInstance", (void(Lua_Client::*)(std::string,std::string,uint32,float,float,float,float,std::string,uint32))&Lua_Client::SendToInstance)
|
||||
.def("CountItem", (int(Lua_Client::*)(uint32))&Lua_Client::CountItem)
|
||||
.def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem)
|
||||
.def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem);
|
||||
.def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem)
|
||||
.def("SetGMStatus", (void(Lua_Client::*)(int32))& Lua_Client::SetGMStatus);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_inventory_where() {
|
||||
|
||||
@@ -370,6 +370,7 @@ public:
|
||||
int CountItem(uint32 item_id);
|
||||
void RemoveItem(uint32 item_id);
|
||||
void RemoveItem(uint32 item_id, uint32 quantity);
|
||||
void SetGMStatus(uint32 newStatus);
|
||||
|
||||
void SetPrimaryWeaponOrnamentation(uint32 model_id);
|
||||
void SetSecondaryWeaponOrnamentation(uint32 model_id);
|
||||
|
||||
+7
-1
@@ -474,6 +474,11 @@ int Lua_Spell::GetDamageShieldType() {
|
||||
return self->DamageShieldType;
|
||||
}
|
||||
|
||||
int Lua_Spell::GetRank() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->rank;
|
||||
}
|
||||
|
||||
luabind::scope lua_register_spell() {
|
||||
return luabind::class_<Lua_Spell>("Spell")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -561,7 +566,8 @@ luabind::scope lua_register_spell() {
|
||||
.def("MaxDist", &Lua_Spell::GetMaxDist)
|
||||
.def("MaxDistMod", &Lua_Spell::GetMaxDistMod)
|
||||
.def("MinRange", &Lua_Spell::GetMinRange)
|
||||
.def("DamageShieldType", &Lua_Spell::GetDamageShieldType);
|
||||
.def("DamageShieldType", &Lua_Spell::GetDamageShieldType)
|
||||
.def("Rank", &Lua_Spell::GetRank);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -107,6 +107,7 @@ public:
|
||||
float GetMaxDistMod();
|
||||
float GetMinRange();
|
||||
int GetDamageShieldType();
|
||||
int GetRank();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -572,7 +572,7 @@ int32 Lua_StatBonuses::GetProcChanceSPA() const {
|
||||
|
||||
int32 Lua_StatBonuses::GetExtraAttackChance() const {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->ExtraAttackChance;
|
||||
return self->ExtraAttackChance[0];
|
||||
}
|
||||
|
||||
int32 Lua_StatBonuses::GetDoTShielding() const {
|
||||
|
||||
+1
-1
@@ -1624,7 +1624,7 @@ void Merc::AI_Process() {
|
||||
}
|
||||
}
|
||||
|
||||
int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance;
|
||||
int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + aabonuses.ExtraAttackChance[0];
|
||||
|
||||
if (GetTarget() && ExtraAttackChanceBonus) {
|
||||
if(zone->random.Roll(ExtraAttackChanceBonus))
|
||||
|
||||
+3
-3
@@ -3708,9 +3708,6 @@ void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id)
|
||||
if(!IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
if (IsEffectInSpell(spell_id, SE_TwinCastBlocker))
|
||||
return;
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
int32 focus = CastToClient()->GetFocusEffect(focusTwincast, spell_id);
|
||||
@@ -4862,6 +4859,9 @@ int16 Mob::GetMeleeDamageMod_SE(uint16 skill)
|
||||
dmg_mod += itembonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier2[EQ::skills::HIGHEST_SKILL + 1] +
|
||||
itembonuses.DamageModifier2[skill] + spellbonuses.DamageModifier2[skill] + aabonuses.DamageModifier2[skill];
|
||||
|
||||
dmg_mod += itembonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier3[EQ::skills::HIGHEST_SKILL + 1] +
|
||||
itembonuses.DamageModifier3[skill] + spellbonuses.DamageModifier3[skill] + aabonuses.DamageModifier3[skill];
|
||||
|
||||
if(dmg_mod < -100)
|
||||
dmg_mod = -100;
|
||||
|
||||
|
||||
@@ -837,6 +837,7 @@ public:
|
||||
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
|
||||
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
|
||||
int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0);
|
||||
bool HarmonySpellLevelCheck(int32 spell_id, Mob* target = nullptr);
|
||||
|
||||
void CastSpellOnLand(Mob* caster, uint32 spell_id);
|
||||
void FocusProcLimitProcess();
|
||||
|
||||
@@ -3413,6 +3413,21 @@ XS(XS_Client_ReadBook) {
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_SetGMStatus); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SetGMStatus) {
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: Client::SetGMStatus(THIS, int newStatus)"); // @categories Script Utility
|
||||
{
|
||||
Client *THIS;
|
||||
int newStatus = (int)SvIV(ST(1));
|
||||
VALIDATE_THIS_IS_CLIENT;
|
||||
THIS->SetGMStatus(newStatus);
|
||||
THIS->UpdateAdmin(true);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_UpdateGroupAAs); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_UpdateGroupAAs) {
|
||||
dXSARGS;
|
||||
@@ -5706,6 +5721,7 @@ XS(boot_Client) {
|
||||
newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$");
|
||||
newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$");
|
||||
newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$");
|
||||
newXSproto(strcpy(buf, "SetGMStatus"), XS_Client_SetGMStatus, file, "$$");
|
||||
newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$");
|
||||
newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$");
|
||||
newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$;$");
|
||||
|
||||
+53
-18
@@ -929,11 +929,31 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::
|
||||
if (slot < 0)
|
||||
return false;
|
||||
|
||||
float speed_mod = speed;
|
||||
|
||||
float distance_mod = 0.0f;
|
||||
float distance = other->CalculateDistance(GetX(), GetY(), GetZ());
|
||||
float hit =
|
||||
1200.0f + (10 * distance / speed_mod); // Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4)
|
||||
|
||||
/*
|
||||
New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances.
|
||||
We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer.
|
||||
Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f
|
||||
For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though.
|
||||
*/
|
||||
|
||||
if (distance <= 125.0f) {
|
||||
if (speed != 4.0f) { //Standard functions will always be 4.0f for archery.
|
||||
distance_mod = (speed - 4.0f) * -20.0f;
|
||||
distance += distance * distance_mod / 100.0f;
|
||||
}
|
||||
}
|
||||
else if (distance > 125.0f && distance <= 200.0f)
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
|
||||
else if (distance > 200.0f) {
|
||||
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
}
|
||||
|
||||
float hit = 1200.0f + (10 * distance / speed);
|
||||
|
||||
ProjectileAtk[slot].increment = 1;
|
||||
ProjectileAtk[slot].hit_increment = static_cast<uint16>(hit); // This projected hit time if target does NOT MOVE
|
||||
@@ -951,14 +971,12 @@ bool Mob::TryProjectileAttack(Mob *other, const EQ::ItemData *item, EQ::skills::
|
||||
|
||||
ProjectileAtk[slot].ammo_slot = 0;
|
||||
ProjectileAtk[slot].skill = skillInUse;
|
||||
ProjectileAtk[slot].speed_mod = speed_mod;
|
||||
ProjectileAtk[slot].speed_mod = speed;
|
||||
|
||||
SetProjectileAttack(true);
|
||||
|
||||
if (item)
|
||||
SendItemAnimation(other, item, skillInUse, speed);
|
||||
// else if (IsNPC())
|
||||
// ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -978,23 +996,40 @@ void Mob::ProjectileAttack()
|
||||
disable = false;
|
||||
Mob *target = entity_list.GetMobID(ProjectileAtk[i].target_id);
|
||||
|
||||
if (target && target->IsMoving()) { // Only recalculate hit increment if target moving
|
||||
// Due to frequency that we need to check increment the targets position variables may not be
|
||||
// updated even if moving. Do a simple check before calculating distance.
|
||||
if (target && target->IsMoving()) {
|
||||
/*
|
||||
Only recalculate hit increment if target is moving.
|
||||
Due to frequency that we need to check increment the targets position variables may not be
|
||||
updated even if moving. Do a simple check before calculating distance.
|
||||
*/
|
||||
if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()) {
|
||||
|
||||
ProjectileAtk[i].tlast_x = target->GetX();
|
||||
ProjectileAtk[i].tlast_y = target->GetY();
|
||||
float distance = target->CalculateDistance(
|
||||
ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z);
|
||||
float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod); // Calcuation: 60 =
|
||||
// Animation Lag, 1.8 =
|
||||
// Speed modifier for speed
|
||||
// of (4)
|
||||
|
||||
//Recalculate from the original location the projectile was fired in relation to the current targets location.
|
||||
float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z);
|
||||
float distance_mod = 0.0f;
|
||||
|
||||
if (distance <= 125.0f) {
|
||||
distance_mod = (ProjectileAtk[i].speed_mod - 4.0f) * -20.0f;
|
||||
distance += distance * distance_mod / 100.0f;
|
||||
}
|
||||
else if (distance > 125.0f && distance <= 200.0f)
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
|
||||
else if (distance > 200.0f) {
|
||||
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
}
|
||||
|
||||
float hit = 1200.0f + (10 * distance / ProjectileAtk[i].speed_mod);
|
||||
|
||||
ProjectileAtk[i].hit_increment = static_cast<uint16>(hit);
|
||||
}
|
||||
}
|
||||
|
||||
// We hit I guess?
|
||||
// Check if we hit.
|
||||
if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment) {
|
||||
if (target) {
|
||||
if (IsNPC()) {
|
||||
@@ -1496,7 +1531,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
speed = 4.0;
|
||||
}
|
||||
if(!angle) {
|
||||
angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2;
|
||||
angle = CalculateHeadingToTarget(to->GetX(), to->GetY());
|
||||
}
|
||||
if(!tilt) {
|
||||
tilt = 125;
|
||||
|
||||
+60
-15
@@ -3226,6 +3226,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_Ff_ReuseTimeMax:
|
||||
case SE_Ff_Value_Min:
|
||||
case SE_Ff_Value_Max:
|
||||
case SE_AddExtraAttackPct_1h_Primary:
|
||||
case SE_AddExtraAttackPct_1h_Secondary:
|
||||
case SE_Skill_Base_Damage_Mod:
|
||||
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -4890,7 +4894,7 @@ int16 Client::CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_FcTwincast:
|
||||
if (type == focusTwincast)
|
||||
if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker))
|
||||
value = base1;
|
||||
break;
|
||||
|
||||
@@ -5531,7 +5535,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_FcTwincast:
|
||||
if (type == focusTwincast)
|
||||
if (type == focusTwincast && !IsEffectInSpell(spell_id, SE_TwinCastBlocker))
|
||||
value = focus_spell.base[i];
|
||||
break;
|
||||
|
||||
@@ -7144,7 +7148,7 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
|
||||
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed) {
|
||||
|
||||
/*For mage 'Bolt' line and other various spells.
|
||||
-This is mostly accurate for how the modern clients handle this effect.
|
||||
@@ -7169,7 +7173,7 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
|
||||
|
||||
//Make sure there is an avialable bolt to be cast.
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
if (ProjectileAtk[i].target_id == 0){
|
||||
if (ProjectileAtk[i].target_id == 0) {
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
@@ -7178,11 +7182,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
|
||||
if (slot < 0)
|
||||
return false;
|
||||
|
||||
float arc = 0.0f;
|
||||
float distance_mod = 0.0f;
|
||||
|
||||
if (CheckLosFN(spell_target)) {
|
||||
|
||||
float speed_mod = speed; //Constant for adjusting speeds to match calculated impact time.
|
||||
float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ());
|
||||
float hit = 1200.0f + (10 * distance / speed_mod);
|
||||
|
||||
/*
|
||||
New Distance Mod constant (7/25/21 update), modifier is needed to adjust slower speeds to have correct impact times at short distances.
|
||||
We use archery 4.0 speed as a baseline for the forumla. At speed 1.5 at 50 pct distance mod is needed, where as speed 4.0 there is no modifer.
|
||||
Therefore, we derive out our modifer as follows. distance_mod = (speed - 4) * ((50 - 0)/(1.5-4)). The ratio there is -20.0f. distance_mod = (speed - 4) * -20.0f
|
||||
For distances >125 we use different modifier, this was all meticulously tested by eye to get the best possible outcome for projectile impact times. Not perfect though.
|
||||
*/
|
||||
|
||||
if (distance <= 125.0f) {
|
||||
distance_mod = (speed - 4.0f) * -20.0f;
|
||||
distance += distance * distance_mod / 100.0f;
|
||||
}
|
||||
else if (distance > 125.0f && distance <= 200.0f)
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
|
||||
else if (distance > 200.0f) {
|
||||
arc = 50.0f - ((distance - 200.0f) * 0.266f); //Arc angle gets drastically larger if >200 distance, lets lower it down gradually for better effect.
|
||||
arc = std::max(arc, 20.0f); //No lower than 20 arc
|
||||
distance = distance * 1.30f; //Add 30% to base distance if over 200 range to tighten up hit timing.
|
||||
distance = 3.14f * (distance / 2.0f); //Get distance of arc to better reflect projectile path length
|
||||
}
|
||||
|
||||
float hit = 1200.0f + (10 * distance / speed);
|
||||
|
||||
ProjectileAtk[slot].increment = 1;
|
||||
ProjectileAtk[slot].hit_increment = static_cast<uint16>(hit); //This projected hit time if target does NOT MOVE
|
||||
@@ -7192,34 +7220,33 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){
|
||||
ProjectileAtk[slot].origin_y = GetY();
|
||||
ProjectileAtk[slot].origin_z = GetZ();
|
||||
ProjectileAtk[slot].skill = EQ::skills::SkillConjuration;
|
||||
ProjectileAtk[slot].speed_mod = speed_mod;
|
||||
ProjectileAtk[slot].speed_mod = speed;
|
||||
|
||||
SetProjectileAttack(true);
|
||||
}
|
||||
|
||||
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
|
||||
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
|
||||
ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1);
|
||||
ProjectileAnimation(spell_target, 0, false, speed, 0.0f, 0.0f, arc, spells[spell_id].player_1);
|
||||
}
|
||||
|
||||
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
|
||||
else {
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (IsClient()) {
|
||||
if (CastToClient()->ClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed);
|
||||
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_Titanium)), false, speed, 0.0f, 0.0f, arc);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed);
|
||||
}
|
||||
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_SOF)), false, speed, 0.0f, 0.0f, arc);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed);
|
||||
ProjectileAnimation(spell_target, (RuleI(Spells, FRProjectileItem_NPC)), false, speed, 0.0f, 0.0f, arc);
|
||||
}
|
||||
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, speed);
|
||||
ProjectileAnimation(spell_target, 0, 1, speed, 0.0f, 0.0f, arc);
|
||||
}
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
@@ -7564,3 +7591,21 @@ void Client::BreakFeignDeathWhenCastOn(bool IsResisted)
|
||||
MessageString(Chat::SpellFailure,FD_CAST_ON);
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::HarmonySpellLevelCheck(int32 spell_id, Mob *target)
|
||||
{
|
||||
//'this' = caster of spell
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
// not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either
|
||||
if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) {
|
||||
if ((spells[spell_id].max[i] != 0 && target->GetLevel() > spells[spell_id].max[i]) || target->GetSpecialAbility(IMMUNE_PACIFY)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+9
-11
@@ -224,17 +224,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(IsClient() && GetTarget() && IsHarmonySpell(spell_id))
|
||||
{
|
||||
for(int i = 0; i < EFFECT_COUNT; i++) {
|
||||
// not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either
|
||||
if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) {
|
||||
if((spells[spell_id].max[i] != 0 && GetTarget()->GetLevel() > spells[spell_id].max[i]) || GetTarget()->GetSpecialAbility(IMMUNE_PACIFY)) {
|
||||
InterruptSpell(CANNOT_AFFECT_NPC, 0x121, spell_id);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, entity_list.GetMobID(target_id))) {
|
||||
InterruptSpell(SPELL_NO_EFFECT, 0x121, spell_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasActiveSong() && IsBardSong(spell_id)) {
|
||||
@@ -3789,6 +3781,12 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//Need this to account for special AOE cases.
|
||||
if (IsClient() && IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, spelltar)) {
|
||||
MessageString(Chat::SpellFailure, SPELL_NO_EFFECT);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Block next spell effect should be used up first(since its blocking the next spell)
|
||||
if(CanBlockSpell()) {
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
|
||||
@@ -4977,3 +4977,21 @@ void ZoneDatabase::SetEXPModifier(uint32 character_id, uint32 zone_id, double ex
|
||||
);
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
|
||||
void ZoneDatabase::UpdateGMStatus(uint32 accID, int newStatus)
|
||||
{
|
||||
if (accID) {
|
||||
std::string query = fmt::format(
|
||||
SQL(
|
||||
UPDATE
|
||||
`account`
|
||||
SET `status` = {}
|
||||
WHERE
|
||||
`id` = {}
|
||||
),
|
||||
newStatus,
|
||||
accID
|
||||
);
|
||||
database.QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,9 @@ public:
|
||||
void DeleteBuyLines(uint32 CharID);
|
||||
void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity);
|
||||
|
||||
|
||||
void UpdateGMStatus(uint32 accID, int newStatus);
|
||||
|
||||
/**
|
||||
************************************************
|
||||
* Character
|
||||
|
||||
Reference in New Issue
Block a user