mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 13:16:39 +00:00
Merge remote-tracking branch 'upstream/master' into Warning_Cleanup
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
#define CANNOT_AFFECT_NPC 251 //That spell can not affect this target NPC.
|
||||
#define SUSPEND_MINION_HAS_AGGRO 256 //Your pet is the focus of something's attention.
|
||||
#define NO_PET 255 //You do not have a pet.
|
||||
#define GATE_FAIL 260 //Your gate is too unstable, and collapses.
|
||||
#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone.
|
||||
#define SPELL_NO_HOLD 263 //Your spell did not take hold.
|
||||
#define CANNOT_CHARM 267 //This NPC cannot be charmed.
|
||||
@@ -208,6 +209,7 @@
|
||||
#define AA_POINTS 1215 //points
|
||||
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles!
|
||||
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close!
|
||||
#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under.
|
||||
#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire.
|
||||
#define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name.
|
||||
#define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death.
|
||||
@@ -253,12 +255,14 @@
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
#define SUCCOR_FAIL 5169 //The portal collapes before you can escape!
|
||||
#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.'
|
||||
#define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!!
|
||||
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
|
||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
||||
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
|
||||
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
||||
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
|
||||
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
|
||||
@@ -330,6 +334,7 @@
|
||||
#define ALREADY_CASTING 12442 //You are already casting a spell!
|
||||
#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name.
|
||||
#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses.
|
||||
#define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1!
|
||||
#define NOT_HOLDING_ITEM 12452 //You are not holding an item!
|
||||
#define SENSE_UNDEAD 12471 //You sense undead in this direction.
|
||||
#define SENSE_ANIMAL 12472 //You sense an animal in this direction.
|
||||
|
||||
+12
-1
@@ -1371,6 +1371,9 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1983,6 +1986,9 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden)
|
||||
{
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
@@ -4079,6 +4085,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
||||
}
|
||||
}
|
||||
}
|
||||
//If OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not.
|
||||
//This is for some servers that may want to have as many procs triggering from weapons as possible in a single round.
|
||||
if(!RuleB(Combat, OneProcPerWeapon))
|
||||
proced = false;
|
||||
|
||||
if (!proced && inst) {
|
||||
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
@@ -4103,7 +4113,8 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
||||
}
|
||||
} else {
|
||||
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
||||
break;
|
||||
if (RuleB(Combat, OneProcPerWeapon))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-5
@@ -1358,16 +1358,27 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_AttackSpeed2:
|
||||
{
|
||||
if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap
|
||||
if (newbon->hastetype2 < 0) break; //Slowed - Don't apply haste2
|
||||
if ((effect_value - 100) > newbon->hastetype2) {
|
||||
newbon->hastetype2 = effect_value - 100;
|
||||
}
|
||||
}
|
||||
else if ((effect_value - 100) < 0) { // Slow
|
||||
int real_slow_value = (100 - effect_value) * -1;
|
||||
if (real_slow_value < newbon->hastetype2)
|
||||
newbon->hastetype2 = real_slow_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AttackSpeed3:
|
||||
{
|
||||
if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
|
||||
if (effect_value < 0){ //Slow
|
||||
if (effect_value < newbon->hastetype3)
|
||||
newbon->hastetype3 = effect_value;
|
||||
}
|
||||
|
||||
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
|
||||
if (effect_value > newbon->hastetype3) {
|
||||
newbon->hastetype3 = effect_value;
|
||||
}
|
||||
@@ -1377,18 +1388,24 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_AttackSpeed4:
|
||||
{
|
||||
if (effect_value > 0) {
|
||||
if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow)
|
||||
effect_value = effect_value * -1;
|
||||
|
||||
if (effect_value > 0 && effect_value > newbon->inhibitmelee) {
|
||||
|
||||
if (slow_mitigation){
|
||||
int new_effect_value = SlowMitigation(false,caster,effect_value);
|
||||
if (new_effect_value > newbon->inhibitmelee) {
|
||||
newbon->inhibitmelee = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
newbon->inhibitmelee = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
}
|
||||
}
|
||||
|
||||
else if (effect_value > newbon->inhibitmelee) {
|
||||
newbon->inhibitmelee = effect_value;
|
||||
newbon->inhibitmelee = effect_value;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2568,6 +2585,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
newbon->AbsorbMagicAtt[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_NegateIfCombat:
|
||||
newbon->NegateIfCombat = true;
|
||||
break;
|
||||
|
||||
case SE_Screech:
|
||||
newbon->Screech = effect_value;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3237,6 +3237,9 @@ void Bot::BotRangedAttack(Mob* other) {
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -5281,6 +5284,28 @@ uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
|
||||
// This essentially performs a '#bot update,' with appearance packets, based on the current methods.
|
||||
// This should not be called outside of Client::SetEXP() due to it's lack of rule checks.
|
||||
if(client) {
|
||||
std::list<Bot*> blist = entity_list.GetBotsByBotOwnerCharacterID(client->CharacterID());
|
||||
|
||||
for(std::list<Bot*>::iterator biter = blist.begin(); biter != blist.end(); ++biter) {
|
||||
Bot* bot = *biter;
|
||||
if(bot && (bot->GetLevel() != client->GetLevel())) {
|
||||
bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
|
||||
bot->CalcBotStats(false);
|
||||
if(sendlvlapp)
|
||||
bot->SendLevelAppearance();
|
||||
// modified from Client::SetLevel()
|
||||
bot->SendAppearancePacket(AT_WhoLevel, level, true, true); // who level change
|
||||
}
|
||||
}
|
||||
|
||||
blist.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Bot::ClassIdToString(uint16 classId) {
|
||||
std::string Result;
|
||||
|
||||
@@ -6640,6 +6665,9 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(GetTarget())
|
||||
TriggerDefensiveProcs(weapon, other, Hand, damage);
|
||||
|
||||
|
||||
@@ -352,6 +352,7 @@ public:
|
||||
static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static uint32 AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static uint32 GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage);
|
||||
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
|
||||
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static std::string ClassIdToString(uint16 classId);
|
||||
static std::string RaceIdToString(uint16 raceId);
|
||||
|
||||
@@ -2306,6 +2306,8 @@ uint64 Client::GetAllMoney() {
|
||||
}
|
||||
|
||||
bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi) {
|
||||
if (IsDead() || IsUnconscious())
|
||||
return false;
|
||||
if (IsAIControlled()) // no skillups while chamred =p
|
||||
return false;
|
||||
if (skillid > HIGHEST_SKILL)
|
||||
@@ -2349,6 +2351,10 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha
|
||||
}
|
||||
|
||||
void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) {
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
if (langid >= MAX_PP_LANGUAGE)
|
||||
return; // do nothing if langid is an invalid language
|
||||
|
||||
|
||||
@@ -305,6 +305,7 @@ public:
|
||||
void SetHideMe(bool hm);
|
||||
inline uint16 GetPort() const { return port; }
|
||||
bool IsDead() const { return(dead); }
|
||||
bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); }
|
||||
inline bool IsLFP() { return LFP; }
|
||||
void UpdateLFP();
|
||||
|
||||
|
||||
@@ -5421,6 +5421,12 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
action = 0;
|
||||
}
|
||||
|
||||
// 1199 I don't have time for that now. etc
|
||||
if (!tmp->CastToNPC()->IsMerchantOpen()) {
|
||||
tmp->Say_StringID(MakeRandomInt(1199, 1202));
|
||||
action = 0;
|
||||
}
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
|
||||
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
|
||||
|
||||
|
||||
@@ -570,6 +570,9 @@ bool Client::Process() {
|
||||
viral_timer_counter = 0;
|
||||
}
|
||||
|
||||
if(projectile_timer.Check())
|
||||
SpellProjectileEffect();
|
||||
|
||||
if(spellbonuses.GravityEffect == 1) {
|
||||
if(gravity_timer.Check())
|
||||
DoGravityEffect();
|
||||
|
||||
+52
-9
@@ -449,7 +449,11 @@ int command_init(void) {
|
||||
command_add("questerrors", "Shows quest errors.", 100, command_questerrors) ||
|
||||
command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) ||
|
||||
command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) ||
|
||||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache)
|
||||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) ||
|
||||
command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) ||
|
||||
command_add("open_shop", nullptr, 100, command_merchantopenshop) ||
|
||||
command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) ||
|
||||
command_add("close_shop", nullptr, 100, command_merchantcloseshop)
|
||||
)
|
||||
{
|
||||
command_deinit();
|
||||
@@ -2638,19 +2642,35 @@ void command_makepet(Client *c, const Seperator *sep)
|
||||
void command_level(Client *c, const Seperator *sep)
|
||||
{
|
||||
uint16 level = atoi(sep->arg[1]);
|
||||
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap)) )
|
||||
|
||||
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) {
|
||||
c->Message(0, "Error: #Level: Invalid Level");
|
||||
else if (c->Admin() < 100)
|
||||
}
|
||||
else if (c->Admin() < 100) {
|
||||
c->SetLevel(level, true);
|
||||
else if (!c->GetTarget())
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
Bot::LevelBotWithClient(c, level, true);
|
||||
#endif
|
||||
}
|
||||
else if (!c->GetTarget()) {
|
||||
c->Message(0, "Error: #Level: No target");
|
||||
else
|
||||
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel))))
|
||||
}
|
||||
else {
|
||||
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) {
|
||||
c->Message(0, "Error: #Level: Invalid Level");
|
||||
else
|
||||
}
|
||||
else {
|
||||
c->GetTarget()->SetLevel(level, true);
|
||||
if(c->GetTarget() && c->GetTarget()->IsClient())
|
||||
c->GetTarget()->CastToClient()->SendLevelAppearance();
|
||||
if(c->GetTarget()->IsClient()) {
|
||||
c->GetTarget()->CastToClient()->SendLevelAppearance();
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void command_spawn(Client *c, const Seperator *sep)
|
||||
@@ -11481,3 +11501,26 @@ void command_npctype_cache(Client *c, const Seperator *sep)
|
||||
c->Message(0, "#npctype_cache all");
|
||||
}
|
||||
}
|
||||
|
||||
void command_merchantopenshop(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *merchant = c->GetTarget();
|
||||
if (!merchant || merchant->GetClass() != MERCHANT) {
|
||||
c->Message(0, "You must target a merchant to open their shop.");
|
||||
return;
|
||||
}
|
||||
|
||||
merchant->CastToNPC()->MerchantOpenShop();
|
||||
}
|
||||
|
||||
void command_merchantcloseshop(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *merchant = c->GetTarget();
|
||||
if (!merchant || merchant->GetClass() != MERCHANT) {
|
||||
c->Message(0, "You must target a merchant to close their shop.");
|
||||
return;
|
||||
}
|
||||
|
||||
merchant->CastToNPC()->MerchantCloseShop();
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,8 @@ void command_enablerecipe(Client *c, const Seperator *sep);
|
||||
void command_disablerecipe(Client *c, const Seperator *sep);
|
||||
void command_showspellslist(Client *c, const Seperator *sep);
|
||||
void command_npctype_cache(Client *c, const Seperator *sep);
|
||||
void command_merchantopenshop(Client *c, const Seperator *sep);
|
||||
void command_merchantcloseshop(Client *c, const Seperator *sep);
|
||||
|
||||
#ifdef EQPROFILE
|
||||
void command_profiledump(Client *c, const Seperator *sep);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../common/spdat.h"
|
||||
|
||||
#define HIGHEST_RESIST 9 //Max resist type value
|
||||
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
||||
|
||||
/* solar: macros for IsAttackAllowed, IsBeneficialAllowed */
|
||||
#define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC())
|
||||
@@ -344,6 +345,8 @@ struct StatBonuses {
|
||||
int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana.
|
||||
uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot
|
||||
uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot
|
||||
bool NegateIfCombat; // Bool Drop buff if cast or melee
|
||||
int8 Screech; // -1 = Will be blocked if another Screech is +(1)
|
||||
|
||||
// AAs
|
||||
int8 Packrat; //weight reduction for items, 1 point = 10%
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
|
||||
if (chance > 0){
|
||||
if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
|
||||
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
|
||||
@@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
|
||||
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) {
|
||||
ratio = MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
|
||||
ratio += MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
|
||||
Critical = true;
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -3415,9 +3415,14 @@ void EntityList::ReloadAllClientsTaskState(int TaskID)
|
||||
|
||||
bool EntityList::IsMobInZone(Mob *who)
|
||||
{
|
||||
auto it = mob_list.find(who->GetID());
|
||||
if (it != mob_list.end())
|
||||
return who == it->second;
|
||||
//We don't use mob_list.find(who) because this code needs to be able to handle dangling pointers for the quest code.
|
||||
auto it = mob_list.begin();
|
||||
while(it != mob_list.end()) {
|
||||
if(it->second == who) {
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -326,7 +326,18 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
|
||||
}
|
||||
else
|
||||
Message(15, "Welcome to level %i!", check_level);
|
||||
|
||||
#ifdef BOTS
|
||||
uint8 myoldlevel = GetLevel();
|
||||
#endif
|
||||
|
||||
SetLevel(check_level);
|
||||
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
// hack way of doing this..but, least invasive... (same criteria as gain level for sendlvlapp)
|
||||
Bot::LevelBotWithClient(this, GetLevel(), (myoldlevel==check_level-1));
|
||||
#endif
|
||||
}
|
||||
|
||||
//If were at max level then stop gaining experience if we make it to the cap
|
||||
|
||||
+18
-9
@@ -209,7 +209,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
if(item == nullptr) {
|
||||
Message(13, "Item %u does not exist.", item_id);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
GetName(), account_name, item_id, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -228,14 +228,20 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This code is ready to implement once the item load code is changed to process the 'minstatus' field.
|
||||
// Checking #iteminfo in-game verfies that item->MinStatus is set to '0' regardless of field value.
|
||||
// An optional sql script will also need to be added, once this goes live, to allow changing of the min status.
|
||||
|
||||
// check to make sure we are a GM if the item is GM-only
|
||||
/*
|
||||
else if(item->gm && (this->Admin() < 100))
|
||||
Message(13, "You are not a GM and can not summon this item.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
else if(item->MinStatus && ((this->Admin() < item->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) {
|
||||
Message(13, "You are not a GM or do not have the status to summon this item.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n",
|
||||
GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus);
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 };
|
||||
@@ -276,12 +282,15 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Same as GM check above
|
||||
|
||||
// check to make sure we are a GM if the augment is GM-only
|
||||
/*
|
||||
else if(augtest->gm && (this->Admin() < 100)) {
|
||||
Message(13, "You are not a GM and can not summon this augment.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
else if(augtest->MinStatus && ((this->Admin() < augtest->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) {
|
||||
Message(13, "You are not a GM or do not have the status to summon this augment.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n",
|
||||
GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+13
-1
@@ -432,6 +432,16 @@ int Lua_NPC::GetScore() {
|
||||
return self->GetScore();
|
||||
}
|
||||
|
||||
void Lua_NPC::MerchantOpenShop() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->MerchantOpenShop();
|
||||
}
|
||||
|
||||
void Lua_NPC::MerchantCloseShop() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->MerchantCloseShop();
|
||||
}
|
||||
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
@@ -520,7 +530,9 @@ luabind::scope lua_register_npc() {
|
||||
.def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed)
|
||||
.def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating)
|
||||
.def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount)
|
||||
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore);
|
||||
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore)
|
||||
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
|
||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -112,7 +112,9 @@ public:
|
||||
int GetAccuracyRating();
|
||||
int GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void MerchantOpenShop();
|
||||
void MerchantCloseShop();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+80
-16
@@ -276,6 +276,14 @@ Mob::Mob(const char* in_name,
|
||||
casting_spell_inventory_slot = 0;
|
||||
target = 0;
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; }
|
||||
projectile_timer.Disable();
|
||||
|
||||
memset(&itembonuses, 0, sizeof(StatBonuses));
|
||||
memset(&spellbonuses, 0, sizeof(StatBonuses));
|
||||
memset(&aabonuses, 0, sizeof(StatBonuses));
|
||||
@@ -2080,27 +2088,35 @@ void Mob::SetAttackTimer() {
|
||||
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDualWield(void) const
|
||||
{
|
||||
if (!IsClient()) {
|
||||
bool Mob::CanThisClassDualWield(void) const {
|
||||
if(!IsClient()) {
|
||||
return(GetSkill(SkillDualWield) > 0);
|
||||
} else {
|
||||
const ItemInst* inst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
||||
}
|
||||
else if(CastToClient()->HasSkill(SkillDualWield)) {
|
||||
const ItemInst* pinst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
||||
const ItemInst* sinst = CastToClient()->GetInv().GetItem(SLOT_SECONDARY);
|
||||
|
||||
// 2HS, 2HB, or 2HP
|
||||
if (inst && inst->IsType(ItemClassCommon)) {
|
||||
const Item_Struct* item = inst->GetItem();
|
||||
if ((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
||||
if(pinst && pinst->IsWeapon()) {
|
||||
const Item_Struct* item = pinst->GetItem();
|
||||
|
||||
if((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
||||
return false;
|
||||
} else {
|
||||
//No weapon in hand... using hand-to-hand...
|
||||
//only monks and beastlords? can dual wield their fists.
|
||||
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (CastToClient()->HasSkill(SkillDualWield)); // No skill = no chance
|
||||
// OffHand Weapon
|
||||
if(sinst && !sinst->IsWeapon())
|
||||
return false;
|
||||
|
||||
// Dual-Wielding Empty Fists
|
||||
if(!pinst && !sinst)
|
||||
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDoubleAttack(void) const
|
||||
@@ -2450,13 +2466,18 @@ bool Mob::RemoveFromHateList(Mob* mob)
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
void Mob::WipeHateList()
|
||||
{
|
||||
if(IsEngaged())
|
||||
{
|
||||
hate_list.Wipe();
|
||||
AI_Event_NoLongerEngaged();
|
||||
}
|
||||
hate_list.Wipe();
|
||||
else
|
||||
{
|
||||
hate_list.Wipe();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Mob::RandomTimer(int min,int max) {
|
||||
@@ -4353,6 +4374,49 @@ bool Mob::TryReflectSpell(uint32 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mob::SpellProjectileEffect()
|
||||
{
|
||||
bool time_disable = false;
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
|
||||
if (projectile_increment[i] == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
Mob* target = entity_list.GetMobID(projectile_target_id[i]);
|
||||
|
||||
float dist = 0;
|
||||
|
||||
if (target)
|
||||
dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]);
|
||||
|
||||
int increment_end = 0;
|
||||
increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms
|
||||
|
||||
if (increment_end <= projectile_increment[i]){
|
||||
|
||||
if (target && IsValidSpell(projectile_spell_id[i]))
|
||||
SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true);
|
||||
|
||||
projectile_spell_id[i] = 0;
|
||||
projectile_target_id[i] = 0;
|
||||
projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0;
|
||||
projectile_increment[i] = 0;
|
||||
time_disable = true;
|
||||
}
|
||||
|
||||
else {
|
||||
projectile_increment[i]++;
|
||||
time_disable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_disable)
|
||||
projectile_timer.Disable();
|
||||
}
|
||||
|
||||
|
||||
void Mob::DoGravityEffect()
|
||||
{
|
||||
Mob *caster = nullptr;
|
||||
|
||||
+11
@@ -192,6 +192,7 @@ public:
|
||||
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
||||
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
||||
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
|
||||
int ResistPhysical(int level_diff, uint8 caster_level);
|
||||
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
|
||||
void SendSpellBarDisable();
|
||||
void SendSpellBarEnable(uint16 spellid);
|
||||
@@ -222,6 +223,8 @@ public:
|
||||
uint16 CastingSpellID() const { return casting_spell_id; }
|
||||
bool DoCastingChecks();
|
||||
bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier);
|
||||
void SpellProjectileEffect();
|
||||
bool TrySpellProjectile(Mob* spell_target, uint16 spell_id);
|
||||
|
||||
//Buff
|
||||
void BuffProcess();
|
||||
@@ -337,6 +340,7 @@ public:
|
||||
inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; }
|
||||
inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; }
|
||||
inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; }
|
||||
inline virtual int16 GetPhR() const { return PhR; }
|
||||
inline StatBonuses GetItemBonuses() const { return itembonuses; }
|
||||
inline StatBonuses GetSpellBonuses() const { return spellbonuses; }
|
||||
inline StatBonuses GetAABonuses() const { return aabonuses; }
|
||||
@@ -915,6 +919,7 @@ protected:
|
||||
int16 DR;
|
||||
int16 PR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
bool moving;
|
||||
int targeted;
|
||||
bool findable;
|
||||
@@ -1040,6 +1045,12 @@ protected:
|
||||
uint8 bardsong_slot;
|
||||
uint32 bardsong_target_id;
|
||||
|
||||
Timer projectile_timer;
|
||||
uint32 projectile_spell_id[MAX_SPELL_PROJECTILE];
|
||||
uint16 projectile_target_id[MAX_SPELL_PROJECTILE];
|
||||
uint8 projectile_increment[MAX_SPELL_PROJECTILE];
|
||||
float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE];
|
||||
|
||||
float rewind_x;
|
||||
float rewind_y;
|
||||
float rewind_z;
|
||||
|
||||
+1
-1
@@ -447,7 +447,7 @@ int main(int argc, char** argv) {
|
||||
if (InterserverTimer.Check()) {
|
||||
InterserverTimer.Start();
|
||||
database.ping();
|
||||
AsyncLoadVariables(dbasync, &database);
|
||||
// AsyncLoadVariables(dbasync, &database);
|
||||
entity_list.UpdateWho();
|
||||
if (worldserver.TryReconnect() && (!worldserver.Connected()))
|
||||
worldserver.AsyncConnect();
|
||||
|
||||
@@ -158,6 +158,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
FR = d->FR;
|
||||
PR = d->PR;
|
||||
Corrup = d->Corrup;
|
||||
PhR = d->PhR;
|
||||
|
||||
STR = d->STR;
|
||||
STA = d->STA;
|
||||
@@ -199,6 +200,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
SetMana(GetMaxMana());
|
||||
|
||||
MerchantType = d->merchanttype;
|
||||
merchant_open = GetClass() == MERCHANT;
|
||||
adventure_template_id = d->adventure_template;
|
||||
org_x = x;
|
||||
org_y = y;
|
||||
@@ -658,6 +660,9 @@ bool NPC::Process()
|
||||
viral_timer_counter = 0;
|
||||
}
|
||||
|
||||
if(projectile_timer.Check())
|
||||
SpellProjectileEffect();
|
||||
|
||||
if(spellbonuses.GravityEffect == 1) {
|
||||
if(gravity_timer.Check())
|
||||
DoGravityEffect();
|
||||
@@ -2058,6 +2063,8 @@ void NPC::CalcNPCResists() {
|
||||
PR = (GetLevel() * 11)/10;
|
||||
if (!Corrup)
|
||||
Corrup = 15;
|
||||
if (!PhR)
|
||||
PhR = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,10 @@ public:
|
||||
void SetSecSkill(uint8 skill_type) { sec_melee_type = skill_type; }
|
||||
|
||||
uint32 MerchantType;
|
||||
bool merchant_open;
|
||||
inline void MerchantOpenShop() { merchant_open = true; }
|
||||
inline void MerchantCloseShop() { merchant_open = false; }
|
||||
inline bool IsMerchantOpen() { return merchant_open; }
|
||||
void Depop(bool StartSpawnTimer = false);
|
||||
void Stun(int duration);
|
||||
void UnStun();
|
||||
|
||||
@@ -839,6 +839,9 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1085,6 +1088,9 @@ void NPC::RangedAttack(Mob* other)
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1227,6 +1233,9 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
|
||||
+131
-27
@@ -326,7 +326,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
if(inuse)
|
||||
break;
|
||||
|
||||
Heal();
|
||||
int32 val = 0;
|
||||
val = 7500*effect_value;
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
|
||||
if (val > 0)
|
||||
HealDamage(val, caster);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -396,10 +402,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
}
|
||||
|
||||
case SE_Succor:
|
||||
{
|
||||
{
|
||||
|
||||
float x, y, z, heading;
|
||||
const char *target_zone;
|
||||
|
||||
|
||||
x = spell.base[1];
|
||||
y = spell.base[0];
|
||||
z = spell.base[2];
|
||||
@@ -426,6 +433,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
if(MakeRandomInt(0, 99) < RuleI(Spells, SuccorFailChance)) { //2% Fail chance by default
|
||||
|
||||
if(IsClient()) {
|
||||
CastToClient()->Message_StringID(MT_SpellFailure,SUCCOR_FAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Below are the spellid's for known evac/succor spells that send player
|
||||
// to the current zone's safe points.
|
||||
|
||||
@@ -441,10 +456,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
LogFile->write(EQEMuLog::Debug, "Succor/Evacuation Spell In Same Zone.");
|
||||
#endif
|
||||
if(IsClient())
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords);
|
||||
else
|
||||
GMMove(x, y, z, heading);
|
||||
if(IsClient())
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords);
|
||||
else
|
||||
GMMove(x, y, z, heading);
|
||||
}
|
||||
else {
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
@@ -457,7 +472,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
break;
|
||||
}
|
||||
case SE_YetAnotherGate: //Shin: Used on Teleport Bind.
|
||||
case SE_GateCastersBindpoint: //Shin: Used on Teleport Bind.
|
||||
case SE_Teleport: // gates, rings, circles, etc
|
||||
case SE_Teleport2:
|
||||
{
|
||||
@@ -489,7 +504,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
}
|
||||
}
|
||||
|
||||
if (effect == SE_YetAnotherGate && caster->IsClient())
|
||||
if (effect == SE_GateCastersBindpoint && caster->IsClient())
|
||||
{ //Shin: Teleport Bind uses caster's bind point
|
||||
x = caster->CastToClient()->GetBindX();
|
||||
y = caster->CastToClient()->GetBindY();
|
||||
@@ -857,7 +872,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_BindAffinity:
|
||||
case SE_BindAffinity: //TO DO: Add support for secondary and tertiary gate abilities
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Bind Affinity");
|
||||
@@ -989,13 +1004,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Gate:
|
||||
case SE_Gate: //TO DO: Add support for secondary and tertiary gate abilities (base2)
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Gate");
|
||||
#endif
|
||||
if(!spellbonuses.AntiGate)
|
||||
Gate();
|
||||
if(!spellbonuses.AntiGate){
|
||||
|
||||
if(MakeRandomInt(0, 99) < effect_value)
|
||||
Gate();
|
||||
else
|
||||
caster->Message_StringID(MT_SpellFailure,GATE_FAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1378,7 +1398,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
(
|
||||
spell.base[i],
|
||||
Mob::GetDefaultGender(spell.base[i], GetGender()),
|
||||
spell.base2[i]
|
||||
spell.base2[i],
|
||||
spell.max[i]
|
||||
);
|
||||
if(spell.base[i] == OGRE){
|
||||
SendAppearancePacket(AT_Size, 9);
|
||||
@@ -1554,8 +1575,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
if(spell_id == 2488) //Dook- Lifeburn fix
|
||||
break;
|
||||
|
||||
if(IsClient())
|
||||
CastToClient()->SetFeigned(true);
|
||||
if(IsClient()) {
|
||||
|
||||
if (MakeRandomInt(0, 99) > spells[spell_id].base[i]) {
|
||||
CastToClient()->SetFeigned(false);
|
||||
entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName());
|
||||
}
|
||||
else
|
||||
CastToClient()->SetFeigned(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1691,19 +1719,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
// Now we should either be casting this on self or its being cast on a valid group member
|
||||
if(TargetClient) {
|
||||
Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient);
|
||||
if(corpse) {
|
||||
if(TargetClient == this->CastToClient())
|
||||
Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName());
|
||||
else
|
||||
Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName());
|
||||
|
||||
corpse->Summon(CastToClient(), true, true);
|
||||
}
|
||||
else {
|
||||
// No corpse found in the zone
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
if (TargetClient->GetLevel() <= effect_value){
|
||||
|
||||
Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient);
|
||||
if(corpse) {
|
||||
if(TargetClient == this->CastToClient())
|
||||
Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName());
|
||||
else
|
||||
Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName());
|
||||
|
||||
corpse->Summon(CastToClient(), true, true);
|
||||
}
|
||||
else {
|
||||
// No corpse found in the zone
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
|
||||
}
|
||||
else {
|
||||
Message_StringID(4, TARGET_NOT_FOUND);
|
||||
@@ -5986,3 +6020,73 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
|
||||
|
||||
/*For mage 'Bolt' line and other various spells.
|
||||
-This is mostly accurate for how the modern clients handle this effect.
|
||||
-It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic)
|
||||
-The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+
|
||||
-There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier
|
||||
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
|
||||
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
|
||||
-The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly
|
||||
and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement).
|
||||
When bolt hits its predicted point the damage is then done to target.
|
||||
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
|
||||
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
|
||||
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
|
||||
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
|
||||
*/
|
||||
|
||||
if (!spell_target)
|
||||
return false;
|
||||
|
||||
uint8 anim = spells[spell_id].CastingAnim;
|
||||
int bolt_id = -1;
|
||||
|
||||
//Make sure there is an avialable bolt to be cast.
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
if (projectile_spell_id[i] == 0){
|
||||
bolt_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bolt_id < 0)
|
||||
return false;
|
||||
|
||||
if (CheckLosFN(spell_target)) {
|
||||
|
||||
projectile_spell_id[bolt_id] = spell_id;
|
||||
projectile_target_id[bolt_id] = spell_target->GetID();
|
||||
projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ();
|
||||
projectile_increment[bolt_id] = 1;
|
||||
projectile_timer.Start(250);
|
||||
}
|
||||
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
}
|
||||
|
||||
//Pending other types of projectile graphics. (They will function but with a default arrow graphic for now)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
|
||||
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+147
-52
@@ -211,6 +211,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(IsClient() && GetTarget() && IsHarmonySpell(spell_id))
|
||||
{
|
||||
for(int i = 0; i < EFFECT_COUNT; i++) {
|
||||
@@ -554,6 +557,15 @@ uint16 Mob::GetSpecializeSkillValue(uint16 spell_id) const {
|
||||
}
|
||||
|
||||
void Client::CheckSpecializeIncrease(uint16 spell_id) {
|
||||
// These are not active because CheckIncreaseSkill() already does so.
|
||||
// It's such a rare occurance that adding them here is wasted..(ref only)
|
||||
/*
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
*/
|
||||
|
||||
switch(spells[spell_id].skill) {
|
||||
case SkillAbjuration:
|
||||
CheckIncreaseSkill(SkillSpecializeAbjure, nullptr);
|
||||
@@ -577,6 +589,15 @@ void Client::CheckSpecializeIncrease(uint16 spell_id) {
|
||||
}
|
||||
|
||||
void Client::CheckSongSkillIncrease(uint16 spell_id){
|
||||
// These are not active because CheckIncreaseSkill() already does so.
|
||||
// It's such a rare occurance that adding them here is wasted..(ref only)
|
||||
/*
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
*/
|
||||
|
||||
switch(spells[spell_id].skill)
|
||||
{
|
||||
case SkillSinging:
|
||||
@@ -1809,7 +1830,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
|
||||
// check line of sight to target if it's a detrimental spell
|
||||
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id))
|
||||
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional)
|
||||
{
|
||||
mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName());
|
||||
Message_StringID(13,CANT_SEE_TARGET);
|
||||
@@ -1874,7 +1895,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
if (isproc) {
|
||||
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
|
||||
} else {
|
||||
if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if (spells[spell_id].targettype == ST_TargetOptional){
|
||||
if (!TrySpellProjectile(spell_target, spell_id))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
||||
// Prevent mana usage/timers being set for beneficial buffs
|
||||
if(casting_spell_type == 1)
|
||||
@@ -1883,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPlayerIllusionSpell(spell_id)
|
||||
&& IsClient()
|
||||
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
||||
@@ -2586,6 +2613,14 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
{
|
||||
effect1 = sp1.effectid[i];
|
||||
effect2 = sp2.effectid[i];
|
||||
|
||||
if (spellbonuses.Screech == 1) {
|
||||
if (effect2 == SE_Screech && sp2.base[i] == -1) {
|
||||
Message_StringID(MT_SpellFailure, SCREECH_BUFF_BLOCK, sp2.name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(effect2 == SE_StackingCommand_Overwrite)
|
||||
{
|
||||
overwrite_effect = sp2.base[i];
|
||||
@@ -2630,7 +2665,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.",
|
||||
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mlog(SPELLS__STACKING, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks",
|
||||
@@ -3419,8 +3454,15 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
|
||||
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
|
||||
{
|
||||
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
|
||||
if (spells[spell_id].resisttype == RESIST_PHYSICAL){
|
||||
Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
else {
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
|
||||
if(spelltar->IsAIControlled()){
|
||||
int32 aggro = CheckAggroAmount(spell_id);
|
||||
@@ -4169,67 +4211,83 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
break;
|
||||
case RESIST_PHYSICAL:
|
||||
{
|
||||
if (IsNPC())
|
||||
target_resist = GetPhR();
|
||||
else
|
||||
target_resist = 0;
|
||||
}
|
||||
default:
|
||||
//This is guessed but the others are right
|
||||
target_resist = (GetSTA() / 4);
|
||||
|
||||
target_resist = 0;
|
||||
}
|
||||
|
||||
//Setup our base resist chance.
|
||||
int resist_chance = 0;
|
||||
int level_mod = 0;
|
||||
|
||||
//Adjust our resist chance based on level modifiers
|
||||
int temp_level_diff = GetLevel() - caster->GetLevel();
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
|
||||
//Physical Resists are calclated using their own formula derived from extensive parsing.
|
||||
if (resist_type == RESIST_PHYSICAL) {
|
||||
level_mod = ResistPhysical(temp_level_diff, caster->GetLevel());
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
int level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
|
||||
if (CharismaCheck)
|
||||
@@ -4376,6 +4434,43 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
int Mob::ResistPhysical(int level_diff, uint8 caster_level)
|
||||
{
|
||||
/* Physical resists use the standard level mod calculation in
|
||||
conjunction with a resist fall off formula that greatly prevents you
|
||||
from landing abilities on mobs that are higher level than you.
|
||||
After level 12, every 4 levels gained the max level you can hit
|
||||
your target without a sharp resist penalty is raised by 1.
|
||||
Extensive parsing confirms this, along with baseline phyiscal resist rates used.
|
||||
*/
|
||||
|
||||
|
||||
if (level_diff == 0)
|
||||
return level_diff;
|
||||
|
||||
int level_mod = 0;
|
||||
|
||||
if (level_diff > 0) {
|
||||
|
||||
int ResistFallOff = 0;
|
||||
|
||||
if (caster_level <= 12)
|
||||
ResistFallOff = 3;
|
||||
else
|
||||
ResistFallOff = caster_level/4;
|
||||
|
||||
if (level_diff > ResistFallOff || level_diff >= 15)
|
||||
level_mod = ((level_diff * 10) + level_diff)*2;
|
||||
else
|
||||
level_mod = level_diff * level_diff / 2;
|
||||
}
|
||||
|
||||
else
|
||||
level_mod = -(level_diff * level_diff / 2);
|
||||
|
||||
return level_mod;
|
||||
}
|
||||
|
||||
int16 Mob::CalcResistChanceBonus()
|
||||
{
|
||||
int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance;
|
||||
|
||||
+9
-8
@@ -1972,14 +1972,6 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
|
||||
Task->Activity[ActivityID].GoalCount,
|
||||
ActivityID);
|
||||
|
||||
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST)
|
||||
{
|
||||
char buf[24];
|
||||
snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID);
|
||||
buf[23] = '\0';
|
||||
parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0);
|
||||
}
|
||||
|
||||
// Flag the activity as complete
|
||||
ActiveTasks[TaskIndex].Activity[ActivityID].State = ActivityCompleted;
|
||||
// Unlock subsequent activities for this task
|
||||
@@ -1991,6 +1983,15 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
|
||||
taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false);
|
||||
// Inform the client the task has been updated, both by a chat message
|
||||
c->Message(0, "Your task '%s' has been updated.", Task->Title);
|
||||
|
||||
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST)
|
||||
{
|
||||
char buf[24];
|
||||
snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID);
|
||||
buf[23] = '\0';
|
||||
parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0);
|
||||
}
|
||||
|
||||
// If this task is now complete, the Completed tasks will have been
|
||||
// updated in UnlockActivities. Send the completed task list to the
|
||||
// client. This is the same sequence the packets are sent on live.
|
||||
|
||||
@@ -1047,6 +1047,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.FR,"
|
||||
"npc_types.PR,"
|
||||
"npc_types.Corrup,"
|
||||
"npc_types.PhR,"
|
||||
"npc_types.mindmg,"
|
||||
"npc_types.maxdmg,"
|
||||
"npc_types.attack_count,"
|
||||
@@ -1143,6 +1144,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->FR = atoi(row[r++]);
|
||||
tmpNPCType->PR = atoi(row[r++]);
|
||||
tmpNPCType->Corrup = atoi(row[r++]);
|
||||
tmpNPCType->PhR = atoi(row[r++]);
|
||||
tmpNPCType->min_dmg = atoi(row[r++]);
|
||||
tmpNPCType->max_dmg = atoi(row[r++]);
|
||||
tmpNPCType->attack_count = atoi(row[r++]);
|
||||
|
||||
@@ -75,6 +75,7 @@ struct NPCType
|
||||
int16 PR;
|
||||
int16 DR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
uint8 haircolor;
|
||||
uint8 beardcolor;
|
||||
uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye?
|
||||
|
||||
Reference in New Issue
Block a user