mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-06 00:32:25 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
1eb090a3b2
158
changelog.txt
158
changelog.txt
@ -1,5 +1,163 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 04/06/2014 ==
|
||||
Uleat: Changed Mob::CanThisClassDualWield() behavior. This should let non-monk/beastlord dual-wielding classes attack with either fist as long as the other hand is occupied.
|
||||
Notes:
|
||||
See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328
|
||||
|
||||
|
||||
== 04/05/2014 ==
|
||||
Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality
|
||||
for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality.
|
||||
This rule is set to 'true' by default as the original functionality from Live was intended to be
|
||||
Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can
|
||||
see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading
|
||||
variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and
|
||||
unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval.
|
||||
Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true.
|
||||
If Combat: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.
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql
|
||||
|
||||
== 04/04/2014 ==
|
||||
Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing.
|
||||
SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations.
|
||||
|
||||
Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql
|
||||
|
||||
== 04/03/2014 ==
|
||||
Kayen: Implemented live like spell projectiles (ie. Mage Bolts).
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql
|
||||
Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients.
|
||||
|
||||
== 04/01/2014 ==
|
||||
demonstar55: Implemented ability for a merchant to open and close shop.
|
||||
Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop()
|
||||
GM Commands: #merchant_open_shop (short: #open_shop) and #merchant_close_shop (short: #close_shop)
|
||||
default to status 100, just in case you need to force the merchants status
|
||||
Trevius: Fixed potential endless quest loop with EVENT_COMBAT and WipeHateList().
|
||||
|
||||
== 03/31/2014 ==
|
||||
Uleat: Fix for unconscious skillups.
|
||||
Uleat: Fix for crash issue with nullptr reference in recent Client::SummonItem() work.
|
||||
Uleat: Added rule for GM Status check code in Client::SummonItem().
|
||||
Note: Rule default is set to 250..but, implementation is on hold until load item code handles the database 'minstatus' field.
|
||||
Uleat: Added RuleB(Bots, BotLevelsWithOwner). Bots will auto-update as their owner levels/de-levels. Appearance packets are sent to show the 'leveling effect' as well as updating client entities.
|
||||
Trevius: Prevented an endless loop crash related to EVENT_TASK_STAGE_COMPLETE.
|
||||
|
||||
Optional Bot SQL: utils/sql/git/bot/optional/2014_03_31_BotLevelsWithOwnerRule.sql
|
||||
Note: This sql is required to activate the optional behavior.
|
||||
|
||||
== 03/27/2014 ==
|
||||
Kayen: SE_Gate will now use have a fail chance as defined by its base value in the spell data.
|
||||
Kayen: SE_Succor will now have a baseline fail chance of (2%). Rule added to adjust this as needed.
|
||||
Kayen: SE_FeignDeath will now have a fail chance as defined by its base value in the spell data.
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_03_27_SuccorFailRule.sql
|
||||
|
||||
== 03/22/2014 ==
|
||||
Uleat: Moved the existing 'load_bots' and 'drop_bots' sqls into the emu git repository for the time being. Look to the
|
||||
/utils/sql/git/bots/ folder to find them. The 'load_bots' sql has been updated to include the below fix, as well as
|
||||
collecting the multiple files into one. This should allow HeidiSQL to now run it properly. (You will still need to
|
||||
search for the optional scripts for the time being.)
|
||||
Uleat: Fixed bot guild script failure. For existing bot databases only, use this sql file to update the affected table and view.
|
||||
|
||||
Required Bot SQL: 2014_03_22_BotGuildMember_ScriptFailureFix.sql
|
||||
|
||||
== 03/19/2014 ==
|
||||
Kayen: Further refinements to root, charm, mez and fear behaviors - See commit message for full details
|
||||
|
||||
New rule for 'Fear' break chance, and updates to default settings of various rules.
|
||||
Optional SQL: utils/sql/git/optional/2014_03_19_RulesUpdates.sql
|
||||
|
||||
|
||||
== 03/18/2014 ==
|
||||
Uleat: Fixed the name/account discrepancy in the Client::SummonItem() code as well as the origin of the mistake (thanks K_K!)
|
||||
Uleat: Condensed and rearranged certain snippets of code in SummonItem(). Added a 'augslotvisible' check to validation check.
|
||||
Note: If you are experiencing issues with SummonItem, please enable 'INVENTORY_ERROR' logging if it not active on your server.
|
||||
|
||||
== 03/17/2014 ==
|
||||
Uleat: Updated Client::SummonItem() to check for valid item combinations when augmentations are passed.
|
||||
Uleat: Changed the return type of Client::SummonItem() from void to bool. Calling methods and APIs have not been update as yet.
|
||||
Uleat: Fixed the RoF Item structure to properly pass the 'augrestrict' variable. RoF clients now show restrictions in the Item Information window.
|
||||
|
||||
Optional SQL: 2014/03/17_EnforceAugmentRules.sql
|
||||
Note: This adds the rules Inventory:EnforceAugmentRestriction, Inventory:EnforceAugmentUsability and Inventory:EnforceAugmentWear.
|
||||
If you run into script/recipe issues, running this sql file will set the default enforcement rules to false.
|
||||
If you still run into issues, you may want to check that your scripts are not trying to augment non-common items.
|
||||
Please post any failures as bugs and be sure to include the base item ID, as well as any augment IDs that you are using.
|
||||
|
||||
== 03/12/2014 ==
|
||||
Kayen: Melee/Magic runes are now calculated as bonuses. Resolved issues with runes not working and not fading properly.
|
||||
|
||||
== 03/08/2014 ==
|
||||
Kayen: Revision to lull/harmony/pacification code to be consistent with live based on extensive personal parsing.
|
||||
*Lulls on initial cast do not check regular resists (MR ect) but only apply a flat ~7.5 % resist chance + level modifier
|
||||
*If Lull is resisted, a second resistance check is made this time using regular resists and a charisma modifier (same as charm)
|
||||
which if 'resisted' will cause the caster to gain aggro.
|
||||
|
||||
== 03/05/2014 ==
|
||||
demonstar55: Corrected rogue's evade to be single target
|
||||
sorvani: fixed crash issue 119
|
||||
|
||||
== 03/04/2014 ==
|
||||
Sorvani: Created RemoveFromInstance and RemoveAllFromInstance to remove a single player or all players in an instance.
|
||||
Sorvani: Exported to Lua as eq.remove_from_instance(instance_id) and eq.remove_all_from_instance(instance_id).
|
||||
Kayen: Revision to root code to be consistent with live based on extensive personal parsing.
|
||||
*ROOT has a 40% chance to do a resist check to break each buff tick.
|
||||
*If multiple roots on target. Root in first slot will always and only be check to break from nukes.
|
||||
*If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot.
|
||||
*Roots on beneficial spells can not be broken by spell damage.
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_03_04_RootRule.sql
|
||||
|
||||
== 03/03/2014 ==
|
||||
demonstar55: Implemented deadly strikes and gave rogues higher innate throwing crit chance
|
||||
|
||||
New rules: Combat:RogueCritThrowingChance, Combat:RogueDeadlyStrikeChance, Combat:RogueDeadlyStrikeMod
|
||||
Defaults should give fairly close to live results
|
||||
|
||||
== 03/02/2014 ==
|
||||
Kayen: Revision to charm code to be consistent with live based on extensive personal parsing.
|
||||
*Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 200 CHA
|
||||
*Charisma DOES NOT extend charm durations.
|
||||
|
||||
Optional SQL: utils/sql/git/optional/2014_03_02_CharmRules.sql
|
||||
|
||||
demonstar55: Melee Crits, HoTs, DoTs messages should now be filtered correctly on all clients.
|
||||
Clients that also support seeing others DoTs will now see them if they don't filter them
|
||||
note: some newer clients have a 'mine only' option for HoTs but it functions the same as show
|
||||
|
||||
== 02/27/2014 ==
|
||||
cavedude: Exported TrainDisc to Lua.
|
||||
|
||||
== 02/26/2014 ==
|
||||
Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells
|
||||
Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs
|
||||
cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal.
|
||||
cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord.
|
||||
cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay.
|
||||
Uleat: Fix for 'sqrt' failure on vs2010 clients
|
||||
image: Added idle zone timer to save CPU cycles.
|
||||
|
||||
Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql
|
||||
|
||||
== 02/24/2014 ==
|
||||
cavedude: Better flee runspeed calculation. Added rule Combat:FleeMultiplier to alter this behavior.
|
||||
Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535)
|
||||
|
||||
== 02/23/2014 ==
|
||||
Secrets: Exported the client object SendTargetCommand to Perl.
|
||||
cavedude: Merchants will now keep better track of charges.
|
||||
|
||||
== 02/20/2014 ==
|
||||
Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value
|
||||
Kayen: Implemented SE_DistanceRemoval - removed from target when target moves X amount of distance away from where initially hit.
|
||||
|
||||
Required SQL: utils/sql/git/2014_02_20_buff_updates.sql
|
||||
|
||||
== 02/18/2014 ==
|
||||
Kayen: Implemented SE_TriggerOnReqCaster - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
Kayen: Implemented SE_ImprovedTaunt - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y
|
||||
|
||||
@ -238,6 +238,14 @@ bool ItemInst::IsStackable() const
|
||||
return m_item->Stackable;
|
||||
}
|
||||
|
||||
bool ItemInst::IsCharged() const
|
||||
{
|
||||
if(m_item->MaxCharges > 1)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can item be equipped?
|
||||
|
||||
bool ItemInst::IsEquipable(uint16 race, uint16 class_) const
|
||||
|
||||
@ -291,6 +291,7 @@ public:
|
||||
|
||||
// Can item be stacked?
|
||||
bool IsStackable() const;
|
||||
bool IsCharged() const;
|
||||
|
||||
// Can item be equipped by/at?
|
||||
bool IsEquipable(uint16 race, uint16 class_) const;
|
||||
|
||||
@ -193,7 +193,10 @@ uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const cha
|
||||
return *strlen;
|
||||
}
|
||||
|
||||
uint32 hextoi(char* num) {
|
||||
uint32 hextoi(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
@ -217,7 +220,10 @@ uint32 hextoi(char* num) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 hextoi64(char* num) {
|
||||
uint64 hextoi64(const char* num) {
|
||||
if (num == nullptr)
|
||||
return 0;
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
return 0;
|
||||
@ -241,7 +247,10 @@ uint64 hextoi64(char* num) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool atobool(char* iBool) {
|
||||
bool atobool(const char* iBool) {
|
||||
|
||||
if (iBool == nullptr)
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "true"))
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "false"))
|
||||
|
||||
@ -33,9 +33,9 @@ void MakeLowerString(const char *source, char *target);
|
||||
int MakeAnyLenString(char** ret, const char* format, ...);
|
||||
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...);
|
||||
|
||||
uint32 hextoi(char* num);
|
||||
uint64 hextoi64(char* num);
|
||||
bool atobool(char* iBool);
|
||||
uint32 hextoi(const char* num);
|
||||
uint64 hextoi64(const char* num);
|
||||
bool atobool(const char* iBool);
|
||||
|
||||
char* strn0cpy(char* dest, const char* source, uint32 size);
|
||||
// return value =true if entire string(source) fit, false if it was truncated
|
||||
|
||||
@ -2516,30 +2516,54 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
uint32 count = RuleI(Zone, ReservedInstances) + 1;
|
||||
uint32 count = RuleI(Zone, ReservedInstances);
|
||||
uint32 max = 65535;
|
||||
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id >= %i ORDER BY id", count), errbuf, &result)) {
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count,count), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) != 0) {
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
if(count < atoi(row[0])) {
|
||||
instance_id = count;
|
||||
mysql_free_result(result);
|
||||
return true;
|
||||
} else if(count > max) {
|
||||
instance_id = 0;
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
row = mysql_fetch_row(result);
|
||||
if(atoi(row[0]) <= max) {
|
||||
count = atoi(row[0]);
|
||||
mysql_free_result(result);
|
||||
} else {
|
||||
mysql_free_result(result);
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id > %u ORDER BY id", count), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) != 0) {
|
||||
count++;
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
if(count < atoi(row[0])) {
|
||||
instance_id = count;
|
||||
mysql_free_result(result);
|
||||
return true;
|
||||
} else if(count > max) {
|
||||
instance_id = 0;
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
} else {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance_id = 0;
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
count++;
|
||||
safe_delete_array(query);
|
||||
instance_id = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance_id = 0;
|
||||
mysql_free_result(result);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
safe_delete_array(query);
|
||||
instance_id = 0;
|
||||
return false;
|
||||
}
|
||||
instance_id = count;
|
||||
return true;
|
||||
|
||||
@ -137,6 +137,83 @@ enum ItemUseTypes : uint8
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
** Augmentation use types (in-work)
|
||||
**
|
||||
** (ref: dbstr_us.txt)
|
||||
**
|
||||
*/
|
||||
enum AugmentationUseTypes : uint32 {
|
||||
AugTypeNone = 0, // not 100% sure on this...
|
||||
AugTypeGeneralSingleStat, /*1^16^1 (General: Single Stat)^0*/
|
||||
AugTypeGeneralMultipleStat, /*2^16^2 (General: Multiple Stat)^0*/
|
||||
AugTypeGeneralSpellEffect, /*3^16^3 (General: Spell Effect)^0*/
|
||||
AugTypeWeaponGeneral, /*4^16^4 (Weapon: General)^0*/
|
||||
AugTypeWeaponElemDamage, /*5^16^5 (Weapon: Elem Damage)^0*/
|
||||
AugTypeWeaponBaseDamage, /*6^16^6 (Weapon: Base Damage)^0*/
|
||||
AugTypeGeneralGroup, /*7^16^7 (General: Group)^0*/
|
||||
AugTypeGeneralRaid, /*8^16^8 (General: Raid)^0*/
|
||||
AugTypeGeneralDragonsPoints, /*9^16^9 (General: Dragons Points)^0*/
|
||||
AugTypeCraftedCommon, /*10^16^10 (Crafted: Common)^0*/
|
||||
AugTypeCraftedGroup1, /*11^16^11 (Crafted: Group)^0*/
|
||||
AugTypeCraftedRaid1, /*12^16^12 (Crafted: Raid)^0*/
|
||||
AugTypeEnergeiacGroup, /*13^16^13 (Energeiac: Group)^0*/
|
||||
AugTypeEnergeiacRaid, /*14^16^14 (Energeiac: Raid)^0*/
|
||||
AugTypeEmblem, /*15^16^15 (Emblem)^0*/
|
||||
AugTypeCraftedGroup2, /*16^16^16 (Crafted: Group)^0*/
|
||||
AugTypeCraftedRaid2, /*17^16^17 (Crafted: Raid)^0*/
|
||||
AugTypeUnknown1, /*18^16^18^0*/
|
||||
AugTypeUnknown2, /*19^16^19^0*/
|
||||
AugTypeOrnamentation, /*20^16^20 (Ornamentation)^0*/
|
||||
AugTypeSpecialOrnamentation, /*21^16^21 (Special Ornamentation)^0*/
|
||||
AugTypeUnknown3, /*22^16^22^0*/
|
||||
AugTypeUnknown4, /*23^16^23^0*/
|
||||
AugTypeUnknown5, /*24^16^24^0*/
|
||||
AugTypeUnknown6, /*25^16^25^0*/
|
||||
AugTypeUnknown7, /*26^16^26^0*/
|
||||
AugTypeUnknown8, /*27^16^27^0*/
|
||||
AugTypeUnknown9, /*28^16^28^0*/
|
||||
AugTypeUnknown10, /*29^16^29^0*/
|
||||
AugTypeEpic25, /*30^16^30^0*/
|
||||
AugTypeTest, /*31^16^Test^0*/ // listed as 31^16^31^0 in 5-10 client
|
||||
_AugTypeCount
|
||||
};
|
||||
|
||||
/*
|
||||
** Augmentation restriction types (in-work)
|
||||
**
|
||||
** (ref: eqstr_us.txt)
|
||||
**
|
||||
*/
|
||||
enum AugmentationRestrictionTypes : uint8 {
|
||||
/*4690*/ AugRestrAny = 0,
|
||||
/*9134*/ AugRestrArmor,
|
||||
/*9135*/ AugRestrWeapons,
|
||||
/*9136*/ AugRestr1HWeapons,
|
||||
/*9137*/ AugRestr2HWeapons,
|
||||
/*9138*/ AugRestr1HSlash,
|
||||
/*9139*/ AugRestr1HBlunt,
|
||||
/*9140*/ AugRestrPiercing,
|
||||
/*9148*/ AugRestrHandToHand,
|
||||
/*9141*/ AugRestr2HSlash,
|
||||
/*9142*/ AugRestr2HBlunt,
|
||||
/*9143*/ AugRestr2HPierce,
|
||||
/*9144*/ AugRestrBows,
|
||||
/*9145*/ AugRestrShields,
|
||||
/*8052*/ AugRestr1HSlash1HBluntOrHandToHand,
|
||||
/*9200*/ AugRestr1HBluntOrHandToHand, // no listed peq entries
|
||||
|
||||
// these three appear to be post-RoF (12-10-2012) and can not be verified until RoF (05-10-2013) is supported
|
||||
/*????*/ AugRestrUnknown1,
|
||||
/*????*/ AugRestrUnknown2,
|
||||
/*????*/ AugRestrUnknown3, // last value in peq entries
|
||||
_AugRestrCount
|
||||
|
||||
/*4687*/ //AugTypeAllItems, // ?? unknown atm
|
||||
/*4688*/ //AugTypePrestige, // ?? unknown atm
|
||||
/*4689*/ //AugTypeNonPrestige, // ?? unknown atm
|
||||
};
|
||||
|
||||
/*
|
||||
** Container use types
|
||||
**
|
||||
|
||||
@ -5084,8 +5084,9 @@ char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint
|
||||
memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct));
|
||||
|
||||
isbs.augtype = item->AugType;
|
||||
isbs.augrestrict = item->AugRestrict;
|
||||
isbs.augdistiller = 0;
|
||||
isbs.augrestrict = item->AugRestrict;
|
||||
|
||||
|
||||
for(int x = 0; x < 5; ++x)
|
||||
{
|
||||
|
||||
@ -4427,8 +4427,11 @@ struct AugSlotStruct
|
||||
struct ItemSecondaryBodyStruct
|
||||
{
|
||||
uint32 augtype;
|
||||
uint32 augrestrict;
|
||||
// swapped augrestrict and augdistiller positions
|
||||
// (this swap does show the proper augment restrictions in Item Information window now)
|
||||
// unsure what the purpose of augdistiller is at this time -U 3/17/2014
|
||||
uint32 augdistiller; // New to December 10th 2012 client - NEW
|
||||
uint32 augrestrict;
|
||||
AugSlotStruct augslots[6];
|
||||
|
||||
uint32 ldonpoint_type;
|
||||
|
||||
@ -138,6 +138,7 @@ RULE_BOOL( Pets, UnTargetableSwarmPet, false )
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( GM )
|
||||
RULE_INT ( GM, MinStatusToSummonItem, 250)
|
||||
RULE_INT ( GM, MinStatusToZoneAnywhere, 250 )
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
@ -290,14 +291,22 @@ RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to i
|
||||
RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs
|
||||
RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers
|
||||
RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod.
|
||||
RULE_INT ( Spells, CharismaEffectivenessCap, 255) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod.
|
||||
RULE_BOOL ( Spells, CharismaCharmDuration, false) // Allow CHA resist mod to extend charm duration.
|
||||
RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick.
|
||||
RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste
|
||||
RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on.
|
||||
RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on.
|
||||
RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing.
|
||||
RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount.
|
||||
RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects)
|
||||
RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this?
|
||||
RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live
|
||||
RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick.
|
||||
RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick.
|
||||
RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player
|
||||
RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'.
|
||||
RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'.
|
||||
RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'.
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Combat )
|
||||
@ -306,12 +315,15 @@ RULE_INT ( Combat, WarBerBaseCritChance, 3 ) //The base crit chance for warriors
|
||||
RULE_INT ( Combat, BerserkBaseCritChance, 6 ) //The bonus base crit chance you get when you're berserk
|
||||
RULE_INT ( Combat, NPCBashKickLevel, 6 ) //The level that npcs can KICK/BASH
|
||||
RULE_INT ( Combat, NPCBashKickStunChance, 15 ) //Percent chance that a bash/kick will stun
|
||||
RULE_INT ( Combat, RogueCritThrowingChance, 25) //Rogue throwing crit bonus
|
||||
RULE_INT ( Combat, RogueDeadlyStrikeChance, 80) //Rogue chance throwing from behind crit becomes a deadly strike
|
||||
RULE_INT ( Combat, RogueDeadlyStrikeMod, 2) //Deadly strike modifier to crit damage
|
||||
RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance.
|
||||
RULE_BOOL ( Combat, UseIntervalAC, true)
|
||||
RULE_INT ( Combat, PetAttackMagicLevel, 30)
|
||||
RULE_BOOL ( Combat, EnableFearPathing, true)
|
||||
RULE_INT ( Combat, FleeHPRatio, 25)
|
||||
RULE_INT ( Combat, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC.
|
||||
RULE_REAL ( Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker.
|
||||
RULE_INT ( Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee.
|
||||
RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
|
||||
RULE_BOOL ( Combat, AdjustProcPerMinute, true)
|
||||
RULE_REAL ( Combat, AvgProcsPerMinute, 2.0)
|
||||
@ -384,6 +396,7 @@ RULE_BOOL ( Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus req
|
||||
RULE_INT ( Combat, ArcheryBonusChance, 50)
|
||||
RULE_INT ( Combat, BerserkerFrenzyStart, 35)
|
||||
RULE_INT ( Combat, BerserkerFrenzyEnd, 45)
|
||||
RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( NPC )
|
||||
@ -439,6 +452,7 @@ RULE_BOOL ( Bots, BotSpellQuest, false ) // Anita Thrall's (Anita_Thrall.pl) Bot
|
||||
RULE_INT ( Bots, BotAAExpansion, 8 ) // Bots get AAs through this expansion
|
||||
RULE_BOOL ( Bots, BotGroupXP, false ) // Determines whether client gets xp for bots outside their group.
|
||||
RULE_BOOL ( Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs.
|
||||
RULE_BOOL ( Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior)
|
||||
RULE_CATEGORY_END()
|
||||
#endif
|
||||
|
||||
@ -466,6 +480,8 @@ RULE_INT ( Merchant, PriceBonusPct, 4) // Determines maximum price bonus from ha
|
||||
RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent.
|
||||
RULE_REAL( Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive.
|
||||
RULE_REAL ( Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive.
|
||||
RULE_BOOL ( Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants
|
||||
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY ( Bazaar )
|
||||
@ -529,6 +545,12 @@ RULE_BOOL( QueryServ, MerchantLogTransactions, false) // Logs Merchant Transacti
|
||||
RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Inventory )
|
||||
RULE_BOOL ( Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions
|
||||
RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item usability
|
||||
RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
#undef RULE_CATEGORY
|
||||
#undef RULE_INT
|
||||
#undef RULE_REAL
|
||||
|
||||
@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
sp[tempid].descnum = atoi(row[155]);
|
||||
sp[tempid].effectdescnum = atoi(row[157]);
|
||||
|
||||
sp[tempid].not_reflectable = atoi(row[161]) != 0;
|
||||
sp[tempid].reflectable = atoi(row[161]) != 0;
|
||||
sp[tempid].bonushate=atoi(row[162]);
|
||||
|
||||
sp[tempid].EndurCost=atoi(row[166]);
|
||||
|
||||
@ -390,9 +390,7 @@ bool IsPartialCapableSpell(uint16 spell_id)
|
||||
if (spells[spell_id].no_partial_resist)
|
||||
return false;
|
||||
|
||||
if (IsPureNukeSpell(spell_id) || IsFearSpell(spell_id) ||
|
||||
IsEffectInSpell(spell_id, SE_Root) ||
|
||||
IsEffectInSpell(spell_id, SE_Charm))
|
||||
if (IsPureNukeSpell(spell_id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
126
common/spdat.h
126
common/spdat.h
@ -159,8 +159,8 @@ typedef enum {
|
||||
#define SE_WIS 9 // implemented
|
||||
#define SE_CHA 10 // implemented - used as a spacer
|
||||
#define SE_AttackSpeed 11 // implemented
|
||||
#define SE_Invisibility 12 // implemented
|
||||
#define SE_SeeInvis 13 // implemented
|
||||
#define SE_Invisibility 12 // implemented - TO DO: Implemented Invisiblity Levels
|
||||
#define SE_SeeInvis 13 // implemented - TO DO: Implemented See Invisiblity Levels
|
||||
#define SE_WaterBreathing 14 // implemented
|
||||
#define SE_CurrentMana 15 // implemented
|
||||
//#define SE_NPCFrenzy 16 // not used
|
||||
@ -172,7 +172,7 @@ typedef enum {
|
||||
#define SE_Charm 22 // implemented
|
||||
#define SE_Fear 23 // implemented
|
||||
#define SE_Stamina 24 // implemented - Invigor and such
|
||||
#define SE_BindAffinity 25 // implemented
|
||||
#define SE_BindAffinity 25 // implemented - TO DO: Implement 2nd and 3rd Recall (value 2,3 ect). Sets additional bind points.
|
||||
#define SE_Gate 26 // implemented - Gate to bind point
|
||||
#define SE_CancelMagic 27 // implemented
|
||||
#define SE_InvisVsUndead 28 // implemented
|
||||
@ -211,7 +211,7 @@ typedef enum {
|
||||
#define SE_Identify 61 // implemented
|
||||
//#define SE_ItemID 62 // not used
|
||||
#define SE_WipeHateList 63 // implemented
|
||||
#define SE_SpinTarget 64 // implemented
|
||||
#define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly
|
||||
#define SE_InfraVision 65 // implemented
|
||||
#define SE_UltraVision 66 // implemented
|
||||
#define SE_EyeOfZomm 67 // implemented
|
||||
@ -241,7 +241,7 @@ typedef enum {
|
||||
#define SE_SummonCorpse 91 // implemented
|
||||
#define SE_InstantHate 92 // implemented - add hate
|
||||
#define SE_StopRain 93 // implemented - Wake of Karana
|
||||
#define SE_NegateIfCombat 94 // *not implemented? - Works client side but there is comment todo in spell effects...Component of Spirit of Scale
|
||||
#define SE_NegateIfCombat 94 // implemented
|
||||
#define SE_Sacrifice 95 // implemented
|
||||
#define SE_Silence 96 // implemented
|
||||
#define SE_ManaPool 97 // implemented
|
||||
@ -258,19 +258,19 @@ typedef enum {
|
||||
#define SE_Familiar 108 // implemented
|
||||
#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container
|
||||
//#define SE_IncreaseArchery 110 // not used
|
||||
#define SE_ResistAll 111 // implemented
|
||||
#define SE_ResistAll 111 // implemented - Note: Physical Resists are not modified by this effect.
|
||||
#define SE_CastingLevel 112 // implemented
|
||||
#define SE_SummonHorse 113 // implemented
|
||||
#define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage)
|
||||
#define SE_Hunger 115 // implemented - Song of Sustenance
|
||||
#define SE_CurseCounter 116 // implemented
|
||||
#define SE_MagicWeapon 117 // implemented - makes weapon magical
|
||||
#define SE_SingingSkill 118 // *implemented - needs AA conversion
|
||||
#define SE_Amplification 118 // implemented - Harmonize/Amplification (stacks with other singing mods)
|
||||
#define SE_AttackSpeed3 119 // implemented
|
||||
#define SE_HealRate 120 // implemented - reduces healing by a %
|
||||
#define SE_ReverseDS 121 // implemented
|
||||
//#define SE_ReduceSkill 122 // not used
|
||||
#define SE_Screech 123 // implemented? Spell Blocker(can only have one buff with this effect at one time)
|
||||
#define SE_Screech 123 // implemented Spell Blocker(If have buff with value +1 will block any effect with -1)
|
||||
#define SE_ImprovedDamage 124 // implemented
|
||||
#define SE_ImprovedHeal 125 // implemented
|
||||
#define SE_SpellResistReduction 126 // implemented
|
||||
@ -293,7 +293,7 @@ typedef enum {
|
||||
#define SE_LimitCastTimeMin 143 // implemented
|
||||
#define SE_LimitCastTimeMax 144 // implemented (*not used in any known live spell)
|
||||
#define SE_Teleport2 145 // implemented - Banishment of the Pantheon
|
||||
#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233)
|
||||
//#define SE_ElectricityResist 146 // *not implemented (Lightning Rod: 23233)
|
||||
#define SE_PercentalHeal 147 // implemented
|
||||
#define SE_StackingCommand_Block 148 // implemented?
|
||||
#define SE_StackingCommand_Overwrite 149 // implemented?
|
||||
@ -349,73 +349,73 @@ typedef enum {
|
||||
#define SE_Taunt 199 // implemented - % chance to taunt the target
|
||||
#define SE_ProcChance 200 // implemented
|
||||
#define SE_RangedProc 201 // implemented
|
||||
#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion)
|
||||
#define SE_MassGroupBuff 203 // *not implemented as bonus
|
||||
//#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion)
|
||||
//#define SE_MassGroupBuff 203 // *not implemented as bonus
|
||||
#define SE_GroupFearImmunity 204 // *not implemented as bonus
|
||||
#define SE_Rampage 205 // implemented
|
||||
#define SE_AETaunt 206 // implemented
|
||||
#define SE_FleshToBone 207 // implemented
|
||||
//#define SE_PurgePoison 208 // not used
|
||||
#define SE_DispelBeneficial 209 // implemented
|
||||
#define SE_PetShield 210 // *not implemented
|
||||
//#define SE_PetShield 210 // *not implemented
|
||||
#define SE_AEMelee 211 // implemented
|
||||
#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live)
|
||||
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
|
||||
#define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet
|
||||
#define SE_MaxHPChange 214 // implemented
|
||||
#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage
|
||||
#define SE_Accuracy 216 // implemented
|
||||
#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage)
|
||||
//#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage)
|
||||
#define SE_PetCriticalHit 218 // implemented[AA] - gives pets a baseline critical hit chance
|
||||
#define SE_SlayUndead 219 // implemented - Allow extra damage against undead (base1 = rate, base2 = damage mod).
|
||||
#define SE_SkillDamageAmount 220 // implemented
|
||||
#define SE_Packrat 221 // not implemented as bonus
|
||||
#define SE_Packrat 221 // implemented as bonus
|
||||
#define SE_BlockBehind 222 // implemented - Chance to block from behind (with our without Shield)
|
||||
//#define SE_DoubleRiposte 223 // not used
|
||||
#define SE_GiveDoubleRiposte 224 // implemented[AA]
|
||||
#define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance.
|
||||
#define SE_TwoHandBash 226 // not implemented as bonus
|
||||
#define SE_TwoHandBash 226 // *not implemented as bonus
|
||||
#define SE_ReduceSkillTimer 227 // implemented
|
||||
#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling
|
||||
#define SE_PersistantCasting 229 // implemented
|
||||
#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability
|
||||
//#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability
|
||||
#define SE_StunBashChance 231 // implemented - increase chance to stun from bash.
|
||||
#define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save)
|
||||
#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism?
|
||||
#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison
|
||||
//#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism?
|
||||
//#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison
|
||||
#define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live.
|
||||
//#define SE_FreePet 236 // not used
|
||||
#define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity)
|
||||
#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed.
|
||||
#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
|
||||
//#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed.
|
||||
//#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
|
||||
//#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.]
|
||||
#define SE_ImprovedReclaimEnergy 241 // not implemented as bonus - increase the amount of mana returned to you when reclaiming your pet.
|
||||
#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr
|
||||
#define SE_CharmBreakChance 243 // implemented - Total Domination
|
||||
#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break.
|
||||
#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
|
||||
#define SE_SetBreathLevel 246 // not implemented as bonus
|
||||
//#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
|
||||
#define SE_SetBreathLevel 246 // *not implemented as bonus
|
||||
#define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap.
|
||||
#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100)
|
||||
//#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100)
|
||||
#define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes)
|
||||
#define SE_SpellProcChance 250 // implemented - Increase chance to sympathetic proc by %
|
||||
#define SE_SpellProcChance 250 // implemented - Increase chance to proc from melee proc spells (ie Spirit of Panther)
|
||||
#define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100)
|
||||
#define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front.
|
||||
#define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage.
|
||||
#define SE_Blank 254 // implemented
|
||||
#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield
|
||||
#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs
|
||||
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
|
||||
//#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield
|
||||
//#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs
|
||||
//#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
|
||||
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
|
||||
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
|
||||
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
|
||||
#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live)
|
||||
#define SE_RaiseStatCap 262 // implemented
|
||||
#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master.
|
||||
#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect
|
||||
//#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect
|
||||
#define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled
|
||||
#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon.
|
||||
#define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast
|
||||
#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance
|
||||
//#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance
|
||||
#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound.
|
||||
#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo)
|
||||
#define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods
|
||||
@ -428,11 +428,11 @@ typedef enum {
|
||||
#define SE_FinishingBlow 278 // implemented[AA] - chance to do massive damage under 10% HP (base1 = chance, base2 = damage)
|
||||
#define SE_Flurry 279 // implemented
|
||||
#define SE_PetFlurry 280 // implemented[AA]
|
||||
#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance
|
||||
//#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance
|
||||
#define SE_ImprovedBindWound 282 // implemented[AA] - increase bind wound amount by percent.
|
||||
#define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk
|
||||
//#define SE_LoHSetHeal 284 // not used
|
||||
#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max
|
||||
//#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max
|
||||
#define SE_FcDamageAmt 286 // implemented - adds direct spell damage
|
||||
#define SE_SpellDurationIncByTic 287 // implemented
|
||||
#define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch].
|
||||
@ -453,13 +453,13 @@ typedef enum {
|
||||
#define SE_FcDamageAmtCrit 303 // implemented - adds direct spell damage
|
||||
#define SE_OffhandRiposteFail 304 // implemented as bonus - enemy cannot riposte offhand attacks
|
||||
#define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance)
|
||||
#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds
|
||||
#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor.
|
||||
//#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds
|
||||
//#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor.
|
||||
#define SE_SuspendMinion 308 // not implemented as bonus
|
||||
#define SE_YetAnotherGate 309 // implemented
|
||||
#define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point
|
||||
#define SE_ReduceReuseTimer 310 // implemented
|
||||
#define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell)
|
||||
#define SE_Sanctuary 312 // *not implemented
|
||||
//#define SE_Sanctuary 312 // *not implemented
|
||||
#define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items
|
||||
#define SE_Invisibility2 314 // implemented - fixed duration invisible
|
||||
#define SE_InvisVsUndead2 315 // implemented - fixed duration ITU
|
||||
@ -472,14 +472,14 @@ typedef enum {
|
||||
#define SE_GateToHomeCity 322 // implemented
|
||||
#define SE_DefensiveProc 323 // implemented
|
||||
#define SE_HPToMana 324 // implemented
|
||||
#define SE_ChanceInvsBreakToAoE 325 // *not implemented[AA] - [AA Nerves of Steel] increasing chance to remain hidden when they are an indirect target of an AoE spell.
|
||||
#define SE_SpellSlotIncrease 326 // not implemented as bonus - increases your spell slot availability
|
||||
//#define SE_ChanceInvsBreakToAoE 325 // *not implemented[AA] - [AA Nerves of Steel] increasing chance to remain hidden when they are an indirect target of an AoE spell.
|
||||
#define SE_SpellSlotIncrease 326 // *not implemented as bonus - increases your spell slot availability
|
||||
#define SE_MysticalAttune 327 // implemented - increases amount of buffs that a player can have
|
||||
#define SE_DelayDeath 328 // implemented - increases how far you can fall below 0 hp before you die
|
||||
#define SE_ManaAbsorbPercentDamage 329 // implemented
|
||||
#define SE_CriticalDamageMob 330 // implemented
|
||||
#define SE_Salvage 331 // *not implemented - chance to recover items that would be destroyed in failed tradeskill combine
|
||||
#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp)
|
||||
#define SE_Salvage 331 // implemented - chance to recover items that would be destroyed in failed tradeskill combine
|
||||
//#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp)
|
||||
#define SE_EffectOnFade 333 // implemented
|
||||
#define SE_BardAEDot 334 // implemented
|
||||
#define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494)
|
||||
@ -492,15 +492,15 @@ typedef enum {
|
||||
#define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing
|
||||
#define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272)
|
||||
#define SE_ChannelChanceItems 344 // implemented[AA] - chance to not have ITEM effects interrupted when you take damage.
|
||||
#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill
|
||||
#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill
|
||||
//#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill
|
||||
//#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill
|
||||
#define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow)
|
||||
#define SE_LimitManaMin 348 // implemented
|
||||
#define SE_ShieldEquipHateMod 349 // implemented[AA] Increase melee hate when wearing a shield.
|
||||
#define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana.
|
||||
#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this
|
||||
#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755)
|
||||
#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect
|
||||
//#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this
|
||||
//#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755)
|
||||
//#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect
|
||||
//#define SE_DeactivateAllTraps 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757)
|
||||
//#define SE_LearnTrap 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758)
|
||||
//#define SE_ChangeTriggerType 356 // not used
|
||||
@ -509,17 +509,17 @@ typedef enum {
|
||||
#define SE_Invulnerabilty 359 // *not implemented - Invulnerability (Brell's Blessing)
|
||||
#define SE_SpellOnKill 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level
|
||||
#define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed
|
||||
#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank.
|
||||
#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
|
||||
//#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank.
|
||||
//#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
|
||||
#define SE_TripleAttackChance 364 // implemented
|
||||
#define SE_SpellOnKill2 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin)
|
||||
#define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA
|
||||
#define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc)
|
||||
#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2
|
||||
//#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2
|
||||
#define SE_CorruptionCounter 369 // implemented
|
||||
#define SE_ResistCorruption 370 // implemented
|
||||
#define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee'
|
||||
#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not.
|
||||
//#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not.
|
||||
#define SE_CastOnWearoff 373 // implemented - Triggers only if fades after natural duration.
|
||||
#define SE_ApplyEffect 374 // implemented
|
||||
#define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount
|
||||
@ -528,30 +528,30 @@ typedef enum {
|
||||
#define SE_SpellEffectResistChance 378 // implemented - Increase chance to resist specific spell effect (base1=value, base2=spell effect id)
|
||||
#define SE_ShadowStepDirectional 379 // implemented - handled by client
|
||||
#define SE_Knockdown 380 // implemented - small knock back(handled by client)
|
||||
#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront
|
||||
//#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront
|
||||
#define SE_NegateSpellEffect 382 // implemented - negates specific spell bonuses for duration of the debuff.
|
||||
#define SE_SympatheticProc 383 // implemented - focus on items that has chance to proc a spell when you cast
|
||||
#define SE_Leap 384 // implemented - Leap effect, ie stomping leap
|
||||
#define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG)
|
||||
#define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing
|
||||
#define SE_CastOnCure 387 // implemented - Casts a spell on the cured person
|
||||
#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA)
|
||||
//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA)
|
||||
#define SE_FcTimerRefresh 389 // implemented - Refresh spell icons
|
||||
#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited.
|
||||
//#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited.
|
||||
#define SE_LimitManaMax 391 // implemented
|
||||
#define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells
|
||||
#define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions.
|
||||
#define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions.
|
||||
#define SE_FcHealPctCritIncoming 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited]
|
||||
#define SE_FcHealAmtCrit 396 // implemented - Adds a direct healing amount to spells
|
||||
#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets.
|
||||
//#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets.
|
||||
#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets
|
||||
#define SE_FcTwincast 399 // implemented - cast 2 spells for every 1
|
||||
#define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained
|
||||
#define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained
|
||||
#define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained
|
||||
#define SE_LimitSpellClass 403 // *not implemented - unclear what this refers too (not 'right click' spell bar)
|
||||
#define SE_LimitSpellSubclass 404 // *not implemented - unclear what this refers too (not 'right click' spell bar)
|
||||
//#define SE_LimitSpellClass 403 // *not implemented - unclear what this refers too (not 'right click' spell bar)
|
||||
//#define SE_LimitSpellSubclass 404 // *not implemented - unclear what this refers too (not 'right click' spell bar)
|
||||
#define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block)
|
||||
#define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted
|
||||
#define SE_CastonFocusEffect 407 // implemented - casts a spell if focus limits are met (ie triggers when a focus effects is applied)
|
||||
@ -573,7 +573,7 @@ typedef enum {
|
||||
#define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type
|
||||
#define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace
|
||||
//#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626)
|
||||
#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window
|
||||
//#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window
|
||||
#define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt)
|
||||
#define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc
|
||||
#define SE_SkillProc2 429 // implemented - chance to proc when using a skill (most have hit limits)
|
||||
@ -584,20 +584,20 @@ typedef enum {
|
||||
#define SE_CriticalHealDecay 434 // implemented - increase critical heal chance, effect decays based on level of spell it effects.
|
||||
#define SE_CriticalRegenDecay 435 // implemented - increase critical heal over time chance, effect decays based on level of spell it effects.
|
||||
//#define SE_BeneficialCountDownHold 436 // not used ( 23491 | ABTest Buff Hold)
|
||||
#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099)
|
||||
#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750)
|
||||
#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination
|
||||
//#define SE_TeleporttoAnchor 437 // *not implemented - Teleport Guild Hall Anchor(33099)
|
||||
//#define SE_TranslocatetoAnchor 438 // *not implemented - Translocate Primary Anchor (27750)
|
||||
//#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination
|
||||
#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC
|
||||
#define SE_CancleIfMoved 441 // *not implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
|
||||
#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
|
||||
#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
#define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
#define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y
|
||||
#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs.
|
||||
//#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs.
|
||||
//#define SE_AStacker 446 // *not implementet - bufff stacking blocker ? (26219 | Qirik's Watch)
|
||||
//#define SE_BStacker 447 // *not implemented
|
||||
//#define SE_CStacker 448 // *not implemented
|
||||
//#define SE_DStacker 449 // *not implemented
|
||||
//#define SE_DotGuard 450 // *not implemented
|
||||
#define SE_MitigateDotDamage 450 // implemented DOT spell mitigation rune with max value
|
||||
#define SE_MeleeThresholdGuard 451 // implemented Partial Melee Rune that only is lowered if melee hits are over X amount of damage
|
||||
#define SE_SpellThresholdGuard 452 // implemented Partial Spell Rune that only is lowered if spell hits are over X amount of damage
|
||||
#define SE_TriggerMeleeThreshold 453 // implemented Trigger effect on X amount of melee damage taken
|
||||
@ -688,7 +688,7 @@ struct SPDat_Spell_Struct
|
||||
/* 157 */ int effectdescnum; // eqstr of effect description
|
||||
/* 158 */ //Category Desc ID 3
|
||||
/* 159 */ //bool npc_no_los;
|
||||
/* 161 */ bool not_reflectable;
|
||||
/* 161 */ bool reflectable;
|
||||
/* 162 */ int bonushate;
|
||||
/* 163 */
|
||||
/* 164 */ // for most spells this appears to mimic ResistDiff
|
||||
|
||||
@ -11,6 +11,8 @@ SET(tests_headers
|
||||
fixed_memory_variable_test.h
|
||||
ipc_mutex_test.h
|
||||
memory_mapped_file_test.h
|
||||
atobool_test.h
|
||||
hextoi_32_64_test.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers})
|
||||
|
||||
103
tests/atobool_test.h
Normal file
103
tests/atobool_test.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __EQEMU_TESTS_ATOBOOL_H
|
||||
#define __EQEMU_TESTS_ATOBOOL_H
|
||||
|
||||
#include "cppunit/cpptest.h"
|
||||
#include "../common/StringUtil.h"
|
||||
|
||||
class atoboolTest : public Test::Suite {
|
||||
typedef void(atoboolTest::*TestFunction)(void);
|
||||
public:
|
||||
atoboolTest() {
|
||||
TEST_ADD(atoboolTest::TrueTest);
|
||||
TEST_ADD(atoboolTest::FalseTest);
|
||||
TEST_ADD(atoboolTest::YesTest);
|
||||
TEST_ADD(atoboolTest::NoTest);
|
||||
TEST_ADD(atoboolTest::OnTest);
|
||||
TEST_ADD(atoboolTest::OffTest);
|
||||
TEST_ADD(atoboolTest::EnableTest);
|
||||
TEST_ADD(atoboolTest::DisableTest);
|
||||
TEST_ADD(atoboolTest::EnabledTest);
|
||||
TEST_ADD(atoboolTest::DisabledTest);
|
||||
TEST_ADD(atoboolTest::YTest);
|
||||
TEST_ADD(atoboolTest::NTest);
|
||||
TEST_ADD(atoboolTest::nullptrTest);
|
||||
}
|
||||
|
||||
~atoboolTest() {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void TrueTest() {
|
||||
TEST_ASSERT(atobool("true"));
|
||||
}
|
||||
|
||||
void FalseTest() {
|
||||
TEST_ASSERT(!atobool("false"));
|
||||
}
|
||||
|
||||
void YesTest() {
|
||||
TEST_ASSERT(atobool("yes"));
|
||||
}
|
||||
|
||||
void NoTest() {
|
||||
TEST_ASSERT(!atobool("no"));
|
||||
}
|
||||
|
||||
void OnTest() {
|
||||
TEST_ASSERT(atobool("on"));
|
||||
}
|
||||
|
||||
void OffTest() {
|
||||
TEST_ASSERT(!atobool("off"));
|
||||
}
|
||||
|
||||
void EnableTest() {
|
||||
TEST_ASSERT(atobool("enable"));
|
||||
}
|
||||
|
||||
void DisableTest() {
|
||||
TEST_ASSERT(!atobool("disable"));
|
||||
}
|
||||
|
||||
void EnabledTest() {
|
||||
TEST_ASSERT(atobool("enabled"));
|
||||
}
|
||||
|
||||
void DisabledTest() {
|
||||
TEST_ASSERT(!atobool("disabled"));
|
||||
}
|
||||
|
||||
void YTest() {
|
||||
TEST_ASSERT(atobool("y"));
|
||||
}
|
||||
|
||||
void NTest() {
|
||||
TEST_ASSERT(!atobool("n"));
|
||||
}
|
||||
|
||||
void nullptrTest() {
|
||||
TEST_ASSERT(!atobool(nullptr));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
213
tests/hextoi_32_64_test.h
Normal file
213
tests/hextoi_32_64_test.h
Normal file
@ -0,0 +1,213 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __EQEMU_TESTS_HEXTOI_32_64_H
|
||||
#define __EQEMU_TESTS_HEXTOI_32_64_H
|
||||
|
||||
#include "cppunit/cpptest.h"
|
||||
#include "../common/StringUtil.h"
|
||||
|
||||
class hextoi_32_64_Test : public Test::Suite {
|
||||
typedef void(hextoi_32_64_Test::*TestFunction)(void);
|
||||
public:
|
||||
hextoi_32_64_Test() {
|
||||
TEST_ADD(hextoi_32_64_Test::nullptr32Test);
|
||||
TEST_ADD(hextoi_32_64_Test::ShortString32Test);
|
||||
TEST_ADD(hextoi_32_64_Test::SingleDigitUpper32Test);
|
||||
TEST_ADD(hextoi_32_64_Test::SingleDigitLower32Test);
|
||||
TEST_ADD(hextoi_32_64_Test::DoubleDigitUpper32Test);
|
||||
TEST_ADD(hextoi_32_64_Test::DoubleDigitLower32Test);
|
||||
|
||||
TEST_ADD(hextoi_32_64_Test::nullptr64Test);
|
||||
TEST_ADD(hextoi_32_64_Test::ShortString64Test);
|
||||
TEST_ADD(hextoi_32_64_Test::SingleDigitUpper64Test);
|
||||
TEST_ADD(hextoi_32_64_Test::SingleDigitLower64Test);
|
||||
TEST_ADD(hextoi_32_64_Test::DoubleDigitUpper64Test);
|
||||
TEST_ADD(hextoi_32_64_Test::DoubleDigitLower64Test);
|
||||
}
|
||||
|
||||
~hextoi_32_64_Test() {
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void nullptr32Test() {
|
||||
TEST_ASSERT(hextoi(nullptr) == 0);
|
||||
}
|
||||
|
||||
void ShortString32Test() {
|
||||
// if the string is too short then it should
|
||||
// spit out a zero.
|
||||
// strings should be formatted: 0x** or 0X**
|
||||
TEST_ASSERT(hextoi("") == 0);
|
||||
TEST_ASSERT(hextoi("0") == 0);
|
||||
TEST_ASSERT(hextoi("01") == 0);
|
||||
}
|
||||
|
||||
void SingleDigitUpper32Test() {
|
||||
TEST_ASSERT(hextoi("0x0") == 0);
|
||||
TEST_ASSERT(hextoi("0x1") == 1);
|
||||
TEST_ASSERT(hextoi("0x2") == 2);
|
||||
TEST_ASSERT(hextoi("0x3") == 3);
|
||||
TEST_ASSERT(hextoi("0x4") == 4);
|
||||
TEST_ASSERT(hextoi("0x5") == 5);
|
||||
TEST_ASSERT(hextoi("0x6") == 6);
|
||||
TEST_ASSERT(hextoi("0x7") == 7);
|
||||
TEST_ASSERT(hextoi("0x8") == 8);
|
||||
TEST_ASSERT(hextoi("0x9") == 9);
|
||||
TEST_ASSERT(hextoi("0xA") == 10);
|
||||
TEST_ASSERT(hextoi("0xB") == 11);
|
||||
TEST_ASSERT(hextoi("0xC") == 12);
|
||||
TEST_ASSERT(hextoi("0xD") == 13);
|
||||
TEST_ASSERT(hextoi("0xE") == 14);
|
||||
TEST_ASSERT(hextoi("0xF") == 15);
|
||||
}
|
||||
|
||||
void SingleDigitLower32Test() {
|
||||
TEST_ASSERT(hextoi("0x0") == 0);
|
||||
TEST_ASSERT(hextoi("0x1") == 1);
|
||||
TEST_ASSERT(hextoi("0x2") == 2);
|
||||
TEST_ASSERT(hextoi("0x3") == 3);
|
||||
TEST_ASSERT(hextoi("0x4") == 4);
|
||||
TEST_ASSERT(hextoi("0x5") == 5);
|
||||
TEST_ASSERT(hextoi("0x6") == 6);
|
||||
TEST_ASSERT(hextoi("0x7") == 7);
|
||||
TEST_ASSERT(hextoi("0x8") == 8);
|
||||
TEST_ASSERT(hextoi("0x9") == 9);
|
||||
TEST_ASSERT(hextoi("0xa") == 10);
|
||||
TEST_ASSERT(hextoi("0xb") == 11);
|
||||
TEST_ASSERT(hextoi("0xc") == 12);
|
||||
TEST_ASSERT(hextoi("0xd") == 13);
|
||||
TEST_ASSERT(hextoi("0xe") == 14);
|
||||
TEST_ASSERT(hextoi("0xf") == 15);
|
||||
}
|
||||
|
||||
// A bit excessive to do an exhaustive test like this
|
||||
// but it usefully tests multi digit hex.
|
||||
void DoubleDigitUpper32Test() {
|
||||
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789ABCDEF";
|
||||
uint32 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter;
|
||||
TEST_ASSERT(hextoi(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bit excessive to do an exhaustive test like this
|
||||
// but it usefully tests multi digit hex.
|
||||
void DoubleDigitLower32Test() {
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789abcdef";
|
||||
uint32 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter;
|
||||
TEST_ASSERT(hextoi(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void nullptr64Test() {
|
||||
TEST_ASSERT(hextoi64(nullptr) == 0);
|
||||
}
|
||||
|
||||
void ShortString64Test() {
|
||||
// if the string is too short then it should
|
||||
// spit out a zero.
|
||||
// strings should be formatted: 0x** or 0X**
|
||||
TEST_ASSERT(hextoi64("") == 0);
|
||||
TEST_ASSERT(hextoi64("0") == 0);
|
||||
TEST_ASSERT(hextoi64("01") == 0);
|
||||
}
|
||||
|
||||
void SingleDigitUpper64Test() {
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789ABCDEF";
|
||||
uint64 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter;
|
||||
TEST_ASSERT(hextoi64(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
void SingleDigitLower64Test() {
|
||||
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789abcdef";
|
||||
uint64 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter;
|
||||
TEST_ASSERT(hextoi64(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
|
||||
// A bit excessive to do an exhaustive test like this
|
||||
// but it usefully tests multi digit hex.
|
||||
void DoubleDigitUpper64Test() {
|
||||
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789ABCDEF";
|
||||
uint64 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter;
|
||||
TEST_ASSERT(hextoi64(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A bit excessive to do an exhaustive test like this
|
||||
// but it usefully tests multi digit hex.
|
||||
void DoubleDigitLower64Test() {
|
||||
std::string prepend = "0x";
|
||||
std::string hexToTest;
|
||||
|
||||
std::string hexElements = "0123456789abcdef";
|
||||
uint64 value = 0;
|
||||
for (std::string::iterator firstDigitIter = hexElements.begin(); firstDigitIter != hexElements.end(); ++firstDigitIter) {
|
||||
for (std::string::iterator secondDigitIter = hexElements.begin(); secondDigitIter != hexElements.end(); ++secondDigitIter) {
|
||||
std::string hexToTest = prepend + *firstDigitIter + *secondDigitIter;
|
||||
TEST_ASSERT(hextoi64(hexToTest.c_str()) == value);
|
||||
value++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -24,6 +24,8 @@
|
||||
#include "ipc_mutex_test.h"
|
||||
#include "fixed_memory_test.h"
|
||||
#include "fixed_memory_variable_test.h"
|
||||
#include "atobool_test.h"
|
||||
#include "hextoi_32_64_test.h"
|
||||
|
||||
int main() {
|
||||
try {
|
||||
@ -34,6 +36,8 @@ int main() {
|
||||
tests.add(new IPCMutexTest());
|
||||
tests.add(new FixedMemoryHashTest());
|
||||
tests.add(new FixedMemoryVariableHashTest());
|
||||
tests.add(new atoboolTest());
|
||||
tests.add(new hextoi_32_64_Test());
|
||||
tests.run(*output, true);
|
||||
} catch(...) {
|
||||
return -1;
|
||||
|
||||
@ -9,4 +9,7 @@ What we'll do instead as follows:
|
||||
All updates will follow a specific format of YYYY_MM_DD_Desc.sql, this is so it's easy to sort.
|
||||
|
||||
So the following is a good example of what I expect to see
|
||||
2013_02_16_GitConversion.sql
|
||||
2013_02_16_GitConversion.sql
|
||||
|
||||
|
||||
The new bots/ folder contains two sub-folders named optional/ and required/ for updates.
|
||||
3
utils/sql/git/bots/README
Normal file
3
utils/sql/git/bots/README
Normal file
@ -0,0 +1,3 @@
|
||||
Use this new load_bots.sql to source bot information into your database.
|
||||
|
||||
This file now contains all of the script information so that it may be run from inside of HeidiSQL.
|
||||
21
utils/sql/git/bots/drop_bots.sql
Normal file
21
utils/sql/git/bots/drop_bots.sql
Normal file
@ -0,0 +1,21 @@
|
||||
DROP TABLE IF EXISTS `botbuffs`;
|
||||
DROP TABLE IF EXISTS `botpetinventory`;
|
||||
DROP TABLE IF EXISTS `botpetbuffs`;
|
||||
DROP TABLE IF EXISTS `botpets`;
|
||||
DROP TABLE IF EXISTS `botgroupmembers`;
|
||||
DROP TABLE IF EXISTS `botgroup`;
|
||||
DROP TABLE IF EXISTS `botgroups`;
|
||||
DROP TABLE IF EXISTS `botinventory`;
|
||||
DROP TABLE IF EXISTS `botguildmembers`;
|
||||
DROP TABLE IF EXISTS `botstances`;
|
||||
DROP TABLE IF EXISTS `bottimers`;
|
||||
DROP TABLE IF EXISTS `bots`;
|
||||
DROP VIEW IF EXISTS `vwGuildMembers`;
|
||||
DROP VIEW IF EXISTS `vwBotCharacterMobs`;
|
||||
DROP VIEW IF EXISTS `vwBotGroups`;
|
||||
|
||||
delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1;
|
||||
|
||||
delete from commands where command = 'bot';
|
||||
|
||||
update spawn2 set enabled = 0 where id in (59297,59298);
|
||||
307
utils/sql/git/bots/load_bots.sql
Normal file
307
utils/sql/git/bots/load_bots.sql
Normal file
@ -0,0 +1,307 @@
|
||||
-- This is pretty much a straight copy of the original files with fixes applied.
|
||||
-- HeidiSQL does not like sub-directory references, so this should now run from there.
|
||||
-- The 'headers' are left in place for reference only.
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botguildmembers.sql;
|
||||
CREATE TABLE IF NOT EXISTS `botguildmembers` (
|
||||
`char_id` int(11) NOT NULL default '0',
|
||||
`guild_id` mediumint(8) unsigned NOT NULL default '0',
|
||||
`rank` tinyint(3) unsigned NOT NULL default '0',
|
||||
`tribute_enable` tinyint(3) unsigned NOT NULL default '0',
|
||||
`total_tribute` int(10) unsigned NOT NULL default '0',
|
||||
`last_tribute` int(10) unsigned NOT NULL default '0',
|
||||
`banker` tinyint(3) unsigned NOT NULL default '0',
|
||||
`public_note` text NULL,
|
||||
`alt` tinyint(3) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (`char_id`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/bots.sql;
|
||||
update spawn2 set enabled = 1 where id in (59297,59298);
|
||||
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.');
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.');
|
||||
|
||||
-- this is a hack fix to maintain the original file process
|
||||
delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1;
|
||||
INSERT INTO rule_values VALUES ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs');
|
||||
|
||||
-- field count has changed
|
||||
-- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.');
|
||||
INSERT INTO `commands` VALUES ('bot', '0');
|
||||
|
||||
CREATE TABLE bots (
|
||||
`BotID` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotOwnerCharacterID` int(10) unsigned NOT NULL,
|
||||
`BotSpellsID` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`Name` varchar(64) NOT NULL,
|
||||
`LastName` varchar(32) DEFAULT NULL,
|
||||
`BotLevel` tinyint(2) unsigned NOT NULL DEFAULT '0',
|
||||
`Race` smallint(5) NOT NULL DEFAULT '0',
|
||||
`Class` tinyint(2) NOT NULL DEFAULT '0',
|
||||
`Gender` tinyint(2) NOT NULL DEFAULT '0',
|
||||
`Size` float NOT NULL DEFAULT '0',
|
||||
`Face` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinHairStyle` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinHairColor` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinEyeColor` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinEyeColor2` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinBeardColor` int(10) NOT NULL DEFAULT '1',
|
||||
`LuclinBeard` int(10) NOT NULL DEFAULT '0',
|
||||
`DrakkinHeritage` int(10) NOT NULL DEFAULT '0',
|
||||
`DrakkinTattoo` int(10) NOT NULL DEFAULT '0',
|
||||
`DrakkinDetails` int(10) NOT NULL DEFAULT '0',
|
||||
`HP` INTEGER NOT NULL DEFAULT '0',
|
||||
`Mana` INTEGER NOT NULL DEFAULT '0',
|
||||
`MR` smallint(5) NOT NULL DEFAULT '0',
|
||||
`CR` smallint(5) NOT NULL DEFAULT '0',
|
||||
`DR` smallint(5) NOT NULL DEFAULT '0',
|
||||
`FR` smallint(5) NOT NULL DEFAULT '0',
|
||||
`PR` smallint(5) NOT NULL DEFAULT '0',
|
||||
`Corrup` SMALLINT(5) NOT NULL DEFAULT '0',
|
||||
`AC` smallint(5) NOT NULL DEFAULT '0',
|
||||
`STR` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`STA` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`DEX` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`AGI` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`_INT` mediumint(8) NOT NULL DEFAULT '80',
|
||||
`WIS` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`CHA` mediumint(8) NOT NULL DEFAULT '75',
|
||||
`ATK` mediumint(9) NOT NULL DEFAULT '0',
|
||||
`BotCreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`LastSpawnDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
||||
`TotalPlayTime` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`LastZoneId` smallint(6) NOT NULL DEFAULT '0',
|
||||
`BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (`BotID`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`);
|
||||
ALTER TABLE `guild_members` DROP PRIMARY KEY;
|
||||
|
||||
DROP VIEW IF EXISTS `vwGuildMembers`;
|
||||
CREATE VIEW `vwGuildMembers` AS
|
||||
select 'C' as mobtype,
|
||||
cm.char_id,
|
||||
cm.guild_id,
|
||||
cm.rank,
|
||||
cm.tribute_enable,
|
||||
cm.total_tribute,
|
||||
cm.last_tribute,
|
||||
cm.banker,
|
||||
cm.public_note,
|
||||
cm.alt
|
||||
from guild_members as cm
|
||||
union all
|
||||
select 'B' as mobtype,
|
||||
bm.char_id,
|
||||
bm.guild_id,
|
||||
bm.rank,
|
||||
bm.tribute_enable,
|
||||
bm.total_tribute,
|
||||
bm.last_tribute,
|
||||
bm.banker,
|
||||
bm.public_note,
|
||||
bm.alt
|
||||
from botguildmembers as bm;
|
||||
|
||||
DROP VIEW IF EXISTS `vwBotCharacterMobs`;
|
||||
CREATE VIEW `vwBotCharacterMobs` AS
|
||||
select 'C' as mobtype,
|
||||
c.id,
|
||||
c.name,
|
||||
c.class,
|
||||
c.level,
|
||||
c.timelaston,
|
||||
c.zoneid
|
||||
from character_ as c
|
||||
union all
|
||||
select 'B' as mobtype,
|
||||
b.BotID as id,
|
||||
b.Name as name,
|
||||
b.Class as class,
|
||||
b.BotLevel as level,
|
||||
0 as timelaston,
|
||||
0 as zoneid
|
||||
from bots as b;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botpetstatepersists.sql;
|
||||
DROP TABLE IF EXISTS `botpetinventory`;
|
||||
DROP TABLE IF EXISTS `botpetbuffs`;
|
||||
DROP TABLE IF EXISTS `botpets`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `botpets` (
|
||||
`BotPetsId` integer unsigned NOT NULL AUTO_INCREMENT,
|
||||
`PetId` integer unsigned NOT NULL DEFAULT '0',
|
||||
`BotId` integer unsigned NOT NULL DEFAULT '0',
|
||||
`Name` varchar(64) NULL,
|
||||
`Mana` integer NOT NULL DEFAULT '0',
|
||||
`HitPoints` integer NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`BotPetsId`),
|
||||
KEY `FK_botpets_1` (`BotId`),
|
||||
CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`),
|
||||
CONSTRAINT `U_botpets_1` UNIQUE (`BotId`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `botpetbuffs` (
|
||||
`BotPetBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotPetsId` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`SpellId` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`CasterLevel` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`Duration` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`BotPetBuffId`),
|
||||
KEY `FK_botpetbuffs_1` (`BotPetsId`),
|
||||
CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `botpetinventory` (
|
||||
`BotPetInventoryId` integer unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotPetsId` integer unsigned NOT NULL DEFAULT '0',
|
||||
`ItemId` integer unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`BotPetInventoryId`),
|
||||
KEY `FK_botpetinventory_1` (`BotPetsId`),
|
||||
CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botinventory.sql;
|
||||
CREATE TABLE IF NOT EXISTS botinventory (
|
||||
BotInventoryID integer unsigned NOT NULL auto_increment,
|
||||
BotID integer unsigned NOT NULL DEFAULT '0',
|
||||
SlotID integer signed NOT NULL DEFAULT '0',
|
||||
ItemID integer unsigned NOT NULL DEFAULT '0',
|
||||
charges tinyint(3) unsigned DEFAULT 0,
|
||||
color integer unsigned NOT NULL DEFAULT 0,
|
||||
augslot1 mediumint(7) unsigned NOT NULL DEFAULT 0,
|
||||
augslot2 mediumint(7) unsigned NOT NULL DEFAULT 0,
|
||||
augslot3 mediumint(7) unsigned NOT NULL DEFAULT 0,
|
||||
augslot4 mediumint(7) unsigned NOT NULL DEFAULT 0,
|
||||
augslot5 mediumint(7) unsigned DEFAULT 0,
|
||||
instnodrop tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (BotInventoryID),
|
||||
KEY FK_botinventory_1 (BotID),
|
||||
CONSTRAINT FK_botinventory_1 FOREIGN KEY (BotID) REFERENCES bots (BotID)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botbuffs.sql;
|
||||
DROP TABLE IF EXISTS `botbuffs`;
|
||||
CREATE TABLE `botbuffs` (
|
||||
`BotBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotId` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`SpellId` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`CasterLevel` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`DurationFormula` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`TicsRemaining` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`PoisonCounters` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`DiseaseCounters` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`CurseCounters` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`HitCount` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`MeleeRune` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`MagicRune` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`DeathSaveSuccessChance` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`CasterAARank` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`Persistent` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`BotBuffId`),
|
||||
KEY `FK_botbuff_1` (`BotId`),
|
||||
CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botadventuring.sql;
|
||||
DELIMITER $$
|
||||
|
||||
DROP FUNCTION IF EXISTS `GetMobType` $$
|
||||
CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1)
|
||||
BEGIN
|
||||
DECLARE Result CHAR(1);
|
||||
|
||||
SET Result = NULL;
|
||||
|
||||
IF (select count(*) from character_ where name = mobname) > 0 THEN
|
||||
SET Result = 'C';
|
||||
ELSEIF (select count(*) from bots where Name = mobname) > 0 THEN
|
||||
SET Result = 'B';
|
||||
END IF;
|
||||
|
||||
RETURN Result;
|
||||
END $$
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
DROP VIEW IF EXISTS `vwGroups`;
|
||||
CREATE VIEW `vwGroups` AS
|
||||
select g.groupid as groupid,
|
||||
GetMobType(g.name) as mobtype,
|
||||
g.name as name,
|
||||
g.charid as mobid,
|
||||
ifnull(c.level, b.BotLevel) as level
|
||||
from group_id as g
|
||||
left join character_ as c on g.name = c.name
|
||||
left join bots as b on g.name = b.Name;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botgroups.sql;
|
||||
DROP TABLE IF EXISTS `botgroupmembers`;
|
||||
DROP TABLE IF EXISTS `botgroup`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `botgroup` (
|
||||
`BotGroupId` integer unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotGroupLeaderBotId` integer unsigned NOT NULL DEFAULT '0',
|
||||
`BotGroupName` varchar(64) NOT NULL,
|
||||
PRIMARY KEY (`BotGroupId`),
|
||||
KEY FK_botgroup_1 (BotGroupLeaderBotId),
|
||||
CONSTRAINT FK_botgroup_1 FOREIGN KEY (BotGroupLeaderBotId) REFERENCES bots (BotID)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `botgroupmembers` (
|
||||
`BotGroupMemberId` integer unsigned NOT NULL AUTO_INCREMENT,
|
||||
`BotGroupId` integer unsigned NOT NULL DEFAULT '0',
|
||||
`BotId` integer unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`BotGroupMemberId`),
|
||||
KEY FK_botgroupmembers_1 (BotGroupId),
|
||||
CONSTRAINT FK_botgroupmembers_1 FOREIGN KEY (BotGroupId) REFERENCES botgroup (BotGroupId),
|
||||
KEY FK_botgroupmembers_2 (BotId),
|
||||
CONSTRAINT FK_botgroupmembers_2 FOREIGN KEY (BotId) REFERENCES bots (BotID)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
DROP VIEW IF EXISTS `vwBotGroups`;
|
||||
CREATE VIEW `vwBotGroups` AS
|
||||
select g.BotGroupId,
|
||||
g.BotGroupName,
|
||||
g.BotGroupLeaderBotId,
|
||||
b.Name as BotGroupLeaderName,
|
||||
b.BotOwnerCharacterId,
|
||||
c.name as BotOwnerCharacterName
|
||||
from botgroup as g
|
||||
join bots as b on g.BotGroupLeaderBotId = b.BotID
|
||||
join character_ as c on b.BotOwnerCharacterID = c.id
|
||||
order by b.BotOwnerCharacterId, g.BotGroupName;
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/botstances.sql;
|
||||
CREATE TABLE botstances (
|
||||
BotID int(10) unsigned NOT NULL default '0',
|
||||
StanceID tinyint unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (BotID),
|
||||
CONSTRAINT FK_botstances_1 FOREIGN KEY (BotID) REFERENCES bots (BotID)
|
||||
);
|
||||
|
||||
-- FILE:
|
||||
-- source player_tables/bottimers.sql;
|
||||
CREATE TABLE bottimers (
|
||||
BotID int(10) unsigned NOT NULL default '0',
|
||||
TimerID int(10) unsigned NOT NULL default '0',
|
||||
Value int(10) unsigned NOT NULL default '0',
|
||||
PRIMARY KEY (BotID),
|
||||
CONSTRAINT FK_bottimers_1 FOREIGN KEY (BotID) REFERENCES bots (BotID)
|
||||
)
|
||||
@ -0,0 +1,4 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (2, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (4, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (10, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
|
||||
@ -0,0 +1,27 @@
|
||||
ALTER TABLE `botguildmembers` ADD `alt` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `public_note`;
|
||||
|
||||
DROP VIEW IF EXISTS `vwGuildMembers`;
|
||||
CREATE VIEW `vwGuildMembers` AS
|
||||
select 'C' as mobtype,
|
||||
cm.char_id,
|
||||
cm.guild_id,
|
||||
cm.rank,
|
||||
cm.tribute_enable,
|
||||
cm.total_tribute,
|
||||
cm.last_tribute,
|
||||
cm.banker,
|
||||
cm.public_note,
|
||||
cm.alt
|
||||
from guild_members as cm
|
||||
union all
|
||||
select 'B' as mobtype,
|
||||
bm.char_id,
|
||||
bm.guild_id,
|
||||
bm.rank,
|
||||
bm.tribute_enable,
|
||||
bm.total_tribute,
|
||||
bm.last_tribute,
|
||||
bm.banker,
|
||||
bm.public_note,
|
||||
bm.alt
|
||||
from botguildmembers as bm;
|
||||
2
utils/sql/git/optional/2014_03_02_CharmRules.sql
Normal file
2
utils/sql/git/optional/2014_03_02_CharmRules.sql
Normal file
@ -0,0 +1,2 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharismaCharmDuration', 'false', 'Allow CHA to extend charm duration.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharismaResistCap', '200', 'Maximium amount of CHA that will effect charm resist rate.');
|
||||
1
utils/sql/git/optional/2014_03_04_RootRule.sql
Normal file
1
utils/sql/git/optional/2014_03_04_RootRule.sql
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:RootBreakCheckChance', '40', 'Chance for root to do a resist check each tick. Decrease for longer roots.');
|
||||
@ -0,0 +1,3 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentRestriction', 'false', 'Forces augment slot restrictions.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentUsability', 'false', 'Forces augmented item usability.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.');
|
||||
6
utils/sql/git/optional/2014_03_19_RulesUpdates.sql
Normal file
6
utils/sql/git/optional/2014_03_19_RulesUpdates.sql
Normal file
@ -0,0 +1,6 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FearBreakCheckChance', '70', 'Chance for fear to do a resist check each tick. Decrease for longer fears.');
|
||||
|
||||
-- Updates rule value if server is using the OLD DEFAULT values
|
||||
UPDATE rule_values SET rule_value = 55 WHERE rule_name LIKE 'Spells:RootBreakFromSpells' AND rule_value = 20;
|
||||
UPDATE rule_values SET rule_value = 70 WHERE rule_name LIKE 'Spells:RootBreakCheckChance' AND rule_value = 40;
|
||||
UPDATE rule_values SET rule_value = 255 WHERE rule_name LIKE 'Spells:CharismaResistCap' AND rule_value = 200;
|
||||
1
utils/sql/git/optional/2014_03_27_SuccorFailRule.sql
Normal file
1
utils/sql/git/optional/2014_03_27_SuccorFailRule.sql
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SuccorFailChance', '2', 'Determines chance for a succor spell not to teleport an invidual player.');
|
||||
@ -0,0 +1,3 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_Titanium', '1113', 'Item id for Titanium clients for Fire spell projectile.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.');
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.');
|
||||
1
utils/sql/git/optional/2014_04_05_ProcRules.sql
Normal file
1
utils/sql/git/optional/2014_04_05_ProcRules.sql
Normal file
@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:OneProcPerWeapon', 'true', 'If OneProcPerWeapon is not enabled, we reset the proc try for that weapon regardless of if we procced or not.');
|
||||
@ -7,8 +7,8 @@ REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES
|
||||
UPDATE aa_effects SET base2 = 100 WHERE effectid = 294 AND base2 = 0;
|
||||
|
||||
-- Consumption of Soul (Need live values - These are what had hard coded, but using spell effect)
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('589', '1', '303', '200', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('589', '2', '139', '2766', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('559', '1', '303', '200', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('559', '2', '139', '2766', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('560', '1', '303', '400', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('560', '2', '139', '2766', '0');
|
||||
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('561', '1', '303', '600', '0');
|
||||
|
||||
22
utils/sql/git/required/2014_02_20_buff_update.sql
Normal file
22
utils/sql/git/required/2014_02_20_buff_update.sql
Normal file
@ -0,0 +1,22 @@
|
||||
-- UPDATE BUFF TABLES
|
||||
ALTER TABLE `character_buffs` CHANGE `death_save_chance` `dot_rune` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `merc_buffs` CHANGE `DeathSaveSuccessChance` `dot_rune` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `botbuffs` CHANGE `DeathSaveSuccessChance` `dot_rune` INT(10) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `character_buffs` CHANGE `death_save_aa_chance` `caston_x` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `merc_buffs` CHANGE `CasterAARank` `caston_x` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `botbuffs` CHANGE `CasterAARank` `caston_x` INT(10) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `character_buffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `merc_buffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `botbuffs` ADD `caston_y` INT(10) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `character_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `merc_buffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `botbuffs` ADD `caston_z` INT(10) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `character_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `merc_buffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0';
|
||||
ALTER TABLE `botbuffs` ADD `ExtraDIChance` INT(10) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `spells_new` CHANGE `not_reflectable` `reflectable` INT(11) NOT NULL DEFAULT '0';
|
||||
2
utils/sql/git/required/2014_02_26_roambox_update.sql
Normal file
2
utils/sql/git/required/2014_02_26_roambox_update.sql
Normal file
@ -0,0 +1,2 @@
|
||||
alter table `spawngroup` add column `mindelay` int(11) not null default 15000 AFTER delay;
|
||||
alter table `spawngroup` change `delay` `delay` int(11) not null default 45000;
|
||||
8
utils/sql/git/required/2014_02_26_virulentvenomAA.sql
Normal file
8
utils/sql/git/required/2014_02_26_virulentvenomAA.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- Virulent Venom
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('888', '1', '250', '10', '0');
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('889', '1', '250', '20', '0');
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('890', '1', '250', '30', '0');
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('891', '1', '250', '40', '0');
|
||||
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('892', '1', '250', '50', '0');
|
||||
|
||||
|
||||
7
utils/sql/git/required/2014_04_04_PhysicalResist.sql
Normal file
7
utils/sql/git/required/2014_04_04_PhysicalResist.sql
Normal file
@ -0,0 +1,7 @@
|
||||
ALTER TABLE `npc_types` ADD `PhR` smallint( 5 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `Corrup`;
|
||||
|
||||
-- Approximate baseline live npc values based on extensive parsing.
|
||||
UPDATE npc_types SET PhR = 10 WHERE PhR = 0 AND level <= 50;
|
||||
UPDATE npc_types SET PhR = (10 + (level - 50)) WHERE PhR = 0 AND (level > 50 AND level <= 60);
|
||||
UPDATE npc_types SET PhR = (20 + ((level - 60)*4)) WHERE PhR = 0 AND level > 60;
|
||||
|
||||
@ -461,7 +461,7 @@ int main(int argc, char** argv) {
|
||||
if (InterserverTimer.Check()) {
|
||||
InterserverTimer.Start();
|
||||
database.ping();
|
||||
AsyncLoadVariables(dbasync, &database);
|
||||
// AsyncLoadVariables(dbasync, &database);
|
||||
ReconnectCounter++;
|
||||
if (ReconnectCounter >= 12) { // only create thread to reconnect every 10 minutes. previously we were creating a new thread every 10 seconds
|
||||
ReconnectCounter = 0;
|
||||
|
||||
@ -94,7 +94,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range
|
||||
)
|
||||
&& (mana_cost <= GetMana() || GetMana() == GetMaxMana())
|
||||
&& (AIspells[i].time_cancast+(MakeRandomInt(0, 4))) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
|
||||
&& (AIspells[i].time_cancast + (MakeRandomInt(0, 4) * 1000)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
|
||||
) {
|
||||
|
||||
#if MobAI_DEBUG_Spells >= 21
|
||||
@ -125,21 +125,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
break;
|
||||
}
|
||||
case SpellType_Root: {
|
||||
if (
|
||||
!tar->IsRooted()
|
||||
&& dist2 >= 900
|
||||
&& MakeRandomInt(0, 99) < 50
|
||||
&& tar->DontRootMeBefore() < Timer::GetCurrentTime()
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
Mob *rootee = GetHateRandom();
|
||||
if (rootee && !rootee->IsRooted() && MakeRandomInt(0, 99) < 50
|
||||
&& rootee->DontRootMeBefore() < Timer::GetCurrentTime()
|
||||
&& rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
if(!checked_los) {
|
||||
if(!CheckLosFN(tar))
|
||||
if(!CheckLosFN(rootee))
|
||||
return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
|
||||
checked_los = true;
|
||||
}
|
||||
uint32 tempTime = 0;
|
||||
AIDoSpellCast(i, tar, mana_cost, &tempTime);
|
||||
tar->SetDontRootMeBefore(tempTime);
|
||||
AIDoSpellCast(i, rootee, mana_cost, &tempTime);
|
||||
rootee->SetDontRootMeBefore(tempTime);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@ -167,7 +165,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
}
|
||||
|
||||
case SpellType_InCombatBuff: {
|
||||
if(MakeRandomInt(0,100) < 50)
|
||||
if(MakeRandomInt(0, 99) < 50)
|
||||
{
|
||||
AIDoSpellCast(i, tar, mana_cost);
|
||||
return true;
|
||||
@ -184,7 +182,20 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
break;
|
||||
}
|
||||
case SpellType_Slow:
|
||||
case SpellType_Debuff:
|
||||
case SpellType_Debuff: {
|
||||
Mob * debuffee = GetHateRandom();
|
||||
if (debuffee && manaR >= 10 && MakeRandomInt(0, 99 < 70) &&
|
||||
debuffee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) {
|
||||
if (!checked_los) {
|
||||
if (!CheckLosFN(debuffee))
|
||||
return false;
|
||||
checked_los = true;
|
||||
}
|
||||
AIDoSpellCast(i, debuffee, mana_cost);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SpellType_Nuke: {
|
||||
if (
|
||||
manaR >= 10 && MakeRandomInt(0, 99) < 70
|
||||
@ -201,7 +212,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
break;
|
||||
}
|
||||
case SpellType_Dispel: {
|
||||
if(MakeRandomInt(0, 100) < 15)
|
||||
if(MakeRandomInt(0, 99) < 15)
|
||||
{
|
||||
if(!checked_los) {
|
||||
if(!CheckLosFN(tar))
|
||||
@ -444,6 +455,7 @@ void NPC::AI_Init() {
|
||||
roambox_distance = 0;
|
||||
roambox_movingto_x = 0;
|
||||
roambox_movingto_y = 0;
|
||||
roambox_min_delay = 2500;
|
||||
roambox_delay = 2500;
|
||||
}
|
||||
|
||||
@ -1579,14 +1591,17 @@ void NPC::AI_DoMovement() {
|
||||
movey *= MakeRandomInt(0, 1) ? 1 : -1;
|
||||
roambox_movingto_x = GetX() + movex;
|
||||
roambox_movingto_y = GetY() + movey;
|
||||
//Try to calculate new coord using distance.
|
||||
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
|
||||
roambox_movingto_x -= movex * 2;
|
||||
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
|
||||
roambox_movingto_y -= movey * 2;
|
||||
//New coord is still invalid, ignore distance and just pick a new random coord.
|
||||
//If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc.
|
||||
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
|
||||
roambox_movingto_x = roambox_max_x;
|
||||
roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1);
|
||||
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
|
||||
roambox_movingto_y = roambox_max_y;
|
||||
roambox_movingto_y = MakeRandomFloat(roambox_min_y+1,roambox_max_y-1);
|
||||
}
|
||||
|
||||
mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
|
||||
@ -1594,7 +1609,7 @@ void NPC::AI_DoMovement() {
|
||||
if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true))
|
||||
{
|
||||
roambox_movingto_x = roambox_max_x + 1; // force update
|
||||
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_delay, roambox_delay + 5000);
|
||||
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
|
||||
SetMoving(false);
|
||||
SendPosition(); // makes mobs stop clientside
|
||||
}
|
||||
@ -1870,7 +1885,7 @@ bool NPC::AI_EngagedCastCheck() {
|
||||
// try casting a heal on nearby
|
||||
if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) {
|
||||
//nobody to heal, try some detrimental spells.
|
||||
if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm)) {
|
||||
if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||
//no spell to cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(500, 1000), false);
|
||||
}
|
||||
|
||||
@ -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.
|
||||
@ -99,6 +100,12 @@
|
||||
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
|
||||
#define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item.
|
||||
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new!
|
||||
#define EVADE_SUCCESS 343 //You have momentarily ducked away from the main combat.
|
||||
#define EVADE_FAIL 344 //Your attempts at ducking clear of combat fail.
|
||||
#define HIDE_FAIL 345 //You failed to hide yourself.
|
||||
#define HIDE_SUCCESS 346 //You have hidden yourself from view.
|
||||
#define SNEAK_SUCCESS 347 //You are as quiet as a cat stalking its prey.
|
||||
#define SNEAK_FAIL 348 //You are as quiet as a herd of running elephants.
|
||||
#define MEND_CRITICAL 349 //You magically mend your wounds and heal considerable damage.
|
||||
#define MEND_SUCCESS 350 //You mend your wounds and heal some damage.
|
||||
#define MEND_WORSEN 351 //You have worsened your wounds!
|
||||
@ -125,6 +132,8 @@
|
||||
#define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected.
|
||||
#define TARGET_RESISTED 425 //Your target resisted the %1 spell.
|
||||
#define YOU_RESIST 426 //You resist the %1 spell!
|
||||
#define YOU_CRIT_HEAL 427 //You perform an exceptional heal! (%1)
|
||||
#define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1)
|
||||
#define SUMMONING_CORPSE 429 //Summoning your corpse.
|
||||
#define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse.
|
||||
#define MISSING_SPELL_COMP_ITEM 433 //You are missing %1.
|
||||
@ -158,6 +167,7 @@
|
||||
#define ASSASSINATES 1016 //%1 ASSASSINATES their victim!!
|
||||
#define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2)
|
||||
#define CRITICAL_HIT 1023 //%1 scores a critical hit! (%2)
|
||||
#define DEADLY_STRIKE 1024 //%1 scores a Deadly Strike!(%2)
|
||||
#define RESISTS_URGE 1025 //%1 resists their urge to flee.
|
||||
#define BERSERK_START 1027 //%1 goes into a berserker frenzy!
|
||||
#define DEATH_PACT 1028 //%1's death pact has been benevolently fulfilled!
|
||||
@ -168,6 +178,8 @@
|
||||
#define OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting.
|
||||
#define GENERIC_SHOUT 1034 //%1 shouts '%2'
|
||||
#define GENERIC_EMOTE 1036 //%1 %2
|
||||
#define OTHER_CRIT_HEAL 1039 //%1 performs an exceptional heal! (%2)
|
||||
#define OTHER_CRIT_BLAST 1040 //%1 delivers a critical blast! (%2)
|
||||
#define NPC_ENRAGE_START 1042 //%1 has become ENRAGED.
|
||||
#define NPC_ENRAGE_END 1043 //%1 is no longer enraged.
|
||||
#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE!
|
||||
@ -197,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.
|
||||
@ -222,6 +235,9 @@
|
||||
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
|
||||
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
|
||||
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
|
||||
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
|
||||
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
|
||||
#define END_SHIELDING 3282 //%1 ceases protecting %2.
|
||||
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.
|
||||
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
|
||||
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
|
||||
@ -239,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.
|
||||
@ -283,7 +301,7 @@
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
||||
#define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell.
|
||||
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
|
||||
#define OTHER_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
|
||||
#define SHAKE_OFF_STUN 9077
|
||||
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
||||
@ -316,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.
|
||||
@ -346,10 +365,14 @@
|
||||
#define NOW_INVISIBLE 12950 //%1 is now Invisible.
|
||||
#define NOW_VISIBLE 12951 //%1 is now Visible.
|
||||
#define GUILD_NOT_MEMBER2 12966 //You are not in a guild.
|
||||
#define HOT_HEAL_SELF 12976 //You have been healed for %1 hit points by your %2.
|
||||
#define HOT_HEAL_OTHER 12997 //You have healed %1 for %2 hit points with your %3.
|
||||
#define HOT_HEALED_OTHER 12998 //%1 healed you for %2 hit points by %3.
|
||||
#define DISC_LEVEL_USE_ERROR 13004 //You are not sufficient level to use this discipline.
|
||||
#define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells.
|
||||
#define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you.
|
||||
#define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog.
|
||||
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4.
|
||||
#define GENERIC_MISS 15041 //%1 missed %2
|
||||
|
||||
#endif
|
||||
|
||||
@ -513,6 +513,14 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
else if(our_owner && our_owner == target)
|
||||
return false;
|
||||
|
||||
// invalidate for swarm pets for later on if their owner is a corpse
|
||||
if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner &&
|
||||
our_owner->IsCorpse() && !our_owner->IsPlayerCorpse())
|
||||
our_owner = nullptr;
|
||||
if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner &&
|
||||
target_owner->IsCorpse() && !target_owner->IsPlayerCorpse())
|
||||
target_owner = nullptr;
|
||||
|
||||
//cannot hurt untargetable mobs
|
||||
bodyType bt = target->GetBodyType();
|
||||
|
||||
@ -1388,14 +1396,21 @@ void Mob::ClearFeignMemory() {
|
||||
|
||||
bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
|
||||
/*
|
||||
Charm formula is correct based on over 50 hours of personal live parsing - Kayen
|
||||
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 CHA)
|
||||
Charisma DOES NOT extend charm durations.
|
||||
Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything)
|
||||
Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference.
|
||||
*/
|
||||
|
||||
if(!caster) return false;
|
||||
|
||||
if(spells[spell_id].ResistDiff <= -600)
|
||||
return true;
|
||||
|
||||
//Applies additional Charisma bonus to resist rate
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,1);
|
||||
|
||||
float resist_check = 0;
|
||||
|
||||
if(IsCharmSpell(spell_id)) {
|
||||
|
||||
if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration.
|
||||
@ -1405,8 +1420,13 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
if (MakeRandomInt(0, 99) > RuleI(Spells, CharmBreakCheckChance))
|
||||
return true;
|
||||
|
||||
if (RuleB(Spells, CharismaCharmDuration))
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,true,true);
|
||||
else
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0, false, true);
|
||||
|
||||
//2: The mob makes a resistance check against the charm
|
||||
if (resist_check == 100)
|
||||
if (resist_check == 100)
|
||||
return true;
|
||||
|
||||
else
|
||||
@ -1414,7 +1434,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
if (caster->IsClient())
|
||||
{
|
||||
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred.
|
||||
uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
|
||||
int16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
|
||||
|
||||
if (MakeRandomInt(0, 99) < TotalDominationBonus)
|
||||
return true;
|
||||
@ -1426,6 +1446,9 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
else
|
||||
{
|
||||
// Assume this is a harmony/pacify spell
|
||||
// If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro.
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, true);
|
||||
|
||||
if (resist_check == 100)
|
||||
return true;
|
||||
}
|
||||
@ -1433,3 +1456,11 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mob::RogueEvade(Mob *other)
|
||||
{
|
||||
int amount = other->GetHateAmount(this) - (GetLevel() * 13);
|
||||
other->SetHate(this, std::max(1, amount));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
564
zone/attack.cpp
564
zone/attack.cpp
@ -91,7 +91,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w
|
||||
case ItemType2HBlunt: // 2H Blunt
|
||||
{
|
||||
skillinuse = Skill2HBlunt;
|
||||
type = anim2HWeapon;
|
||||
type = anim2HSlashing; //anim2HWeapon
|
||||
break;
|
||||
}
|
||||
case ItemType2HPiercing: // 2H Piercing
|
||||
@ -140,7 +140,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w
|
||||
}
|
||||
case Skill2HBlunt: // 2H Blunt
|
||||
{
|
||||
type = anim2HWeapon;
|
||||
type = anim2HSlashing; //anim2HWeapon
|
||||
break;
|
||||
}
|
||||
case 99: // 2H Piercing // change to Skill2HPiercing once activated
|
||||
@ -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));
|
||||
@ -2004,6 +2010,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
|
||||
if(!bRiposte && other->GetHP() > 0 ) {
|
||||
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
|
||||
TrySpellProc(nullptr, weapon, other, Hand);
|
||||
}
|
||||
|
||||
TriggerDefensiveProcs(nullptr, other, Hand, damage);
|
||||
@ -3187,7 +3194,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
//UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3212,7 +3218,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3220,7 +3225,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
" damage remaining.", damage_to_reduce, buffs[slot].melee_rune);
|
||||
buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce);
|
||||
damage -= damage_to_reduce;
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3242,7 +3246,7 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
if(damage < 1)
|
||||
return -6;
|
||||
|
||||
if (HasRune())
|
||||
if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0)
|
||||
damage = RuneAbsorb(damage, SE_Rune);
|
||||
|
||||
if(damage < 1)
|
||||
@ -3273,8 +3277,28 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
}
|
||||
|
||||
// If this is a DoT, use DoT Shielding...
|
||||
if(iBuffTic)
|
||||
damage -= (damage * itembonuses.DoTShielding / 100);
|
||||
if(iBuffTic) {
|
||||
damage -= (damage * itembonuses.DoTShielding / 100);
|
||||
|
||||
if (spellbonuses.MitigateDotRune[0]){
|
||||
slot = spellbonuses.MitigateDotRune[1];
|
||||
if(slot >= 0)
|
||||
{
|
||||
int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100;
|
||||
if(damage_to_reduce > buffs[slot].dot_rune)
|
||||
{
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce);
|
||||
damage -= damage_to_reduce;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This must be a DD then so lets apply Spell Shielding and runes.
|
||||
else
|
||||
@ -3296,7 +3320,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
//UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3320,7 +3343,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3328,7 +3350,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
" damage remaining.", damage_to_reduce, buffs[slot].magic_rune);
|
||||
buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce);
|
||||
damage -= damage_to_reduce;
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3342,7 +3363,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
else{
|
||||
buffs[slot].melee_rune = (buffs[slot].magic_rune - damage);
|
||||
buffs[slot].magic_rune = (buffs[slot].magic_rune - damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3351,7 +3372,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
return 0;
|
||||
|
||||
|
||||
if (HasSpellRune())
|
||||
if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0)
|
||||
damage = RuneAbsorb(damage, SE_AbsorbMagicAtt);
|
||||
|
||||
if(damage < 1)
|
||||
@ -3664,29 +3685,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
|
||||
if(spell_id != SPELL_UNKNOWN && !iBuffTic) {
|
||||
//see if root will break
|
||||
if (IsRooted() && !FromDamageShield) { // neotoyko: only spells cancel root
|
||||
|
||||
/*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443
|
||||
The Viscid Roots AA does the following: Reduces the chance for root to break by X percent.
|
||||
There is no distinction of any kind between the caster inflicted damage, or anyone
|
||||
else's damage. There is also no distinction between Direct and DOT damage in the root code.
|
||||
There is however, a provision that if the damage inflicted is greater than 500 per hit, the
|
||||
chance to break root is increased. My guess is when this code was put in place, the devs at
|
||||
the time couldn't imagine DOT damage getting that high.
|
||||
*/
|
||||
int BreakChance = RuleI(Spells, RootBreakFromSpells);
|
||||
BreakChance -= BreakChance*rooted_mod/100;
|
||||
|
||||
if (BreakChance < 1)
|
||||
BreakChance = 1;
|
||||
|
||||
if (MakeRandomInt(0, 99) < BreakChance) {
|
||||
mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance");
|
||||
BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself
|
||||
} else {
|
||||
mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance");
|
||||
}
|
||||
}
|
||||
if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root
|
||||
TryRootFadeByDamage(buffslot, attacker);
|
||||
}
|
||||
else if(spell_id == SPELL_UNKNOWN)
|
||||
{
|
||||
@ -3808,76 +3808,96 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
// Everhood - So we can see our dot dmg like live shows it.
|
||||
if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) {
|
||||
//might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it
|
||||
if(attacker->CastToClient()->GetFilter(FilterDOT) != FilterHide) {
|
||||
attacker->Message_StringID(MT_DoTDamage, OTHER_HIT_DOT, GetCleanName(),itoa(damage),spells[spell_id].name);
|
||||
}
|
||||
attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT,
|
||||
YOUR_HIT_DOT, GetCleanName(), itoa(damage), spells[spell_id].name);
|
||||
// older clients don't have the below String ID, but it will be filtered
|
||||
entity_list.FilteredMessageClose_StringID(attacker, true, 200,
|
||||
MT_DoTDamage, FilterDOT, OTHER_HIT_DOT, GetCleanName(),
|
||||
itoa(damage), attacker->GetCleanName(), spells[spell_id].name);
|
||||
}
|
||||
} //end packet sending
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Mob::HealDamage(uint32 amount, Mob* caster) {
|
||||
void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
|
||||
{
|
||||
int32 maxhp = GetMaxHP();
|
||||
int32 curhp = GetHP();
|
||||
uint32 acthealed = 0;
|
||||
|
||||
if(caster && amount > 0)
|
||||
{
|
||||
if(caster->IsNPC() && !caster->IsPet())
|
||||
{
|
||||
if (caster && amount > 0) {
|
||||
if (caster->IsNPC() && !caster->IsPet()) {
|
||||
float npchealscale = caster->CastToNPC()->GetHealScale();
|
||||
amount = ((float)amount * npchealscale) / (float)100;
|
||||
amount = (static_cast<float>(amount) * npchealscale) / 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(amount > (maxhp - curhp))
|
||||
if (amount > (maxhp - curhp))
|
||||
acthealed = (maxhp - curhp);
|
||||
else
|
||||
acthealed = amount;
|
||||
|
||||
char *TempString = nullptr;
|
||||
|
||||
MakeAnyLenString(&TempString, "%d", acthealed);
|
||||
|
||||
if(acthealed > 100){
|
||||
if(caster){
|
||||
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), TempString);
|
||||
if(caster != this){
|
||||
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), TempString);
|
||||
if (acthealed > 100) {
|
||||
if (caster) {
|
||||
if (IsBuffSpell(spell_id)) { // hots
|
||||
// message to caster
|
||||
if (caster->IsClient() && caster == this) {
|
||||
if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name);
|
||||
else
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEALED, GetCleanName(), itoa(acthealed));
|
||||
} else if (caster->IsClient() && caster != this) {
|
||||
if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed),
|
||||
spells[spell_id].name);
|
||||
else
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
// message to target
|
||||
if (IsClient() && caster != this) {
|
||||
if (CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEALED_OTHER, caster->GetCleanName(),
|
||||
itoa(acthealed), spells[spell_id].name);
|
||||
else
|
||||
FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
} else { // normal heals
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage,
|
||||
YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
if (caster != this)
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed);
|
||||
}
|
||||
}
|
||||
|
||||
if (curhp < maxhp) {
|
||||
if ((curhp+amount)>maxhp)
|
||||
curhp=maxhp;
|
||||
if ((curhp + amount) > maxhp)
|
||||
curhp = maxhp;
|
||||
else
|
||||
curhp+=amount;
|
||||
curhp += amount;
|
||||
SetHP(curhp);
|
||||
|
||||
SendHPUpdate();
|
||||
}
|
||||
safe_delete_array(TempString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//proc chance includes proc bonus
|
||||
float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) {
|
||||
float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand)
|
||||
{
|
||||
int mydex = GetDEX();
|
||||
float AABonus = 0;
|
||||
ProcBonus = 0;
|
||||
ProcChance = 0;
|
||||
float ProcChance = 0.0f;
|
||||
|
||||
if (aabonuses.ProcChance)
|
||||
AABonus = float(aabonuses.ProcChance) / 100.0f;
|
||||
|
||||
switch(hand){
|
||||
switch (hand) {
|
||||
case 13:
|
||||
weapon_speed = attack_timer.GetDuration();
|
||||
break;
|
||||
@ -3889,24 +3909,20 @@ float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//calculate the weapon speed in ms, so we can use the rule to compare against.
|
||||
|
||||
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
|
||||
// fast as a client can swing, so should be the floor of the proc chance
|
||||
if (weapon_speed < RuleI(Combat, MinHastedDelay))
|
||||
weapon_speed = RuleI(Combat, MinHastedDelay);
|
||||
|
||||
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus);
|
||||
|
||||
if(RuleB(Combat, AdjustProcPerMinute) == true)
|
||||
{
|
||||
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
|
||||
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f;
|
||||
ProcChance = ProcChance + (ProcChance * ProcBonus);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
|
||||
ProcChance = ProcChance + (ProcChance * ProcBonus);
|
||||
if (RuleB(Combat, AdjustProcPerMinute)) {
|
||||
ProcChance = (static_cast<float>(weapon_speed) *
|
||||
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
|
||||
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
|
||||
ProcChance += ProcChance * ProcBonus / 100.0f;
|
||||
} else {
|
||||
ProcChance = RuleR(Combat, BaseProcChance) +
|
||||
static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
|
||||
ProcChance += ProcChance * ProcBonus / 100.0f;
|
||||
}
|
||||
|
||||
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus);
|
||||
@ -3930,16 +3946,6 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 w
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
float PermaHaste;
|
||||
if(GetHaste() > 0)
|
||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
||||
else if(GetHaste() < 0)
|
||||
PermaHaste = 1 * (1 - (float)GetHaste()/100);
|
||||
else
|
||||
PermaHaste = 1.0f;
|
||||
*/
|
||||
|
||||
//calculate the weapon speed in ms, so we can use the rule to compare against.
|
||||
//weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste));
|
||||
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
|
||||
@ -4019,159 +4025,185 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) {
|
||||
}
|
||||
|
||||
if(!weapon_g) {
|
||||
TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand);
|
||||
TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!weapon_g->IsType(ItemClassCommon)) {
|
||||
TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand);
|
||||
TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
|
||||
return;
|
||||
}
|
||||
|
||||
//do main procs
|
||||
// Innate + aug procs from weapons
|
||||
// TODO: powersource procs
|
||||
TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand);
|
||||
// Procs from Buffs and AA both melee and range
|
||||
TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand);
|
||||
|
||||
|
||||
//we have to calculate these again, oh well
|
||||
int ourlevel = GetLevel();
|
||||
float ProcChance, ProcBonus;
|
||||
GetProcChances(ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand);
|
||||
if(hand != 13)
|
||||
{
|
||||
ProcChance /= 2;
|
||||
}
|
||||
|
||||
//do augment procs
|
||||
int r;
|
||||
for(r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
const ItemInst* aug_i = weapon_g->GetAugment(r);
|
||||
if(!aug_i)
|
||||
continue;
|
||||
const Item_Struct* aug = aug_i->GetItem();
|
||||
if(!aug)
|
||||
continue;
|
||||
|
||||
if (aug->Proc.Type == ET_CombatProc) {
|
||||
ProcChance = ProcChance*(100+aug->ProcRate)/100;
|
||||
if (MakeRandomFloat(0, 1) < ProcChance) {
|
||||
if(aug->Proc.Level > ourlevel) {
|
||||
Mob * own = GetOwner();
|
||||
if(own != nullptr) {
|
||||
own->Message_StringID(13,PROC_PETTOOLOW);
|
||||
} else {
|
||||
Message_StringID(13,PROC_TOOLOW);
|
||||
}
|
||||
} else {
|
||||
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) {
|
||||
void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
|
||||
{
|
||||
if (!weapon)
|
||||
return;
|
||||
uint16 skillinuse = 28;
|
||||
int ourlevel = GetLevel();
|
||||
float ProcChance, ProcBonus;
|
||||
if(weapon!=nullptr)
|
||||
GetProcChances(ProcBonus, ProcChance, weapon->Delay, hand);
|
||||
else
|
||||
GetProcChances(ProcBonus, ProcChance);
|
||||
float ProcBonus = static_cast<float>(aabonuses.ProcChanceSPA +
|
||||
spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
|
||||
ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
|
||||
float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
|
||||
|
||||
if(hand != 13) //Is Archery intended to proc at 50% rate?
|
||||
if (hand != 13) //Is Archery intened to proc at 50% rate?
|
||||
ProcChance /= 2;
|
||||
|
||||
//give weapon a chance to proc first.
|
||||
if(weapon != nullptr) {
|
||||
skillinuse = GetSkillByItemType(weapon->ItemType);
|
||||
if (weapon->Proc.Type == ET_CombatProc) {
|
||||
float WPC = ProcChance*(100.0f+(float)weapon->ProcRate)/100.0f;
|
||||
if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
|
||||
if(weapon->Proc.Level > ourlevel) {
|
||||
mlog(COMBAT__PROCS, "Tried to proc (%s), but our level (%d) is lower than required (%d)", weapon->Name, ourlevel, weapon->Proc.Level);
|
||||
Mob * own = GetOwner();
|
||||
if(own != nullptr) {
|
||||
own->Message_StringID(13,PROC_PETTOOLOW);
|
||||
} else {
|
||||
Message_StringID(13,PROC_TOOLOW);
|
||||
}
|
||||
// Try innate proc on weapon
|
||||
// We can proc once here, either weapon or one aug
|
||||
bool proced = false; // silly bool to prevent augs from going if weapon does
|
||||
skillinuse = GetSkillByItemType(weapon->ItemType);
|
||||
if (weapon->Proc.Type == ET_CombatProc) {
|
||||
float WPC = ProcChance * (100.0f + // Proc chance for this weapon
|
||||
static_cast<float>(weapon->ProcRate)) / 100.0f;
|
||||
if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
|
||||
if (weapon->Proc.Level > ourlevel) {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Tried to proc (%s), but our level (%d) is lower than required (%d)",
|
||||
weapon->Name, ourlevel, weapon->Proc.Level);
|
||||
if (IsPet()) {
|
||||
Mob *own = GetOwner();
|
||||
if (own)
|
||||
own->Message_StringID(13, PROC_PETTOOLOW);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS, "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", weapon->Name, weapon->Proc.Effect, ProcChance*100);
|
||||
ExecWeaponProc(inst, weapon->Proc.Effect, on);
|
||||
Message_StringID(13, PROC_TOOLOW);
|
||||
}
|
||||
} else {
|
||||
mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100);
|
||||
mlog(COMBAT__PROCS,
|
||||
"Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)",
|
||||
weapon->Name, weapon->Proc.Effect, WPC * 100);
|
||||
ExecWeaponProc(inst, weapon->Proc.Effect, on);
|
||||
proced = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//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(ProcBonus == -1) {
|
||||
LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!");
|
||||
return;
|
||||
}
|
||||
if (!proced && inst) {
|
||||
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
const ItemInst *aug_i = inst->GetAugment(r);
|
||||
if (!aug_i) // no aug, try next slot!
|
||||
continue;
|
||||
const Item_Struct *aug = aug_i->GetItem();
|
||||
if (!aug)
|
||||
continue;
|
||||
|
||||
bool bRangedAttack = false;
|
||||
if (weapon != nullptr) {
|
||||
if (weapon->ItemType == ItemTypeBow || weapon->ItemType == ItemTypeLargeThrowing || weapon->ItemType == ItemTypeSmallThrowing) {
|
||||
bRangedAttack = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool isRanged = false;
|
||||
if(weapon)
|
||||
{
|
||||
if(weapon->ItemType == ItemTypeArrow ||
|
||||
weapon->ItemType == ItemTypeLargeThrowing ||
|
||||
weapon->ItemType == ItemTypeSmallThrowing ||
|
||||
weapon->ItemType == ItemTypeBow)
|
||||
{
|
||||
isRanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 i;
|
||||
for(i = 0; i < MAX_PROCS; i++) {
|
||||
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
if(MakeRandomInt(0, 100) < PermaProcs[i].chance) {
|
||||
mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
|
||||
ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
|
||||
if (aug->Proc.Type == ET_CombatProc) {
|
||||
float APC = ProcChance * (100.0f + // Proc chance for this aug
|
||||
static_cast<float>(aug->ProcRate)) / 100.0f;
|
||||
if (MakeRandomFloat(0, 1) <= APC) {
|
||||
if (aug->Proc.Level > ourlevel) {
|
||||
if (IsPet()) {
|
||||
Mob *own = GetOwner();
|
||||
if (own)
|
||||
own->Message_StringID(13, PROC_PETTOOLOW);
|
||||
} else {
|
||||
Message_StringID(13, PROC_TOOLOW);
|
||||
}
|
||||
} else {
|
||||
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
||||
if (RuleB(Combat, OneProcPerWeapon))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: Powersource procs
|
||||
if (HasSkillProcs())
|
||||
TrySkillProc(on, skillinuse, ProcChance);
|
||||
|
||||
if(!isRanged)
|
||||
{
|
||||
if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
|
||||
{
|
||||
//Maybe implement this later if pets are ever given dual procs?
|
||||
return;
|
||||
}
|
||||
|
||||
void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
|
||||
{
|
||||
float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
|
||||
itembonuses.SpellProcChance + aabonuses.SpellProcChance);
|
||||
float ProcChance = 0.0f;
|
||||
if (weapon)
|
||||
ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
|
||||
else
|
||||
ProcChance = GetProcChances(ProcBonus);
|
||||
|
||||
if (hand != 13) //Is Archery intened to proc at 50% rate?
|
||||
ProcChance /= 2;
|
||||
|
||||
bool rangedattk = false;
|
||||
if (weapon && hand == 11) {
|
||||
if (weapon->ItemType == ItemTypeArrow ||
|
||||
weapon->ItemType == ItemTypeLargeThrowing ||
|
||||
weapon->ItemType == ItemTypeSmallThrowing ||
|
||||
weapon->ItemType == ItemTypeBow)
|
||||
rangedattk = true;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < MAX_PROCS; i++) {
|
||||
if (IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
|
||||
continue; // If pets ever can proc from off hand, this will need to change
|
||||
|
||||
// Not ranged
|
||||
if (!rangedattk) {
|
||||
// Perma procs (AAs)
|
||||
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
if (MakeRandomInt(0, 99) < PermaProcs[i].chance) { // TODO: Do these get spell bonus?
|
||||
mlog(COMBAT__PROCS,
|
||||
"Permanent proc %d procing spell %d (%d percent chance)",
|
||||
i, PermaProcs[i].spellID, PermaProcs[i].chance);
|
||||
ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Permanent proc %d failed to proc %d (%d percent chance)",
|
||||
i, PermaProcs[i].spellID, PermaProcs[i].chance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int chance = ProcChance * (SpellProcs[i].chance);
|
||||
if(MakeRandomInt(0, 100) < chance) {
|
||||
mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
|
||||
// Spell procs (buffs)
|
||||
if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
float chance = ProcChance * (SpellProcs[i].chance / 100.0f);
|
||||
if (MakeRandomFloat(0, 1) <= chance) {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Spell proc %d procing spell %d (%.2f percent chance)",
|
||||
i, SpellProcs[i].spellID, chance);
|
||||
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS, "Spell proc %d failed to proc %d (%d percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
mlog(COMBAT__PROCS,
|
||||
"Spell proc %d failed to proc %d (%.2f percent chance)",
|
||||
i, SpellProcs[i].spellID, chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bRangedAttack) {
|
||||
int chance = ProcChance * RangedProcs[i].chance;
|
||||
if(MakeRandomInt(0, 100) < chance) {
|
||||
mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance);
|
||||
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance);
|
||||
} else if (rangedattk) { // ranged only
|
||||
// ranged spell procs (buffs)
|
||||
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
float chance = ProcChance * (RangedProcs[i].chance / 100.0f);
|
||||
if (MakeRandomFloat(0, 1) <= chance) {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Ranged proc %d procing spell %d (%.2f percent chance)",
|
||||
i, RangedProcs[i].spellID, chance);
|
||||
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
|
||||
} else {
|
||||
mlog(COMBAT__PROCS,
|
||||
"Ranged proc %d failed to proc %d (%.2f percent chance)",
|
||||
i, RangedProcs[i].spellID, chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HasSkillProcs())
|
||||
TrySkillProc(on, skillinuse, ProcChance);
|
||||
return;
|
||||
}
|
||||
|
||||
void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)
|
||||
@ -4218,7 +4250,9 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)
|
||||
{
|
||||
critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100
|
||||
damage = (damage * critMod) / 100;
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT,
|
||||
GetCleanName(), itoa(damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4283,11 +4317,16 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
|
||||
}
|
||||
}
|
||||
|
||||
int deadlyChance = 0;
|
||||
int deadlyMod = 0;
|
||||
if(skill == SkillArchery && GetClass() == RANGER && GetSkill(SkillArchery) >= 65)
|
||||
critChance += 6;
|
||||
|
||||
if(skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65)
|
||||
critChance += 6;
|
||||
if (skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) {
|
||||
critChance += RuleI(Combat, RogueCritThrowingChance);
|
||||
deadlyChance = RuleI(Combat, RogueDeadlyStrikeChance);
|
||||
deadlyMod = RuleI(Combat, RogueDeadlyStrikeMod);
|
||||
}
|
||||
|
||||
int CritChanceBonus = GetCriticalChanceBonus(skill);
|
||||
|
||||
@ -4333,16 +4372,32 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
|
||||
critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100
|
||||
damage = damage * critMod / 100;
|
||||
|
||||
bool deadlySuccess = false;
|
||||
if (deadlyChance && MakeRandomFloat(0, 1) < static_cast<float>(deadlyChance) / 100.0f) {
|
||||
if (BehindMob(defender, GetX(), GetY())) {
|
||||
damage *= deadlyMod;
|
||||
deadlySuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (crip_success) {
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW,
|
||||
GetCleanName(), itoa(damage));
|
||||
// Crippling blows also have a chance to stun
|
||||
//Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message.
|
||||
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){
|
||||
defender->Emote("staggers.");
|
||||
defender->Stun(0);
|
||||
}
|
||||
} else if (deadlySuccess) {
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, DEADLY_STRIKE,
|
||||
GetCleanName(), itoa(damage));
|
||||
} else {
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT,
|
||||
GetCleanName(), itoa(damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4504,14 +4559,67 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance)
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) {
|
||||
|
||||
/*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443
|
||||
The Viscid Roots AA does the following: Reduces the chance for root to break by X percent.
|
||||
There is no distinction of any kind between the caster inflicted damage, or anyone
|
||||
else's damage. There is also no distinction between Direct and DOT damage in the root code.
|
||||
|
||||
/* General Mechanics
|
||||
- Check buffslot to make sure damage from a root does not cancel the root
|
||||
- If multiple roots on target, always and only checks first root slot and if broken only removes that slots root.
|
||||
- Only roots on determental spells can be broken by damage.
|
||||
- Root break chance values obtained from live parses.
|
||||
*/
|
||||
|
||||
if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0)
|
||||
return false;
|
||||
|
||||
if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){
|
||||
|
||||
int BreakChance = RuleI(Spells, RootBreakFromSpells);
|
||||
|
||||
BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100;
|
||||
int level_diff = attacker->GetLevel() - GetLevel();
|
||||
|
||||
//Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level)
|
||||
|
||||
if (level_diff == 2)
|
||||
BreakChance = (BreakChance * 80) /100; //Decrease by 20%;
|
||||
|
||||
else if (level_diff >= 3 && level_diff <= 20)
|
||||
BreakChance = (BreakChance * 60) /100; //Decrease by 40%;
|
||||
|
||||
else if (level_diff > 21)
|
||||
BreakChance = (BreakChance * 20) /100; //Decrease by 80%;
|
||||
|
||||
if (BreakChance < 1)
|
||||
BreakChance = 1;
|
||||
|
||||
if (MakeRandomInt(0, 99) < BreakChance) {
|
||||
|
||||
if (!TryFadeEffect(spellbonuses.Root[1])) {
|
||||
BuffFadeBySlot(spellbonuses.Root[1]);
|
||||
mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
uint32 buff_max = GetMaxTotalSlots();
|
||||
if (type == SE_Rune){
|
||||
for(uint32 slot = 0; slot < buff_max; slot++) {
|
||||
if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].melee_rune) && IsEffectInSpell(buffs[slot].spellid, type)){
|
||||
if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){
|
||||
uint32 melee_rune_left = buffs[slot].melee_rune;
|
||||
if(melee_rune_left >= damage)
|
||||
|
||||
if(melee_rune_left > damage)
|
||||
{
|
||||
melee_rune_left -= damage;
|
||||
buffs[slot].melee_rune = melee_rune_left;
|
||||
@ -4522,22 +4630,20 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
if(melee_rune_left > 0)
|
||||
damage -= melee_rune_left;
|
||||
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
else{
|
||||
for(uint32 slot = 0; slot < buff_max; slot++) {
|
||||
if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].magic_rune) && IsEffectInSpell(buffs[slot].spellid, type)){
|
||||
if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){
|
||||
uint32 magic_rune_left = buffs[slot].magic_rune;
|
||||
if(magic_rune_left >= damage)
|
||||
if(magic_rune_left > damage)
|
||||
{
|
||||
magic_rune_left -= damage;
|
||||
buffs[slot].magic_rune = magic_rune_left;
|
||||
@ -4548,14 +4654,14 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
if(magic_rune_left > 0)
|
||||
damage -= magic_rune_left;
|
||||
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
171
zone/bonuses.cpp
171
zone/bonuses.cpp
@ -963,7 +963,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
newbon->GiveDoubleAttack += base1;
|
||||
break;
|
||||
case SE_ProcChance:
|
||||
newbon->ProcChance += base1;
|
||||
newbon->ProcChanceSPA += base1;
|
||||
break;
|
||||
case SE_RiposteChance:
|
||||
newbon->RiposteChance += base1;
|
||||
@ -1228,6 +1228,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_FrenziedDevastation:
|
||||
newbon->FrenziedDevastation += base2;
|
||||
break;
|
||||
|
||||
case SE_SpellProcChance:
|
||||
newbon->SpellProcChance += base1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1350,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;
|
||||
}
|
||||
@ -1369,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;
|
||||
}
|
||||
|
||||
@ -1607,12 +1632,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
newbon->reflect_chance += effect_value;
|
||||
break;
|
||||
|
||||
case SE_SingingSkill:
|
||||
{
|
||||
if(effect_value > newbon->singingMod)
|
||||
newbon->singingMod = effect_value;
|
||||
case SE_Amplification:
|
||||
newbon->Amplification += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_ChangeAggro:
|
||||
newbon->hatemod += effect_value;
|
||||
@ -1885,17 +1907,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_ProcChance:
|
||||
{
|
||||
//multiplier is to be compatible with item effects,watching for overflow too
|
||||
effect_value = effect_value<3000? effect_value : 3000;
|
||||
|
||||
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
|
||||
newbon->ProcChance += effect_value;
|
||||
newbon->ProcChanceSPA += effect_value;
|
||||
|
||||
else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value))
|
||||
newbon->ProcChance = effect_value;
|
||||
else if((effect_value < 0) && (newbon->ProcChanceSPA > effect_value))
|
||||
newbon->ProcChanceSPA = effect_value;
|
||||
|
||||
if(newbon->ProcChance < effect_value)
|
||||
newbon->ProcChance = effect_value;
|
||||
if(newbon->ProcChanceSPA < effect_value)
|
||||
newbon->ProcChanceSPA = effect_value;
|
||||
|
||||
break;
|
||||
}
|
||||
@ -2239,6 +2258,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MitigateDotDamage:
|
||||
{
|
||||
if (newbon->MitigateDotRune[0] < effect_value){
|
||||
newbon->MitigateDotRune[0] = effect_value;
|
||||
newbon->MitigateDotRune[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_ManaAbsorbPercentDamage:
|
||||
{
|
||||
@ -2513,6 +2541,59 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case SE_DistanceRemoval:
|
||||
newbon->DistanceRemoval = true;
|
||||
break;
|
||||
|
||||
case SE_FrenziedDevastation:
|
||||
newbon->FrenziedDevastation += spells[spell_id].base2[i];
|
||||
break;
|
||||
|
||||
case SE_Root:
|
||||
if (newbon->Root[0] && (newbon->Root[1] > buffslot)){
|
||||
newbon->Root[0] = 1;
|
||||
newbon->Root[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->Root[0]){
|
||||
newbon->Root[0] = 1;
|
||||
newbon->Root[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Rune:
|
||||
|
||||
if (newbon->MeleeRune[0] && (newbon->MeleeRune[1] > buffslot)){
|
||||
|
||||
newbon->MeleeRune[0] = effect_value;
|
||||
newbon->MeleeRune[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->MeleeRune[0]){
|
||||
newbon->MeleeRune[0] = effect_value;
|
||||
newbon->MeleeRune[1] = buffslot;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SE_AbsorbMagicAtt:
|
||||
if (newbon->AbsorbMagicAtt[0] && (newbon->AbsorbMagicAtt[1] > buffslot)){
|
||||
newbon->AbsorbMagicAtt[0] = effect_value;
|
||||
newbon->AbsorbMagicAtt[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->AbsorbMagicAtt[0]){
|
||||
newbon->AbsorbMagicAtt[0] = effect_value;
|
||||
newbon->AbsorbMagicAtt[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_NegateIfCombat:
|
||||
newbon->NegateIfCombat = true;
|
||||
break;
|
||||
|
||||
case SE_Screech:
|
||||
newbon->Screech = effect_value;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3124,10 +3205,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
itembonuses.reflect_chance = effect_value;
|
||||
break;
|
||||
|
||||
case SE_SingingSkill:
|
||||
spellbonuses.singingMod = effect_value;
|
||||
itembonuses.singingMod = effect_value;
|
||||
aabonuses.singingMod = effect_value;
|
||||
case SE_Amplification:
|
||||
spellbonuses.Amplification = effect_value;
|
||||
itembonuses.Amplification = effect_value;
|
||||
aabonuses.Amplification = effect_value;
|
||||
break;
|
||||
|
||||
case SE_ChangeAggro:
|
||||
@ -3300,9 +3381,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_ProcChance:
|
||||
spellbonuses.ProcChance = effect_value;
|
||||
aabonuses.ProcChance = effect_value;
|
||||
itembonuses.ProcChance = effect_value;
|
||||
spellbonuses.ProcChanceSPA = effect_value;
|
||||
aabonuses.ProcChanceSPA = effect_value;
|
||||
itembonuses.ProcChanceSPA = effect_value;
|
||||
break;
|
||||
|
||||
case SE_ExtraAttackChance:
|
||||
@ -3558,6 +3639,11 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
spellbonuses.MitigateSpellRune[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_MitigateDotDamage:
|
||||
spellbonuses.MitigateDotRune[0] = effect_value;
|
||||
spellbonuses.MitigateDotRune[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_ManaAbsorbPercentDamage:
|
||||
spellbonuses.ManaAbsorbPercentDamage[0] = effect_value;
|
||||
spellbonuses.ManaAbsorbPercentDamage[1] = -1;
|
||||
@ -3841,7 +3927,38 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
itembonuses.CriticalMend = effect_value;
|
||||
aabonuses.CriticalMend = effect_value;
|
||||
break;
|
||||
|
||||
|
||||
case SE_DistanceRemoval:
|
||||
spellbonuses.DistanceRemoval = effect_value;
|
||||
break;
|
||||
|
||||
case SE_ImprovedTaunt:
|
||||
spellbonuses.ImprovedTaunt[0] = effect_value;
|
||||
spellbonuses.ImprovedTaunt[1] = effect_value;
|
||||
spellbonuses.ImprovedTaunt[2] = -1;
|
||||
break;
|
||||
|
||||
case SE_FrenziedDevastation:
|
||||
spellbonuses.FrenziedDevastation = effect_value;
|
||||
aabonuses.FrenziedDevastation = effect_value;
|
||||
itembonuses.FrenziedDevastation = effect_value;
|
||||
break;
|
||||
|
||||
case SE_Root:
|
||||
spellbonuses.Root[0] = effect_value;
|
||||
spellbonuses.Root[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_Rune:
|
||||
spellbonuses.MeleeRune[0] = effect_value;
|
||||
spellbonuses.MeleeRune[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_AbsorbMagicAtt:
|
||||
spellbonuses.AbsorbMagicAtt[0] = effect_value;
|
||||
spellbonuses.AbsorbMagicAtt[1] = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
zone/bot.cpp
105
zone/bot.cpp
@ -2476,7 +2476,7 @@ void Bot::SaveBuffs() {
|
||||
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, "
|
||||
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, "
|
||||
"DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);",
|
||||
"dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
|
||||
GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula,
|
||||
buffs[BuffCount].ticsremaining,
|
||||
CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
@ -2484,8 +2484,12 @@ void Bot::SaveBuffs() {
|
||||
CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune,
|
||||
buffs[BuffCount].deathSaveSuccessChance,
|
||||
buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) {
|
||||
buffs[BuffCount].dot_rune,
|
||||
buffs[BuffCount].caston_x,
|
||||
IsPersistent,
|
||||
buffs[BuffCount].caston_y,
|
||||
buffs[BuffCount].caston_z,
|
||||
buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) {
|
||||
errorMessage = std::string(TempErrorMessageBuffer);
|
||||
safe_delete(Query);
|
||||
Query = 0;
|
||||
@ -2515,7 +2519,7 @@ void Bot::LoadBuffs() {
|
||||
|
||||
bool BuffsLoaded = false;
|
||||
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) {
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) {
|
||||
errorMessage = std::string(TempErrorMessageBuffer);
|
||||
}
|
||||
else {
|
||||
@ -2541,8 +2545,8 @@ void Bot::LoadBuffs() {
|
||||
buffs[BuffCount].numhits = atoi(DataRow[8]);
|
||||
buffs[BuffCount].melee_rune = atoi(DataRow[9]);
|
||||
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
|
||||
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
|
||||
buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]);
|
||||
buffs[BuffCount].dot_rune = atoi(DataRow[11]);
|
||||
buffs[BuffCount].caston_x = atoi(DataRow[12]);
|
||||
buffs[BuffCount].casterid = 0;
|
||||
|
||||
bool IsPersistent = false;
|
||||
@ -2550,6 +2554,10 @@ void Bot::LoadBuffs() {
|
||||
if(atoi(DataRow[13]))
|
||||
IsPersistent = true;
|
||||
|
||||
buffs[BuffCount].caston_y = atoi(DataRow[14]);
|
||||
buffs[BuffCount].caston_z = atoi(DataRow[15]);
|
||||
buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]);
|
||||
|
||||
buffs[BuffCount].persistant_buff = IsPersistent;
|
||||
|
||||
BuffCount++;
|
||||
@ -3143,9 +3151,9 @@ void Bot::SpellProcess()
|
||||
void Bot::BotMeditate(bool isSitting) {
|
||||
if(isSitting) {
|
||||
// If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate
|
||||
if(GetManaRatio() < 99.0f)
|
||||
if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f)
|
||||
{
|
||||
if(!IsSitting())
|
||||
if (!IsEngaged() && !IsSitting())
|
||||
Sit();
|
||||
}
|
||||
else
|
||||
@ -3229,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;
|
||||
@ -5273,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;
|
||||
|
||||
@ -6632,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);
|
||||
|
||||
@ -6818,13 +6854,6 @@ int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id)
|
||||
SpellSkill_Found = true;
|
||||
break;
|
||||
|
||||
case SE_LimitSpellSubclass:{
|
||||
int16 spell_skill = spell.skill * -1;
|
||||
if(base1 == spell_skill)
|
||||
LimitFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_LimitClass:
|
||||
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
|
||||
if (!PassLimitClass(base1, GetClass()))
|
||||
@ -7423,13 +7452,6 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
SpellSkill_Found = true;
|
||||
break;
|
||||
|
||||
case SE_LimitSpellSubclass:{
|
||||
int16 spell_skill = spell.skill * -1;
|
||||
if(focus_spell.base[i] == spell_skill)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_LimitClass:
|
||||
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
|
||||
if (!PassLimitClass(focus_spell.base[i], GetClass()))
|
||||
@ -7711,16 +7733,11 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
|
||||
}
|
||||
|
||||
//proc chance includes proc bonus
|
||||
float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) {
|
||||
float Bot::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) {
|
||||
int mydex = GetDEX();
|
||||
float AABonus = 0;
|
||||
ProcBonus = 0;
|
||||
ProcChance = 0;
|
||||
float ProcChance = 0.0f;
|
||||
|
||||
if (aabonuses.ProcChance)
|
||||
AABonus = float(aabonuses.ProcChance) / 100.0f;
|
||||
|
||||
switch(hand){
|
||||
switch (hand) {
|
||||
case SLOT_PRIMARY:
|
||||
weapon_speed = attack_timer.GetDuration();
|
||||
break;
|
||||
@ -7732,24 +7749,20 @@ float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//calculate the weapon speed in ms, so we can use the rule to compare against.
|
||||
|
||||
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
|
||||
// fast as a client can swing, so should be the floor of the proc chance
|
||||
if (weapon_speed < RuleI(Combat, MinHastedDelay))
|
||||
weapon_speed = RuleI(Combat, MinHastedDelay);
|
||||
|
||||
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus);
|
||||
|
||||
if(RuleB(Combat, AdjustProcPerMinute) == true)
|
||||
{
|
||||
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
|
||||
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f;
|
||||
ProcChance = ProcChance + (ProcChance * ProcBonus);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
|
||||
ProcChance = ProcChance + (ProcChance * ProcBonus);
|
||||
if (RuleB(Combat, AdjustProcPerMinute)) {
|
||||
ProcChance = (static_cast<float>(weapon_speed) *
|
||||
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
|
||||
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
|
||||
ProcChance += ProcChance * ProcBonus / 100.0f;
|
||||
} else {
|
||||
ProcChance = RuleR(Combat, BaseProcChance) +
|
||||
static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
|
||||
ProcChance += ProcChance*ProcBonus / 100.0f;
|
||||
}
|
||||
|
||||
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus);
|
||||
@ -14982,7 +14995,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bot* targetedBot;
|
||||
Bot *targetedBot = nullptr;
|
||||
|
||||
if(c->GetTarget() != nullptr) {
|
||||
if (c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c))
|
||||
|
||||
@ -167,7 +167,7 @@ public:
|
||||
uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
|
||||
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
|
||||
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
|
||||
virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand);
|
||||
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand);
|
||||
virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
|
||||
virtual int GetMonkHandToHandDamage(void);
|
||||
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);
|
||||
@ -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);
|
||||
|
||||
155
zone/client.cpp
155
zone/client.cpp
@ -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
|
||||
|
||||
@ -2852,7 +2858,12 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
|
||||
else
|
||||
ClientFilters[FilterSpellCrits] = FilterHide;
|
||||
|
||||
Filter1(FilterMeleeCrits);
|
||||
if (filter->filters[FilterMeleeCrits] == 0)
|
||||
ClientFilters[FilterMeleeCrits] = FilterShow;
|
||||
else if (filter->filters[FilterMeleeCrits] == 1)
|
||||
ClientFilters[FilterMeleeCrits] = FilterShowSelfOnly;
|
||||
else
|
||||
ClientFilters[FilterMeleeCrits] = FilterHide;
|
||||
|
||||
if(filter->filters[FilterSpellDamage] == 0)
|
||||
ClientFilters[FilterSpellDamage] = FilterShow;
|
||||
@ -2866,12 +2877,41 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
|
||||
Filter0(FilterOthersHit);
|
||||
Filter0(FilterMissedMe);
|
||||
Filter1(FilterDamageShields);
|
||||
Filter1(FilterDOT);
|
||||
|
||||
if (GetClientVersionBit() & BIT_SoDAndLater) {
|
||||
if (filter->filters[FilterDOT] == 0)
|
||||
ClientFilters[FilterDOT] = FilterShow;
|
||||
else if (filter->filters[FilterDOT] == 1)
|
||||
ClientFilters[FilterDOT] = FilterShowSelfOnly;
|
||||
else if (filter->filters[FilterDOT] == 2)
|
||||
ClientFilters[FilterDOT] = FilterShowGroupOnly;
|
||||
else
|
||||
ClientFilters[FilterDOT] = FilterHide;
|
||||
} else {
|
||||
if (filter->filters[FilterDOT] == 0) // show functions as self only
|
||||
ClientFilters[FilterDOT] = FilterShowSelfOnly;
|
||||
else
|
||||
ClientFilters[FilterDOT] = FilterHide;
|
||||
}
|
||||
|
||||
Filter1(FilterPetHits);
|
||||
Filter1(FilterPetMisses);
|
||||
Filter1(FilterFocusEffects);
|
||||
Filter1(FilterPetSpells);
|
||||
Filter1(FilterHealOverTime);
|
||||
|
||||
if (GetClientVersionBit() & BIT_SoDAndLater) {
|
||||
if (filter->filters[FilterHealOverTime] == 0)
|
||||
ClientFilters[FilterHealOverTime] = FilterShow;
|
||||
// This is called 'Show Mine Only' in the clients, but functions the same as show
|
||||
// so instead of apply special logic, just set to show
|
||||
else if (filter->filters[FilterHealOverTime] == 1)
|
||||
ClientFilters[FilterHealOverTime] = FilterShow;
|
||||
else
|
||||
ClientFilters[FilterHealOverTime] = FilterHide;
|
||||
} else {
|
||||
// these clients don't have a 'self only' filter
|
||||
Filter1(FilterHealOverTime);
|
||||
}
|
||||
}
|
||||
|
||||
// this version is for messages with no parameters
|
||||
@ -2963,6 +3003,111 @@ void Client::Message_StringID(uint32 type, uint32 string_id, const char* message
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
// helper function, returns true if we should see the message
|
||||
bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
|
||||
{
|
||||
eqFilterMode mode = GetFilter(filter);
|
||||
// easy ones first
|
||||
if (mode == FilterShow)
|
||||
return true;
|
||||
else if (mode == FilterHide)
|
||||
return false;
|
||||
|
||||
if (!sender && mode == FilterHide) {
|
||||
return false;
|
||||
} else if (sender) {
|
||||
if (this == sender) {
|
||||
if (mode == FilterHide) // don't need to check others
|
||||
return false;
|
||||
} else if (mode == FilterShowSelfOnly) { // we know sender isn't us
|
||||
return false;
|
||||
} else if (mode == FilterShowGroupOnly) {
|
||||
Group *g = GetGroup();
|
||||
Raid *r = GetRaid();
|
||||
if (g) {
|
||||
if (g->IsGroupMember(sender))
|
||||
return true;
|
||||
} else if (r && sender->IsClient()) {
|
||||
uint32 rgid1 = r->GetGroup(this);
|
||||
uint32 rgid2 = r->GetGroup(sender->CastToClient());
|
||||
if (rgid1 != 0xFFFFFFFF && rgid1 == rgid2)
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we passed our checks
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::FilteredMessage_StringID(Mob *sender, uint32 type,
|
||||
eqFilterType filter, uint32 string_id)
|
||||
{
|
||||
if (!FilteredMessageCheck(sender, filter))
|
||||
return;
|
||||
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
|
||||
SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer;
|
||||
sms->color = type;
|
||||
sms->string_id = string_id;
|
||||
|
||||
sms->unknown8 = 0;
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id,
|
||||
const char *message1, const char *message2, const char *message3,
|
||||
const char *message4, const char *message5, const char *message6,
|
||||
const char *message7, const char *message8, const char *message9)
|
||||
{
|
||||
if (!FilteredMessageCheck(sender, filter))
|
||||
return;
|
||||
|
||||
int i, argcount, length;
|
||||
char *bufptr;
|
||||
const char *message_arg[9] = {0};
|
||||
|
||||
if (type == MT_Emote)
|
||||
type = 4;
|
||||
|
||||
if (!message1) {
|
||||
FilteredMessage_StringID(sender, type, filter, string_id); // use the simple message instead
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
message_arg[i++] = message1;
|
||||
message_arg[i++] = message2;
|
||||
message_arg[i++] = message3;
|
||||
message_arg[i++] = message4;
|
||||
message_arg[i++] = message5;
|
||||
message_arg[i++] = message6;
|
||||
message_arg[i++] = message7;
|
||||
message_arg[i++] = message8;
|
||||
message_arg[i++] = message9;
|
||||
|
||||
for (argcount = length = 0; message_arg[argcount]; argcount++)
|
||||
length += strlen(message_arg[argcount]) + 1;
|
||||
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_FormattedMessage, length+13);
|
||||
FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer;
|
||||
fm->string_id = string_id;
|
||||
fm->type = type;
|
||||
bufptr = fm->message;
|
||||
for (i = 0; i < argcount; i++) {
|
||||
strcpy(bufptr, message_arg[i]);
|
||||
bufptr += strlen(message_arg[i]) + 1;
|
||||
}
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::SetTint(int16 in_slot, uint32 color) {
|
||||
Color_Struct new_color;
|
||||
@ -6571,9 +6716,9 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (int i=0; i < buff_count; i++) {
|
||||
if (buffs[i].spellid != SPELL_UNKNOWN) {
|
||||
if ((HasRune() || HasPartialMeleeRune()) && buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
|
||||
if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
|
||||
|
||||
if ((HasSpellRune() || HasPartialSpellRune()) && buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
|
||||
if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -250,6 +250,14 @@ public:
|
||||
uint8 SlotConvert(uint8 slot,bool bracer=false);
|
||||
void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0);
|
||||
void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
|
||||
bool FilteredMessageCheck(Mob *sender, eqFilterType filter);
|
||||
void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id);
|
||||
void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter,
|
||||
uint32 string_id, const char *message1, const char *message2 = nullptr,
|
||||
const char *message3 = nullptr, const char *message4 = nullptr,
|
||||
const char *message5 = nullptr, const char *message6 = nullptr,
|
||||
const char *message7 = nullptr, const char *message8 = nullptr,
|
||||
const char *message9 = nullptr);
|
||||
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
|
||||
void SendTraderItem(uint32 item_id,uint16 quantity);
|
||||
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
||||
@ -297,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();
|
||||
|
||||
@ -786,7 +795,7 @@ public:
|
||||
void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false);
|
||||
void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0);
|
||||
bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
|
||||
void SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR);
|
||||
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR);
|
||||
void SetStats(uint8 type,int16 set_val);
|
||||
void IncStats(uint8 type,int16 increase_val);
|
||||
void DropItem(int16 slot_id);
|
||||
|
||||
@ -1857,7 +1857,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const
|
||||
effectmod = itembonuses.singingMod;
|
||||
else
|
||||
effectmod = spellbonuses.singingMod;
|
||||
effectmod += aabonuses.singingMod;
|
||||
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
|
||||
break;
|
||||
default:
|
||||
effectmod = 10;
|
||||
|
||||
@ -1615,7 +1615,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
|
||||
|
||||
if (shield_target)
|
||||
{
|
||||
entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetName(),shield_target->GetName());
|
||||
entity_list.MessageClose_StringID(this, false, 100, 0,
|
||||
END_SHIELDING, GetName(), shield_target->GetName());
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
if (shield_target->shielder[y].shielder_id == GetID())
|
||||
@ -1640,7 +1641,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
|
||||
{
|
||||
if (shield_target->shielder[x].shielder_id == 0)
|
||||
{
|
||||
entity_list.MessageClose(this,false,100,0,"%s uses their shield to guard %s.",GetName(),shield_target->GetName());
|
||||
entity_list.MessageClose_StringID(this ,false, 100, 0,
|
||||
START_SHIELDING, GetName(), shield_target->GetName());
|
||||
shield_target->shielder[x].shielder_id = GetID();
|
||||
int shieldbonus = shield->AC*2;
|
||||
switch (GetAA(197))
|
||||
@ -1677,7 +1679,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
|
||||
}
|
||||
if (!ack)
|
||||
{
|
||||
Message(0, "No more than two warriors may shield the same being.");
|
||||
Message_StringID(0, ALREADY_SHIELDED);
|
||||
shield_target = 0;
|
||||
return;
|
||||
}
|
||||
@ -3476,21 +3478,23 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app)
|
||||
}
|
||||
if(GetClass() == ROGUE){
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct));
|
||||
SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer;
|
||||
msg->color=0x010E;
|
||||
if (!auto_attack && entity_list.Fighting(this)) {
|
||||
SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer;
|
||||
msg->color = 0x010E;
|
||||
Mob *evadetar = GetTarget();
|
||||
if (!auto_attack && (evadetar && evadetar->CheckAggro(this)
|
||||
&& evadetar->IsNPC())) {
|
||||
if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) {
|
||||
msg->string_id=343;
|
||||
entity_list.Evade(this);
|
||||
msg->string_id = EVADE_SUCCESS;
|
||||
RogueEvade(evadetar);
|
||||
} else {
|
||||
msg->string_id=344;
|
||||
msg->string_id = EVADE_FAIL;
|
||||
}
|
||||
} else {
|
||||
if (hidden){
|
||||
msg->string_id=346;
|
||||
msg->string_id = HIDE_SUCCESS;
|
||||
}
|
||||
else {
|
||||
msg->string_id=345;
|
||||
msg->string_id = HIDE_FAIL;
|
||||
}
|
||||
}
|
||||
FastQueuePacket(&outapp);
|
||||
@ -5417,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;
|
||||
|
||||
@ -5561,8 +5571,13 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
Message(15,"You can only have one of a lore item.");
|
||||
return;
|
||||
}
|
||||
if(tmpmer_used && (mp->quantity > prevcharges))
|
||||
mp->quantity = prevcharges;
|
||||
if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1))
|
||||
{
|
||||
if(prevcharges > item->MaxCharges && item->MaxCharges > 1)
|
||||
mp->quantity = item->MaxCharges;
|
||||
else
|
||||
mp->quantity = prevcharges;
|
||||
}
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
|
||||
Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer;
|
||||
@ -5573,13 +5588,11 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
|
||||
int16 freeslotid=0;
|
||||
int16 charges = 0;
|
||||
if (item->Stackable) {
|
||||
if (item->Stackable || item->MaxCharges > 1)
|
||||
charges = mp->quantity;
|
||||
} else {
|
||||
// this needs expanded to handle varying charges from the merchant,
|
||||
// but will require merchantlist_temp changes amonst other things.
|
||||
else
|
||||
charges = item->MaxCharges;
|
||||
}
|
||||
|
||||
ItemInst* inst = database.CreateItem(item, charges);
|
||||
|
||||
int SinglePrice = 0;
|
||||
@ -5588,7 +5601,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
else
|
||||
SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate);
|
||||
|
||||
mpo->price = SinglePrice * mp->quantity;
|
||||
if(item->MaxCharges > 1)
|
||||
mpo->price = SinglePrice;
|
||||
else
|
||||
mpo->price = SinglePrice * mp->quantity;
|
||||
if(mpo->price < 0 )
|
||||
{
|
||||
safe_delete(outapp);
|
||||
@ -5632,9 +5648,6 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
std::string packet;
|
||||
if(mp->quantity==1 && item->MaxCharges>0 && item->MaxCharges<255)
|
||||
mp->quantity=item->MaxCharges;
|
||||
|
||||
if (!stacked && inst) {
|
||||
PutItemInInventory(freeslotid, *inst);
|
||||
SendItemPacket(freeslotid, inst, ItemPacketTrade);
|
||||
@ -5756,37 +5769,36 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
//Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName());
|
||||
return;
|
||||
}
|
||||
if (RuleB(Merchant, UsePriceMod)){
|
||||
price=(int)((item->Price*mp->quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price
|
||||
}
|
||||
|
||||
int cost_quantity = mp->quantity;
|
||||
if(inst->IsCharged())
|
||||
int cost_quantity = 1;
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod))
|
||||
price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price
|
||||
else
|
||||
price=(int)((item->Price*mp->quantity)*(RuleR(Merchant, BuyCostMod))+0.5);
|
||||
price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))+0.5);
|
||||
AddMoneyToPP(price,false);
|
||||
|
||||
if (inst->IsStackable())
|
||||
if (inst->IsStackable() || inst->IsCharged())
|
||||
{
|
||||
unsigned int i_quan = inst->GetCharges();
|
||||
if (mp->quantity > i_quan)
|
||||
if (mp->quantity > i_quan || inst->IsCharged())
|
||||
mp->quantity = i_quan;
|
||||
}
|
||||
else
|
||||
{
|
||||
mp->quantity = 1;
|
||||
}
|
||||
|
||||
if (RuleB(EventLog, RecordSellToMerchant))
|
||||
LogMerchant(this, vendor, mp->quantity, price, item, false);
|
||||
|
||||
int freeslot = 0;
|
||||
int charges = 0;
|
||||
if(inst->IsStackable())
|
||||
charges = mp->quantity;
|
||||
else
|
||||
//charges = inst->GetCharges();
|
||||
//FIXME: Temp merchant table uses 'charges' as the quantity, so doesn't properly handle charged items.
|
||||
int charges = mp->quantity;
|
||||
//Hack workaround so usable items with 0 charges aren't simply deleted
|
||||
if(charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21)
|
||||
charges = 1;
|
||||
|
||||
if((freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){
|
||||
int freeslot = 0;
|
||||
if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){
|
||||
ItemInst* inst2 = inst->Clone();
|
||||
if (RuleB(Merchant, UsePriceMod)){
|
||||
inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false));
|
||||
@ -5846,6 +5858,10 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
else
|
||||
this->DeleteItemInInventory(mp->itemslot,mp->quantity,false);
|
||||
|
||||
//This forces the price to show up correctly for charged items.
|
||||
if(inst->IsCharged())
|
||||
mp->quantity = 1;
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
|
||||
Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer;
|
||||
mco->npcid = vendor->GetID();
|
||||
@ -12621,32 +12637,38 @@ void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app)
|
||||
uint32 cost = 0;
|
||||
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);
|
||||
uint32 merchant_id = tar->MerchantType;
|
||||
bool found = false;
|
||||
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
|
||||
std::list<MerchantList>::const_iterator itr;
|
||||
for(itr = merlist.begin(); itr != merlist.end(); ++itr) {
|
||||
MerchantList ml = *itr;
|
||||
if(GetLevel() < ml.level_required) {
|
||||
continue;
|
||||
|
||||
if (RuleB(Merchant, EnableAltCurrencySell)) {
|
||||
bool found = false;
|
||||
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
|
||||
std::list<MerchantList>::const_iterator itr;
|
||||
for (itr = merlist.begin(); itr != merlist.end(); ++itr) {
|
||||
MerchantList ml = *itr;
|
||||
if (GetLevel() < ml.level_required) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 fac = tar->GetPrimaryFaction();
|
||||
if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item = database.GetItem(ml.item);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
if (item->ID == inst->GetItem()->ID) {
|
||||
cost = ml.alt_currency_cost;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32 fac = tar->GetPrimaryFaction();
|
||||
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item = database.GetItem(ml.item);
|
||||
if(!item)
|
||||
continue;
|
||||
|
||||
if(item->ID == inst->GetItem()->ID) {
|
||||
cost = ml.alt_currency_cost;
|
||||
found = true;
|
||||
break;
|
||||
if (!found) {
|
||||
cost = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
else {
|
||||
cost = 0;
|
||||
}
|
||||
|
||||
@ -12796,6 +12818,10 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!RuleB(Merchant, EnableAltCurrencySell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Item_Struct* item = nullptr;
|
||||
uint32 cost = 0;
|
||||
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);
|
||||
@ -12818,7 +12844,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) {
|
||||
if(!item)
|
||||
continue;
|
||||
|
||||
if(item->ID == inst->GetItem()->ID) {
|
||||
if(item->ID == inst->GetItem()->ID) {
|
||||
cost = ml.alt_currency_cost;
|
||||
found = true;
|
||||
break;
|
||||
|
||||
@ -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();
|
||||
@ -581,7 +584,8 @@ bool Client::Process() {
|
||||
{
|
||||
if (!CombatRange(shield_target))
|
||||
{
|
||||
entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetCleanName(),shield_target->GetCleanName());
|
||||
entity_list.MessageClose_StringID(this, false, 100, 0,
|
||||
END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
|
||||
for (int y = 0; y < 2; y++)
|
||||
{
|
||||
if (shield_target->shielder[y].shielder_id == GetID())
|
||||
|
||||
@ -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();
|
||||
@ -2085,21 +2089,27 @@ void command_ai(Client *c, const Seperator *sep)
|
||||
}
|
||||
else if (strcasecmp(sep->arg[1], "roambox") == 0) {
|
||||
if (target && target->IsAIControlled() && target->IsNPC()) {
|
||||
if ((sep->argnum == 6 || sep->argnum == 7) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
|
||||
if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
|
||||
uint32 tmp = 2500;
|
||||
uint32 tmp2 = 2500;
|
||||
if (sep->IsNumber(7))
|
||||
tmp = atoi(sep->arg[7]);
|
||||
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp);
|
||||
if (sep->IsNumber(8))
|
||||
tmp2 = atoi(sep->arg[8]);
|
||||
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2);
|
||||
}
|
||||
else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
|
||||
uint32 tmp = 2500;
|
||||
uint32 tmp2 = 2500;
|
||||
if (sep->IsNumber(4))
|
||||
tmp = atoi(sep->arg[4]);
|
||||
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp);
|
||||
if (sep->IsNumber(5))
|
||||
tmp2 = atoi(sep->arg[5]);
|
||||
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2);
|
||||
}
|
||||
else {
|
||||
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay]");
|
||||
c->Message(0, "Usage: #ai roambox dist roamdist [delay]");
|
||||
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]");
|
||||
c->Message(0, "Usage: #ai roambox dist roamdist [delay] [mindelay]");
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -2632,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)
|
||||
@ -11047,9 +11073,6 @@ void command_showbonusstats(Client *c, const Seperator *sep)
|
||||
c->Message(0, " Target Spell Bonuses:");
|
||||
c->Message(0, " Accuracy: %i%% Divine Save: %i%%",c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance);
|
||||
c->Message(0, " Flurry: %i%% HitChance: %i%% ",c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15);
|
||||
int deathsaveslot = c->GetTarget()->GetBuffSlotFromType(SE_DeathSave);
|
||||
int dschance = deathsaveslot >= 0 ? c->GetTarget()->GetBuffs()[deathsaveslot].deathSaveSuccessChance : 0;
|
||||
c->Message(0, " Death Save: %i%%",dschance);
|
||||
}
|
||||
c->Message(0, " Effective Casting Level: %i",c->GetTarget()->GetCasterLevel(0));
|
||||
}
|
||||
@ -11478,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())
|
||||
@ -122,7 +123,10 @@ enum {
|
||||
TETHER = 33,
|
||||
DESTRUCTIBLE_OBJECT = 34,
|
||||
NO_HARM_FROM_CLIENT = 35,
|
||||
MAX_SPECIAL_ATTACK = 36
|
||||
ALWAYS_FLEE = 36,
|
||||
FLEE_PERCENT = 37,
|
||||
MAX_SPECIAL_ATTACK = 38
|
||||
|
||||
};
|
||||
|
||||
typedef enum { //fear states
|
||||
@ -155,8 +159,12 @@ struct Buffs_Struct {
|
||||
uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects
|
||||
uint32 melee_rune;
|
||||
uint32 magic_rune;
|
||||
uint8 deathSaveSuccessChance;
|
||||
uint8 deathsaveCasterAARank;
|
||||
uint32 dot_rune;
|
||||
int32 caston_x;
|
||||
int32 caston_y;
|
||||
int32 caston_z;
|
||||
int32 ExtraDIChance;
|
||||
int16 RootBreakChance; //Not saved to dbase
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
@ -231,6 +239,7 @@ struct StatBonuses {
|
||||
int effective_casting_level;
|
||||
int reflect_chance; // chance to reflect incoming spell
|
||||
uint16 singingMod;
|
||||
uint16 Amplification; // stacks with singingMod
|
||||
uint16 brassMod;
|
||||
uint16 percussionMod;
|
||||
uint16 windMod;
|
||||
@ -270,6 +279,7 @@ struct StatBonuses {
|
||||
int16 DamageModifier[HIGHEST_SKILL+2]; //i
|
||||
int16 MinDamageModifier[HIGHEST_SKILL+2]; //i
|
||||
int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects
|
||||
int16 ProcChanceSPA; // ProcChance from spell effects
|
||||
int16 ExtraAttackChance;
|
||||
int16 DoTShielding;
|
||||
int16 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger)
|
||||
@ -307,8 +317,8 @@ struct StatBonuses {
|
||||
//uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used
|
||||
bool ImmuneToFlee; // Bypass the fleeing flag
|
||||
uint16 VoiceGraft; // Stores the ID of the mob with which to talk through
|
||||
uint16 SpellProcChance; // chance to proc from sympathetic spell effects
|
||||
uint16 CharmBreakChance; // chance to break charm
|
||||
int16 SpellProcChance; // chance to proc from sympathetic spell effects
|
||||
int16 CharmBreakChance; // chance to break charm
|
||||
int16 SongRange; // increases range of beneficial bard songs
|
||||
uint16 HPToManaConvert; // Uses HP to cast spells at specific conversion
|
||||
uint16 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
|
||||
@ -319,6 +329,7 @@ struct StatBonuses {
|
||||
uint16 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger.
|
||||
uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger.
|
||||
uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot
|
||||
uint16 MitigateDotRune[2]; // 0 = Mitigation value 1 = Buff Slot
|
||||
uint32 TriggerMeleeThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger
|
||||
uint32 TriggerSpellThreshold[3]; // 0 = Spell Effect ID 1 = Buff slot 2 = Damage Amount to Trigger
|
||||
uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot
|
||||
@ -328,9 +339,14 @@ struct StatBonuses {
|
||||
bool CriticalHealDecay; // increase critical heal chance, decays based on spell level cast
|
||||
bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast
|
||||
bool DivineAura; // invulnerability
|
||||
bool DistanceRemoval; // Check if Cancle if Moved effect is present
|
||||
int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
|
||||
//bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect
|
||||
//bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect
|
||||
int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot
|
||||
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%
|
||||
|
||||
121
zone/effects.cpp
121
zone/effects.cpp
@ -62,7 +62,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
|
||||
int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
|
||||
if (spells[spell_id].targettype == ST_Self)
|
||||
return value;
|
||||
|
||||
@ -74,22 +74,24 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
|
||||
if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
|
||||
value -= (GetLevel() - 40) * 20;
|
||||
|
||||
|
||||
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
|
||||
|
||||
int chance = RuleI(Spells, BaseCritChance);
|
||||
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
|
||||
|
||||
if (chance > 0){
|
||||
|
||||
|
||||
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
|
||||
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.
|
||||
|
||||
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
|
||||
chance = 100;
|
||||
|
||||
|
||||
if (MakeRandomInt(1,100) <= chance){
|
||||
Critical = true;
|
||||
ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
|
||||
@ -97,58 +99,60 @@ 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;
|
||||
}
|
||||
|
||||
ratio += RuleI(Spells, WizCritRatio); //Default is zero
|
||||
|
||||
|
||||
if (Critical){
|
||||
|
||||
value = value_BaseEffect*ratio/100;
|
||||
value = value_BaseEffect*ratio/100;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
|
||||
if (target) {
|
||||
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
|
||||
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
|
||||
|
||||
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value);
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
|
||||
OTHER_CRIT_BLAST, GetName(), itoa(-value));
|
||||
Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value));
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
value = value_BaseEffect;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value = value_BaseEffect;
|
||||
|
||||
if (target) {
|
||||
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
|
||||
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
|
||||
if (target) {
|
||||
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
|
||||
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
@ -255,7 +259,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
|
||||
int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
|
||||
if (target == nullptr)
|
||||
target = this;
|
||||
|
||||
@ -263,54 +267,57 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
int16 chance = 0;
|
||||
int8 modifier = 1;
|
||||
bool Critical = false;
|
||||
|
||||
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
|
||||
|
||||
|
||||
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
|
||||
|
||||
value = value_BaseEffect;
|
||||
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
|
||||
|
||||
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
|
||||
|
||||
// Instant Heals
|
||||
if(spells[spell_id].buffduration < 1) {
|
||||
|
||||
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
|
||||
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
|
||||
|
||||
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
|
||||
|
||||
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
|
||||
|
||||
if (spellbonuses.CriticalHealDecay)
|
||||
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
|
||||
|
||||
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
|
||||
|
||||
if(chance && (MakeRandomInt(0,99) < chance)) {
|
||||
Critical = true;
|
||||
modifier = 2; //At present time no critical heal amount modifier SPA exists.
|
||||
}
|
||||
|
||||
|
||||
value *= modifier;
|
||||
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
|
||||
value += GetFocusEffect(focusFcHealAmt, spell_id);
|
||||
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
|
||||
|
||||
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
|
||||
value += GetFocusEffect(focusFcHealAmt, spell_id);
|
||||
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
|
||||
|
||||
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
|
||||
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
|
||||
|
||||
value += value*target->GetHealRate(spell_id, this)/100;
|
||||
value += value*target->GetHealRate(spell_id, this)/100;
|
||||
|
||||
if (Critical)
|
||||
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value);
|
||||
if (Critical) {
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
|
||||
OTHER_CRIT_HEAL, GetName(), itoa(value));
|
||||
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
|
||||
else {
|
||||
|
||||
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
|
||||
|
||||
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
|
||||
|
||||
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
|
||||
|
||||
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
|
||||
|
||||
if (spellbonuses.CriticalRegenDecay)
|
||||
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
|
||||
|
||||
|
||||
if(chance && (MakeRandomInt(0,99) < chance))
|
||||
return (value * 2);
|
||||
}
|
||||
@ -321,6 +328,12 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
|
||||
{
|
||||
//FrenziedDevastation doubles mana cost of all DD spells
|
||||
int16 FrenziedDevastation = itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
|
||||
if (FrenziedDevastation && IsPureNukeSpell(spell_id))
|
||||
cost *= 2;
|
||||
|
||||
// Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell
|
||||
if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
|
||||
{
|
||||
@ -426,7 +439,7 @@ int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
|
||||
|
||||
// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
|
||||
// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
|
||||
if (!IsShortDurationBuff(spell_id) ||
|
||||
if (!(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
|
||||
IsFearSpell(spell_id) ||
|
||||
IsCharmSpell(spell_id) ||
|
||||
IsMezSpell(spell_id) ||
|
||||
|
||||
@ -632,6 +632,37 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
perl->eval(cmd.c_str());
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
|
||||
{
|
||||
std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client";
|
||||
std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc";
|
||||
std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem";
|
||||
std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list";
|
||||
if(clear_vars_.find(cl) != clear_vars_.end()) {
|
||||
std::string eval_str = cl;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(np) != clear_vars_.end()) {
|
||||
std::string eval_str = np;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(qi) != clear_vars_.end()) {
|
||||
std::string eval_str = qi;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(enl) != clear_vars_.end()) {
|
||||
std::string eval_str = enl;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
char namebuf[64];
|
||||
|
||||
//init a couple special vars: client, npc, entity_list
|
||||
@ -670,11 +701,16 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
ret_value = perl->dosub(std::string(pkgprefix).append("::").append(event).c_str());
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
{
|
||||
std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client";
|
||||
std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc";
|
||||
std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem";
|
||||
std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list";
|
||||
clear_vars_[cl] = 1;
|
||||
clear_vars_[np] = 1;
|
||||
clear_vars_[qi] = 1;
|
||||
clear_vars_[enl] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
} catch(const char * err) {
|
||||
@ -687,12 +723,37 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
error += "::";
|
||||
error += event;
|
||||
error += " - ";
|
||||
error += err;
|
||||
if(strlen(err) > 0)
|
||||
error += err;
|
||||
AddError(error);
|
||||
}
|
||||
}
|
||||
|
||||
quest_manager.EndQuest();
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
if(!quest_manager.QuestsRunning()) {
|
||||
auto iter = clear_vars_.begin();
|
||||
std::string eval_str;
|
||||
while(iter != clear_vars_.end()) {
|
||||
eval_str += iter->first;
|
||||
eval_str += " = undef;";
|
||||
++iter;
|
||||
}
|
||||
clear_vars_.clear();
|
||||
|
||||
try {
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
catch (const char * err) {
|
||||
std::string error = "Script clear error: ";
|
||||
if (strlen(err) > 0)
|
||||
error += err;
|
||||
AddError(error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
|
||||
@ -114,6 +114,7 @@ private:
|
||||
|
||||
std::map<std::string, std::string> vars_;
|
||||
SV *_empty_sv;
|
||||
std::map<std::string, int> clear_vars_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
101
zone/entity.cpp
101
zone/entity.cpp
@ -1012,6 +1012,22 @@ Mob *EntityList::GetMobByNpcTypeID(uint32 get_id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id)
|
||||
{
|
||||
if (get_id == 0 || npc_list.empty())
|
||||
return false;
|
||||
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
// Mobs will have a 0 as their GetID() if they're dead
|
||||
if (it->second->GetNPCTypeID() == get_id && it->second->GetID() != 0)
|
||||
return true;
|
||||
++it;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *EntityList::GetObjectByDBID(uint32 id)
|
||||
{
|
||||
if (id == 0 || object_list.empty())
|
||||
@ -1891,6 +1907,24 @@ void EntityList::MessageClose_StringID(Mob *sender, bool skipsender, float dist,
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::FilteredMessageClose_StringID(Mob *sender, bool skipsender,
|
||||
float dist, uint32 type, eqFilterType filter, uint32 string_id,
|
||||
const char *message1, const char *message2, const char *message3,
|
||||
const char *message4, const char *message5, const char *message6,
|
||||
const char *message7, const char *message8, const char *message9)
|
||||
{
|
||||
Client *c;
|
||||
float dist2 = dist * dist;
|
||||
|
||||
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
|
||||
c = it->second;
|
||||
if (c && c->DistNoRoot(*sender) <= dist2 && (!skipsender || c != sender))
|
||||
c->FilteredMessage_StringID(sender, type, filter, string_id,
|
||||
message1, message2, message3, message4, message5,
|
||||
message6, message7, message8, message9);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9)
|
||||
{
|
||||
Client *c;
|
||||
@ -1902,6 +1936,23 @@ void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uin
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::FilteredMessage_StringID(Mob *sender, bool skipsender,
|
||||
uint32 type, eqFilterType filter, uint32 string_id,
|
||||
const char *message1, const char *message2, const char *message3,
|
||||
const char *message4, const char *message5, const char *message6,
|
||||
const char *message7, const char *message8, const char *message9)
|
||||
{
|
||||
Client *c;
|
||||
|
||||
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
|
||||
c = it->second;
|
||||
if (c && (!skipsender || c != sender))
|
||||
c->FilteredMessage_StringID(sender, type, filter, string_id,
|
||||
message1, message2, message3, message4, message5, message6,
|
||||
message7, message8, message9);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
@ -2965,6 +3016,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
{
|
||||
NPC *cur = nullptr;
|
||||
uint16 count = 0;
|
||||
std::list<NPC *> npc_sub_list;
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
cur = it->second;
|
||||
@ -2974,11 +3026,13 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
continue;
|
||||
}
|
||||
if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) {
|
||||
npc_sub_list.push_back(cur);
|
||||
++count;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
if (thedam > 1) {
|
||||
if (count > 0)
|
||||
thedam /= count;
|
||||
@ -2988,32 +3042,26 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
}
|
||||
|
||||
cur = nullptr;
|
||||
it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
cur = it->second;
|
||||
if (!cur->CheckAggro(target)) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
auto sit = npc_sub_list.begin();
|
||||
while (sit != npc_sub_list.end()) {
|
||||
cur = *sit;
|
||||
|
||||
if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) {
|
||||
if (cur->IsPet()) {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
}
|
||||
if (cur->IsPet()) {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
}
|
||||
} else {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
} else {
|
||||
cur->AddToHateList(caster, thedam * 0.33);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
} else {
|
||||
cur->AddToHateList(caster, thedam * 0.33);
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
++sit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3367,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;
|
||||
}
|
||||
|
||||
|
||||
@ -135,6 +135,7 @@ public:
|
||||
inline Mob *GetMobID(uint16 id) { return(GetMob(id)); } //for perl
|
||||
Mob *GetMob(const char* name);
|
||||
Mob *GetMobByNpcTypeID(uint32 get_id);
|
||||
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
|
||||
Mob *GetTargetForVirus(Mob* spreader);
|
||||
inline NPC *GetNPCByID(uint16 id)
|
||||
{ return npc_list.count(id) ? npc_list.at(id) : nullptr; }
|
||||
@ -266,7 +267,9 @@ public:
|
||||
void MessageStatus(uint32 to_guilddbid, int to_minstatus, uint32 type, const char* message, ...);
|
||||
void MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...);
|
||||
void Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
|
||||
void FilteredMessage_StringID(Mob *sender, bool skipsender, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
|
||||
void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
|
||||
void FilteredMessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
|
||||
void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message);
|
||||
void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...);
|
||||
void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...);
|
||||
|
||||
11
zone/exp.cpp
11
zone/exp.cpp
@ -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
|
||||
|
||||
@ -53,7 +53,10 @@ void Mob::CheckFlee() {
|
||||
|
||||
//see if were possibly hurt enough
|
||||
float ratio = GetHPRatio();
|
||||
if(ratio >= RuleI(Combat, FleeHPRatio))
|
||||
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
||||
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
||||
|
||||
if(ratio >= fleeratio)
|
||||
return;
|
||||
|
||||
//we might be hurt enough, check con now..
|
||||
@ -77,25 +80,24 @@ void Mob::CheckFlee() {
|
||||
switch(con) {
|
||||
//these values are not 100% researched
|
||||
case CON_GREEN:
|
||||
run_ratio = RuleI(Combat, FleeHPRatio);
|
||||
run_ratio = fleeratio;
|
||||
break;
|
||||
case CON_LIGHTBLUE:
|
||||
run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10;
|
||||
run_ratio = fleeratio * 9 / 10;
|
||||
break;
|
||||
case CON_BLUE:
|
||||
run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10;
|
||||
run_ratio = fleeratio * 8 / 10;
|
||||
break;
|
||||
default:
|
||||
run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10;
|
||||
run_ratio = fleeratio * 7 / 10;
|
||||
break;
|
||||
}
|
||||
if(ratio < run_ratio)
|
||||
{
|
||||
if (RuleB(Combat, FleeIfNotAlone) ||
|
||||
(!RuleB(Combat, FleeIfNotAlone) &&
|
||||
(entity_list.GetHatedCount(hate_top, this) == 0)))
|
||||
GetSpecialAbility(ALWAYS_FLEE) ||
|
||||
(!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0)))
|
||||
StartFleeing();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +112,9 @@ void Mob::ProcessFlee() {
|
||||
}
|
||||
|
||||
//see if we are still dying, if so, do nothing
|
||||
if(GetHPRatio() < (float)RuleI(Combat, FleeHPRatio))
|
||||
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
||||
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
||||
if(GetHPRatio() < fleeratio)
|
||||
return;
|
||||
|
||||
//we are not dying anymore... see what we do next
|
||||
@ -128,19 +132,19 @@ void Mob::ProcessFlee() {
|
||||
float Mob::GetFearSpeed() {
|
||||
if(flee_mode) {
|
||||
//we know ratio < FLEE_HP_RATIO
|
||||
float speed = GetRunspeed();
|
||||
float speed = GetBaseRunspeed();
|
||||
float ratio = GetHPRatio();
|
||||
float multiplier = RuleR(Combat, FleeMultiplier);
|
||||
|
||||
// mob's movement will halt with a decent snare at HP specified by rule.
|
||||
if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) {
|
||||
return 0.0001f;
|
||||
}
|
||||
if(GetSnaredAmount() > 40)
|
||||
multiplier = multiplier / 6.0f;
|
||||
|
||||
if (ratio < FLEE_HP_MINSPEED)
|
||||
ratio = FLEE_HP_MINSPEED;
|
||||
|
||||
speed = speed * 0.5 * ratio / 100;
|
||||
speed = speed * ratio * multiplier / 100;
|
||||
|
||||
//NPC will eventually stop. Snares speeds this up.
|
||||
if(speed < 0.09)
|
||||
speed = 0.0001f;
|
||||
|
||||
return(speed);
|
||||
}
|
||||
return(GetRunspeed());
|
||||
|
||||
@ -200,124 +200,399 @@ bool Client::CheckLoreConflict(const Item_Struct* item) {
|
||||
return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID);
|
||||
}
|
||||
|
||||
void Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
|
||||
bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
|
||||
// TODO: update calling methods and script apis to handle a failure return
|
||||
|
||||
const Item_Struct* item = database.GetItem(item_id);
|
||||
|
||||
if (item == nullptr) {
|
||||
Message(0, "No such item: %i", item_id);
|
||||
return;
|
||||
} else {
|
||||
// if the item is stackable and the charge amount is -1 or 0 then set to 1 charge.
|
||||
// removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant
|
||||
if (charges <= 0 && item->Stackable) {
|
||||
charges = 1;
|
||||
// if the charges is -1, then no charge value was passed in set to max charges
|
||||
} else if(charges == -1) {
|
||||
charges = item->MaxCharges;
|
||||
// in any other situation just use charges as passed
|
||||
}
|
||||
// make sure the item exists
|
||||
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);
|
||||
|
||||
return false;
|
||||
}
|
||||
// Checking to see if the Item is lore or not.
|
||||
bool foundlore = CheckLoreConflict(item);
|
||||
// check that there is not a lore conflict between base item and existing inventory
|
||||
else if(CheckLoreConflict(item)) {
|
||||
// DuplicateLoreMessage(item_id);
|
||||
Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id);
|
||||
|
||||
//TODO: check for lore conflict on augments
|
||||
return false;
|
||||
}
|
||||
// check to make sure we are augmenting an augmentable item
|
||||
else if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) {
|
||||
Message(13, "You can not augment an augment or a non-common class item.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
// Checking to see if it is a GM only Item or not.
|
||||
//bool foundgm = (item->gm && (this->Admin() < 100));
|
||||
bool foundgm = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory.
|
||||
ItemInst* inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
// Corrected the augment references to reflect augment name/id instead of base item name/id
|
||||
if (aug1) {
|
||||
const Item_Struct* augitem1 = database.GetItem(aug1);
|
||||
if (augitem1) {
|
||||
if (!CheckLoreConflict(augitem1)) {
|
||||
inst->PutAugment(&database, 0, aug1);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug2) {
|
||||
const Item_Struct* augitem2 = database.GetItem(aug2);
|
||||
if (augitem2) {
|
||||
if (!CheckLoreConflict(augitem2)) {
|
||||
inst->PutAugment(&database, 1, aug2);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug3) {
|
||||
const Item_Struct* augitem3 = database.GetItem(aug3);
|
||||
if (augitem3) {
|
||||
if (!CheckLoreConflict(augitem3)) {
|
||||
inst->PutAugment(&database, 2, aug3);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug4) {
|
||||
const Item_Struct* augitem4 = database.GetItem(aug4);
|
||||
if (augitem4) {
|
||||
if (!CheckLoreConflict(augitem4)) {
|
||||
inst->PutAugment(&database, 3, aug4);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug5) {
|
||||
const Item_Struct* augitem5 = database.GetItem(aug5);
|
||||
if (augitem5) {
|
||||
if (!CheckLoreConflict(augitem5)) {
|
||||
inst->PutAugment(&database, 4, aug5);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attuned) {
|
||||
if (inst->GetItem()->Attuneable) {
|
||||
inst->SetInstNoDrop(true);
|
||||
}
|
||||
}
|
||||
if (to_slot == SLOT_CURSOR)
|
||||
{
|
||||
//inst->SetCharges(
|
||||
PushItemOnCursor(*inst);
|
||||
// Send item packet to user
|
||||
SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
PutItemInInventory(to_slot, *inst, true);
|
||||
}
|
||||
safe_delete(inst);
|
||||
// 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.
|
||||
|
||||
if ((RuleB(Character, EnableDiscoveredItems)))
|
||||
{
|
||||
if(!GetGM() && !IsDiscovered(item_id))
|
||||
DiscoverItem(item_id);
|
||||
// check to make sure we are a GM if the item is GM-only
|
||||
/*
|
||||
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 };
|
||||
|
||||
uint32 classes = item->Classes;
|
||||
uint32 races = item->Races;
|
||||
uint32 slots = item->Slots;
|
||||
|
||||
bool enforcewear = RuleB(Inventory, EnforceAugmentWear);
|
||||
bool enforcerestr = RuleB(Inventory, EnforceAugmentRestriction);
|
||||
bool enforceusable = RuleB(Inventory, EnforceAugmentUsability);
|
||||
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
const Item_Struct* augtest = database.GetItem(augments[iter]);
|
||||
|
||||
if(augtest == nullptr) {
|
||||
if(augments[iter]) {
|
||||
Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check that there is not a lore conflict between augment and existing inventory
|
||||
if(CheckLoreConflict(augtest)) {
|
||||
// DuplicateLoreMessage(augtest->ID);
|
||||
Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID);
|
||||
|
||||
return false;
|
||||
}
|
||||
// check that augment is an actual augment
|
||||
else if(augtest->AugType == 0) {
|
||||
Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Same as GM check above
|
||||
|
||||
// check to make sure we are a GM if the augment is GM-only
|
||||
/*
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
||||
// check for augment type allowance
|
||||
if(enforcewear) {
|
||||
if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) {
|
||||
Message(13, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with an unacceptable augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(item->AugSlotVisible[iter] == 0) {
|
||||
Message(13, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an unevolved item with augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check for augment to item restriction
|
||||
if(enforcerestr) {
|
||||
bool restrictfail = false;
|
||||
uint8 it = item->ItemType;
|
||||
|
||||
switch(augtest->AugRestrict) {
|
||||
case AugRestrAny:
|
||||
break;
|
||||
case AugRestrArmor:
|
||||
switch(it) {
|
||||
case ItemTypeArmor:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrWeapons:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemType1HPiercing:
|
||||
case ItemTypeMartial:
|
||||
case ItemType2HSlash:
|
||||
case ItemType2HBlunt:
|
||||
case ItemType2HPiercing:
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HWeapons:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemType1HPiercing:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HWeapons:
|
||||
switch(it) {
|
||||
case ItemType2HSlash:
|
||||
case ItemType2HBlunt:
|
||||
case ItemType2HPiercing:
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HSlash:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HBlunt:
|
||||
switch(it) {
|
||||
case ItemType1HBlunt:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrPiercing:
|
||||
switch(it) {
|
||||
case ItemType1HPiercing:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrHandToHand:
|
||||
switch(it) {
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HSlash:
|
||||
switch(it) {
|
||||
case ItemType2HSlash:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HBlunt:
|
||||
switch(it) {
|
||||
case ItemType2HBlunt:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HPierce:
|
||||
switch(it) {
|
||||
case ItemType2HPiercing:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrBows:
|
||||
switch(it) {
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrShields:
|
||||
switch(it) {
|
||||
case ItemTypeShield:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HSlash1HBluntOrHandToHand:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HBluntOrHandToHand:
|
||||
switch(it) {
|
||||
case ItemType1HBlunt:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// These 3 are in-work
|
||||
case AugRestrUnknown1:
|
||||
case AugRestrUnknown2:
|
||||
case AugRestrUnknown3:
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(restrictfail) {
|
||||
Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(enforceusable) {
|
||||
// check for class usability
|
||||
if(item->Classes && !(classes &= augtest->Classes)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for race usability
|
||||
if(item->Races && !(races &= augtest->Races)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for slot usability
|
||||
if(item->Slots && !(slots &= augtest->Slots)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it.
|
||||
if (foundlore){
|
||||
DuplicateLoreMessage(item_id);
|
||||
//Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
|
||||
}
|
||||
else if (foundgm)
|
||||
Message(0, "You are not a GM to summon this item");
|
||||
|
||||
// validation passed..so, set the charges and create the actual item
|
||||
|
||||
// if the item is stackable and the charge amount is -1 or 0 then set to 1 charge.
|
||||
// removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant
|
||||
if(charges <= 0 && item->Stackable)
|
||||
charges = 1;
|
||||
|
||||
// if the charges is -1, then no charge value was passed in set to max charges
|
||||
else if(charges == -1)
|
||||
charges = item->MaxCharges;
|
||||
|
||||
// in any other situation just use charges as passed
|
||||
|
||||
ItemInst* inst = database.CreateItem(item, charges);
|
||||
|
||||
if(inst == nullptr) {
|
||||
Message(13, "An unknown server error has occurred and your item was not created.");
|
||||
// this goes to logfile since this is a major error
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// add any validated augments
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
if(augments[iter])
|
||||
inst->PutAugment(&database, iter, augments[iter]);
|
||||
}
|
||||
|
||||
// attune item
|
||||
if(attuned && inst->GetItem()->Attuneable)
|
||||
inst->SetInstNoDrop(true);
|
||||
|
||||
// check to see if item is usable in requested slot
|
||||
if(enforceusable && (((to_slot >= 0) && (to_slot <= 21)) || (to_slot == 9999))) {
|
||||
uint32 slottest = (to_slot == 9999) ? 22 : to_slot;
|
||||
|
||||
if(!(slots & ((uint32)1 << slottest))) {
|
||||
Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to equip an item unusable in slot %u - moved to cursor.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
to_slot = SLOT_CURSOR;
|
||||
}
|
||||
}
|
||||
|
||||
// put item into inventory
|
||||
if(to_slot == SLOT_CURSOR) {
|
||||
PushItemOnCursor(*inst);
|
||||
SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
|
||||
}
|
||||
else {
|
||||
PutItemInInventory(to_slot, *inst, true);
|
||||
}
|
||||
|
||||
safe_delete(inst);
|
||||
|
||||
// discover item and any augments
|
||||
if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) {
|
||||
if(!IsDiscovered(item_id))
|
||||
DiscoverItem(item_id);
|
||||
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
if(augments[iter] && !IsDiscovered(augments[iter]))
|
||||
DiscoverItem(augments[iter]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
|
||||
@ -1120,7 +1395,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
//verify shared bank transactions in the database
|
||||
if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) {
|
||||
if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) {
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName());
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", GetName(), account_name);
|
||||
DeleteItemInInventory(dst_slot_id,0,true);
|
||||
return(false);
|
||||
}
|
||||
@ -1135,7 +1410,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
}
|
||||
if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) {
|
||||
if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) {
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName());
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", GetName(), account_name);
|
||||
DeleteItemInInventory(src_slot_id,0,true);
|
||||
return(false);
|
||||
}
|
||||
|
||||
@ -560,6 +560,11 @@ void Lua_Client::UnscribeSpellAll(bool update_client) {
|
||||
self->UnscribeSpellAll(update_client);
|
||||
}
|
||||
|
||||
void Lua_Client::TrainDisc(int itemid) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->TrainDiscipline(itemid);
|
||||
}
|
||||
|
||||
void Lua_Client::UntrainDisc(int slot) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->UntrainDisc(slot);
|
||||
@ -1352,6 +1357,7 @@ luabind::scope lua_register_client() {
|
||||
.def("UnscribeSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnscribeSpell)
|
||||
.def("UnscribeSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnscribeSpellAll)
|
||||
.def("UnscribeSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnscribeSpellAll)
|
||||
.def("TrainDisc", (void(Lua_Client::*)(int))&Lua_Client::TrainDisc)
|
||||
.def("UntrainDisc", (void(Lua_Client::*)(int))&Lua_Client::UntrainDisc)
|
||||
.def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc)
|
||||
.def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll)
|
||||
|
||||
@ -138,6 +138,7 @@ public:
|
||||
void UnscribeSpell(int slot, bool update_client);
|
||||
void UnscribeSpellAll();
|
||||
void UnscribeSpellAll(bool update_client);
|
||||
void TrainDisc(int itemid);
|
||||
void UntrainDisc(int slot);
|
||||
void UntrainDisc(int slot, bool update_client);
|
||||
void UntrainDiscAll();
|
||||
|
||||
@ -65,6 +65,11 @@ Lua_Mob Lua_EntityList::GetMobByNpcTypeID(int npc_type) {
|
||||
return Lua_Mob(self->GetMobByNpcTypeID(npc_type));
|
||||
}
|
||||
|
||||
bool Lua_EntityList::IsMobSpawnedByNpcTypeID(int npc_type) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsMobSpawnedByNpcTypeID(npc_type);
|
||||
}
|
||||
|
||||
Lua_NPC Lua_EntityList::GetNPCByID(int id) {
|
||||
Lua_Safe_Call_Class(Lua_NPC);
|
||||
return Lua_NPC(self->GetNPCByID(id));
|
||||
@ -420,6 +425,7 @@ luabind::scope lua_register_entity_list() {
|
||||
.def("GetMob", (Lua_Mob(Lua_EntityList::*)(const char*))&Lua_EntityList::GetMob)
|
||||
.def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob)
|
||||
.def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID)
|
||||
.def("IsMobSpawnedByNpcTypeID", (bool(Lua_EntityList::*)(int))&Lua_EntityList::IsMobSpawnedByNpcTypeID)
|
||||
.def("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID)
|
||||
.def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID)
|
||||
.def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName)
|
||||
|
||||
@ -51,6 +51,7 @@ public:
|
||||
Lua_Mob GetMob(const char *name);
|
||||
Lua_Mob GetMob(int id);
|
||||
Lua_Mob GetMobByNpcTypeID(int npc_type);
|
||||
bool IsMobSpawnedByNpcTypeID(int npc_type);
|
||||
Lua_NPC GetNPCByID(int id);
|
||||
Lua_NPC GetNPCByNPCTypeID(int npc_type);
|
||||
Lua_Client GetClientByName(const char *name);
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "QuestParserCollection.h"
|
||||
#include "questmgr.h"
|
||||
#include "QGlobals.h"
|
||||
#include "../common/timer.h"
|
||||
|
||||
struct Events { };
|
||||
struct Factions { };
|
||||
@ -167,14 +168,38 @@ void lua_set_timer(const char *timer, int time_ms) {
|
||||
quest_manager.settimerMS(timer, time_ms);
|
||||
}
|
||||
|
||||
void lua_set_timer(const char *timer, int time_ms, Lua_ItemInst inst) {
|
||||
quest_manager.settimerMS(timer, time_ms, inst);
|
||||
}
|
||||
|
||||
void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) {
|
||||
quest_manager.settimerMS(timer, time_ms, mob);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer) {
|
||||
quest_manager.stoptimer(timer);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer, Lua_ItemInst inst) {
|
||||
quest_manager.stoptimer(timer, inst);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer, Lua_Mob mob) {
|
||||
quest_manager.stoptimer(timer, mob);
|
||||
}
|
||||
|
||||
void lua_stop_all_timers() {
|
||||
quest_manager.stopalltimers();
|
||||
}
|
||||
|
||||
void lua_stop_all_timers(Lua_ItemInst inst) {
|
||||
quest_manager.stopalltimers(inst);
|
||||
}
|
||||
|
||||
void lua_stop_all_timers(Lua_Mob mob) {
|
||||
quest_manager.stopalltimers(mob);
|
||||
}
|
||||
|
||||
void lua_depop() {
|
||||
quest_manager.depop(0);
|
||||
}
|
||||
@ -658,6 +683,14 @@ void lua_assign_raid_to_instance(uint32 instance_id) {
|
||||
quest_manager.AssignRaidToInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_remove_from_instance(uint32 instance_id) {
|
||||
quest_manager.RemoveFromInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_remove_all_from_instance(uint32 instance_id) {
|
||||
quest_manager.RemoveAllFromInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_flag_instance_by_group_leader(uint32 zone, uint32 version) {
|
||||
quest_manager.FlagInstanceByGroupLeader(zone, version);
|
||||
}
|
||||
@ -1064,6 +1097,13 @@ void lua_clear_npctype_cache(int npctype_id) {
|
||||
quest_manager.ClearNPCTypeCache(npctype_id);
|
||||
}
|
||||
|
||||
double lua_clock() {
|
||||
timeval read_time;
|
||||
gettimeofday(&read_time, nullptr);
|
||||
uint32 t = read_time.tv_sec * 1000 + read_time.tv_usec / 1000;
|
||||
return static_cast<double>(t) / 1000.0;
|
||||
}
|
||||
|
||||
luabind::scope lua_register_general() {
|
||||
return luabind::namespace_("eq")
|
||||
[
|
||||
@ -1083,9 +1123,15 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("spawn_from_spawn2", (Lua_Mob(*)(uint32))&lua_spawn_from_spawn2),
|
||||
luabind::def("enable_spawn2", &lua_enable_spawn2),
|
||||
luabind::def("disable_spawn2", &lua_disable_spawn2),
|
||||
luabind::def("set_timer", &lua_set_timer),
|
||||
luabind::def("stop_timer", &lua_stop_timer),
|
||||
luabind::def("stop_all_timers", &lua_stop_all_timers),
|
||||
luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer),
|
||||
luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer),
|
||||
luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer),
|
||||
luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers),
|
||||
luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers),
|
||||
luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers),
|
||||
luabind::def("depop", (void(*)(void))&lua_depop),
|
||||
luabind::def("depop", (void(*)(int))&lua_depop),
|
||||
luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer),
|
||||
@ -1187,6 +1233,8 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("assign_to_instance", &lua_assign_to_instance),
|
||||
luabind::def("assign_group_to_instance", &lua_assign_group_to_instance),
|
||||
luabind::def("assign_raid_to_instance", &lua_assign_raid_to_instance),
|
||||
luabind::def("remove_from_instance", &lua_remove_from_instance),
|
||||
luabind::def("remove_all_from_instance", &lua_remove_all_from_instance),
|
||||
luabind::def("flag_instance_by_group_leader", &lua_flag_instance_by_group_leader),
|
||||
luabind::def("flag_instance_by_raid_leader", &lua_flag_instance_by_raid_leader),
|
||||
luabind::def("fly_mode", &lua_fly_mode),
|
||||
@ -1231,7 +1279,8 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("clear_opcode", &lua_clear_opcode),
|
||||
luabind::def("enable_recipe", &lua_enable_recipe),
|
||||
luabind::def("disable_recipe", &lua_disable_recipe),
|
||||
luabind::def("clear_npctype_cache", &lua_clear_npctype_cache)
|
||||
luabind::def("clear_npctype_cache", &lua_clear_npctype_cache),
|
||||
luabind::def("clock", &lua_clock)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -1811,6 +1811,57 @@ void Lua_Mob::SetAppearance(int app, bool ignore_self) {
|
||||
self->SetAppearance(static_cast<EmuAppearance>(app), ignore_self);
|
||||
}
|
||||
|
||||
void Lua_Mob::SetDestructibleObject(bool set) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->SetDestructibleObject(set);
|
||||
}
|
||||
|
||||
bool Lua_Mob::IsImmuneToSpell(int spell_id, Lua_Mob caster) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsImmuneToSpell(spell_id, caster);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySpellID(int spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySpellID(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeByEffect(int effect_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeByEffect(effect_id);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeByEffect(int effect_id, int skipslot) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeByEffect(effect_id, skipslot);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeAll() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeAll();
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySlot(int slot) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySlot(int slot, bool recalc_bonuses) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySlot(slot, recalc_bonuses);
|
||||
}
|
||||
|
||||
int Lua_Mob::CanBuffStack(int spell_id, int caster_level) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->CanBuffStack(spell_id, caster_level);
|
||||
}
|
||||
|
||||
int Lua_Mob::CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->CanBuffStack(spell_id, caster_level, fail_if_overwrite);
|
||||
}
|
||||
|
||||
|
||||
luabind::scope lua_register_mob() {
|
||||
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
|
||||
.def(luabind::constructor<>())
|
||||
@ -2119,7 +2170,17 @@ luabind::scope lua_register_mob() {
|
||||
.def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities)
|
||||
.def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities)
|
||||
.def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance)
|
||||
.def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance);
|
||||
.def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance)
|
||||
.def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject)
|
||||
.def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell)
|
||||
.def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID)
|
||||
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect)
|
||||
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect)
|
||||
.def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll)
|
||||
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot)
|
||||
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
|
||||
.def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack)
|
||||
.def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_special_abilities() {
|
||||
@ -2160,7 +2221,9 @@ luabind::scope lua_register_special_abilities() {
|
||||
luabind::value("leash", static_cast<int>(LEASH)),
|
||||
luabind::value("tether", static_cast<int>(TETHER)),
|
||||
luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)),
|
||||
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT))
|
||||
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT)),
|
||||
luabind::value("always_flee", static_cast<int>(ALWAYS_FLEE)),
|
||||
luabind::value("flee_percent", static_cast<int>(FLEE_PERCENT))
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -344,6 +344,16 @@ public:
|
||||
void ProcessSpecialAbilities(std::string str);
|
||||
void SetAppearance(int app);
|
||||
void SetAppearance(int app, bool ignore_self);
|
||||
void SetDestructibleObject(bool set);
|
||||
bool IsImmuneToSpell(int spell_id, Lua_Mob caster);
|
||||
void BuffFadeBySpellID(int spell_id);
|
||||
void BuffFadeByEffect(int effect_id);
|
||||
void BuffFadeByEffect(int effect_id, int skipslot);
|
||||
void BuffFadeAll();
|
||||
void BuffFadeBySlot(int slot);
|
||||
void BuffFadeBySlot(int slot, bool recalc_bonuses);
|
||||
int CanBuffStack(int spell_id, int caster_level);
|
||||
int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -297,9 +297,9 @@ void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, f
|
||||
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y);
|
||||
}
|
||||
|
||||
void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay) {
|
||||
void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay);
|
||||
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay, mindelay);
|
||||
}
|
||||
|
||||
int Lua_NPC::GetNPCSpellsID() {
|
||||
@ -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")
|
||||
@ -494,7 +504,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot)
|
||||
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding)
|
||||
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox)
|
||||
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32))&Lua_NPC::AI_SetRoambox)
|
||||
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox)
|
||||
.def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID)
|
||||
.def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID)
|
||||
.def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX)
|
||||
@ -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
|
||||
|
||||
@ -85,7 +85,7 @@ public:
|
||||
void SaveGuardSpot(bool clear);
|
||||
bool IsGuarding();
|
||||
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y);
|
||||
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay);
|
||||
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay);
|
||||
int GetNPCSpellsID();
|
||||
int GetSpawnPointID();
|
||||
float GetSpawnPointX();
|
||||
@ -112,7 +112,9 @@ public:
|
||||
int GetAccuracyRating();
|
||||
int GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void MerchantOpenShop();
|
||||
void MerchantCloseShop();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -2824,7 +2824,8 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
|
||||
value = (value * GetSpellScale() / 100);
|
||||
|
||||
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value);
|
||||
entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits,
|
||||
OTHER_CRIT_BLAST, GetName(), itoa(-value));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
110
zone/mob.cpp
110
zone/mob.cpp
@ -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));
|
||||
@ -349,12 +357,6 @@ Mob::Mob(const char* in_name,
|
||||
nextinchpevent = -1;
|
||||
|
||||
TempPets(false);
|
||||
SetHasRune(false);
|
||||
SetHasSpellRune(false);
|
||||
SetHasPartialMeleeRune(false);
|
||||
SetHasPartialSpellRune(false);
|
||||
|
||||
m_hasDeathSaveChance = false;
|
||||
|
||||
m_is_running = false;
|
||||
|
||||
@ -2086,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
|
||||
@ -2456,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) {
|
||||
@ -4346,7 +4361,7 @@ void Mob::MeleeLifeTap(int32 damage) {
|
||||
|
||||
bool Mob::TryReflectSpell(uint32 spell_id)
|
||||
{
|
||||
if (spells[spell_id].not_reflectable)
|
||||
if (!spells[spell_id].reflectable)
|
||||
return false;
|
||||
|
||||
int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance;
|
||||
@ -4357,6 +4372,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;
|
||||
@ -4759,11 +4817,7 @@ int32 Mob::GetItemFactionBonus(uint32 pFactionID) {
|
||||
}
|
||||
|
||||
void Mob::ClearItemFactionBonuses() {
|
||||
std::map <uint32, int32> :: iterator itr;
|
||||
for(itr = item_faction_bonuses.begin(); itr != item_faction_bonuses.end(); ++itr)
|
||||
{
|
||||
item_faction_bonuses.erase(itr->first);
|
||||
}
|
||||
item_faction_bonuses.clear();
|
||||
}
|
||||
|
||||
FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) {
|
||||
|
||||
42
zone/mob.h
42
zone/mob.h
@ -146,6 +146,7 @@ public:
|
||||
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
|
||||
bool CombatRange(Mob* other);
|
||||
virtual inline bool IsBerserk() { return false; } // only clients
|
||||
void RogueEvade(Mob *other);
|
||||
|
||||
//Appearance
|
||||
void SendLevelAppearance();
|
||||
@ -190,7 +191,8 @@ public:
|
||||
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;}
|
||||
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);
|
||||
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);
|
||||
@ -221,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();
|
||||
@ -247,14 +251,6 @@ public:
|
||||
virtual int GetMaxTotalSlots() const { return 0; }
|
||||
virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
|
||||
virtual void UninitializeBuffSlots() { }
|
||||
inline bool HasRune() const { return m_hasRune; }
|
||||
inline bool HasSpellRune() const { return m_hasSpellRune; }
|
||||
inline bool HasPartialMeleeRune() const { return m_hasPartialMeleeRune; }
|
||||
inline bool HasPartialSpellRune() const { return m_hasPartialSpellRune; }
|
||||
inline void SetHasRune(bool hasRune) { m_hasRune = hasRune; }
|
||||
inline void SetHasSpellRune(bool hasSpellRune) { m_hasSpellRune = hasSpellRune; }
|
||||
inline void SetHasPartialMeleeRune(bool hasPartialMeleeRune) { m_hasPartialMeleeRune = hasPartialMeleeRune; }
|
||||
inline void SetHasPartialSpellRune(bool hasPartialSpellRune) { m_hasPartialSpellRune = hasPartialSpellRune; }
|
||||
EQApplicationPacket *MakeBuffsPacket(bool for_target = true);
|
||||
void SendBuffsToClient(Client *c);
|
||||
inline Buffs_Struct* GetBuffs() { return buffs; }
|
||||
@ -297,7 +293,7 @@ public:
|
||||
bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false);
|
||||
inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;}
|
||||
virtual void Heal();
|
||||
virtual void HealDamage(uint32 ammount, Mob* caster = nullptr);
|
||||
virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN);
|
||||
virtual void SetMaxHP() { cur_hp = max_hp; }
|
||||
virtual inline uint16 GetBaseRace() const { return base_race; }
|
||||
virtual inline uint8 GetBaseGender() const { return base_gender; }
|
||||
@ -344,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; }
|
||||
@ -524,6 +521,13 @@ public:
|
||||
virtual void Message_StringID(uint32 type, uint32 string_id, const char* message, const char* message2 = 0,
|
||||
const char* message3 = 0, const char* message4 = 0, const char* message5 = 0, const char* message6 = 0,
|
||||
const char* message7 = 0, const char* message8 = 0, const char* message9 = 0, uint32 distance = 0) { }
|
||||
virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id) { }
|
||||
virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter,
|
||||
uint32 string_id, const char *message1, const char *message2 = nullptr,
|
||||
const char *message3 = nullptr, const char *message4 = nullptr,
|
||||
const char *message5 = nullptr, const char *message6 = nullptr,
|
||||
const char *message7 = nullptr, const char *message8 = nullptr,
|
||||
const char *message9 = nullptr) { }
|
||||
void Say(const char *format, ...);
|
||||
void Say_StringID(uint32 string_id, const char *message3 = 0, const char *message4 = 0, const char *message5 = 0,
|
||||
const char *message6 = 0, const char *message7 = 0, const char *message8 = 0, const char *message9 = 0);
|
||||
@ -593,6 +597,7 @@ public:
|
||||
void MeleeLifeTap(int32 damage);
|
||||
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
|
||||
bool ImprovedTaunt();
|
||||
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
|
||||
|
||||
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
|
||||
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
|
||||
@ -914,6 +919,7 @@ protected:
|
||||
int16 DR;
|
||||
int16 PR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
bool moving;
|
||||
int targeted;
|
||||
bool findable;
|
||||
@ -969,9 +975,10 @@ protected:
|
||||
bool PassLimitClass(uint32 Classes_, uint16 Class_);
|
||||
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0);
|
||||
void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
|
||||
void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
|
||||
void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
|
||||
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on);
|
||||
virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13);
|
||||
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13);
|
||||
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13);
|
||||
int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item);
|
||||
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);
|
||||
@ -983,7 +990,6 @@ protected:
|
||||
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
VERTEX UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
void PrintRoute();
|
||||
void UpdateRuneFlags();
|
||||
|
||||
virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod);
|
||||
|
||||
@ -1039,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;
|
||||
@ -1075,7 +1087,6 @@ protected:
|
||||
bool inWater; // Set to true or false by Water Detection code if enabled by rules
|
||||
bool has_virus; // whether this mob has a viral spell on them
|
||||
uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids
|
||||
int16 rooted_mod; //Modifier to root break chance, defined when root is cast on a target.
|
||||
bool offhand;
|
||||
bool has_shieldequiped;
|
||||
bool has_numhits;
|
||||
@ -1183,11 +1194,6 @@ protected:
|
||||
float tar_vz;
|
||||
float test_vector;
|
||||
|
||||
bool m_hasRune;
|
||||
bool m_hasSpellRune;
|
||||
bool m_hasPartialMeleeRune;
|
||||
bool m_hasPartialSpellRune;
|
||||
bool m_hasDeathSaveChance;
|
||||
uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots
|
||||
int flymode;
|
||||
bool m_targetable;
|
||||
|
||||
17
zone/net.cpp
17
zone/net.cpp
@ -336,8 +336,10 @@ int main(int argc, char** argv) {
|
||||
bool worldwasconnected = worldserver.Connected();
|
||||
EQStream* eqss;
|
||||
EQStreamInterface *eqsi;
|
||||
Timer temp_timer(10);
|
||||
temp_timer.Start();
|
||||
uint8 IDLEZONEUPDATE = 200;
|
||||
uint8 ZONEUPDATE = 10;
|
||||
Timer zoneupdate_timer(ZONEUPDATE);
|
||||
zoneupdate_timer.Start();
|
||||
while(RunLoops) {
|
||||
{ //profiler block to omit the sleep from times
|
||||
|
||||
@ -381,6 +383,13 @@ int main(int argc, char** argv) {
|
||||
entity_list.AddClient(client);
|
||||
}
|
||||
|
||||
if ( numclients < 1 && zoneupdate_timer.GetDuration() != IDLEZONEUPDATE )
|
||||
zoneupdate_timer.SetTimer(IDLEZONEUPDATE);
|
||||
else if ( numclients > 0 && zoneupdate_timer.GetDuration() == IDLEZONEUPDATE )
|
||||
{
|
||||
zoneupdate_timer.SetTimer(ZONEUPDATE);
|
||||
zoneupdate_timer.Trigger();
|
||||
}
|
||||
|
||||
//check for timeouts in other threads
|
||||
timeout_manager.CheckTimeouts();
|
||||
@ -394,7 +403,7 @@ int main(int argc, char** argv) {
|
||||
worldwasconnected = false;
|
||||
}
|
||||
|
||||
if (ZoneLoaded && temp_timer.Check()) {
|
||||
if (ZoneLoaded && zoneupdate_timer.Check()) {
|
||||
{
|
||||
if(net.group_timer.Enabled() && net.group_timer.Check())
|
||||
entity_list.GroupProcess();
|
||||
@ -438,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();
|
||||
|
||||
23
zone/npc.cpp
23
zone/npc.cpp
@ -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;
|
||||
@ -216,6 +218,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
roambox_min_y = -2;
|
||||
roambox_movingto_x = -2;
|
||||
roambox_movingto_y = -2;
|
||||
roambox_min_delay = 1000;
|
||||
roambox_delay = 1000;
|
||||
org_heading = heading;
|
||||
p_depop = false;
|
||||
@ -657,6 +660,9 @@ bool NPC::Process()
|
||||
viral_timer_counter = 0;
|
||||
}
|
||||
|
||||
if(projectile_timer.Check())
|
||||
SpellProjectileEffect();
|
||||
|
||||
if(spellbonuses.GravityEffect == 1) {
|
||||
if(gravity_timer.Check())
|
||||
DoGravityEffect();
|
||||
@ -1526,6 +1532,12 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem
|
||||
case 'i':
|
||||
SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1);
|
||||
break;
|
||||
case 'e':
|
||||
SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1);
|
||||
break;
|
||||
case 'h':
|
||||
SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -1686,7 +1698,14 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
|
||||
HasAllAttacks = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
if(!GetSpecialAbility(ALWAYS_FLEE))
|
||||
HasAllAttacks = false;
|
||||
break;
|
||||
case 'h':
|
||||
if(!GetSpecialAbility(FLEE_PERCENT))
|
||||
HasAllAttacks = false;
|
||||
break;
|
||||
default:
|
||||
HasAllAttacks = false;
|
||||
break;
|
||||
@ -2044,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();
|
||||
@ -265,8 +269,8 @@ public:
|
||||
inline bool IsGuarding() const { return(guard_heading != 0); }
|
||||
void SaveGuardSpotCharm();
|
||||
void RestoreGuardSpotCharm();
|
||||
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500);
|
||||
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500);
|
||||
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
|
||||
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
|
||||
|
||||
//mercenary stuff
|
||||
void LoadMercTypes();
|
||||
@ -430,6 +434,7 @@ protected:
|
||||
float roambox_movingto_x;
|
||||
float roambox_movingto_y;
|
||||
uint32 roambox_delay;
|
||||
uint32 roambox_min_delay;
|
||||
|
||||
uint16 skills[HIGHEST_SKILL+1];
|
||||
uint32 equipment[MAX_WORN_INVENTORY]; //this is an array of item IDs
|
||||
|
||||
@ -5792,6 +5792,30 @@ XS(XS_Client_SetThirst)
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_SendTargetCommand); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SendTargetCommand)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: Client::SendTargetCommand(THIS, in_entid)");
|
||||
{
|
||||
Client * THIS;
|
||||
int32 in_entid = (uint32)SvUV(ST(1));
|
||||
|
||||
if (sv_derived_from(ST(0), "Client")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(Client *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type Client");
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
THIS->SendTargetCommand(in_entid);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_SetConsumption); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SetConsumption)
|
||||
{
|
||||
@ -6109,6 +6133,7 @@ XS(boot_Client)
|
||||
newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$");
|
||||
newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$");
|
||||
newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$");
|
||||
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
@ -150,6 +150,33 @@ XS(XS_EntityList_GetMobByNpcTypeID)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_EntityList_IsMobSpawnedByNpcTypeID); /* prototype pass -Wmissing-prototypes */
|
||||
XS(XS_EntityList_IsMobSpawnedByNpcTypeID)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: EntityList::ValidMobByNpcTypeID(THIS, get_id)");
|
||||
{
|
||||
EntityList * THIS;
|
||||
bool RETVAL;
|
||||
uint32 get_id = (uint32)SvUV(ST(1));
|
||||
|
||||
if (sv_derived_from(ST(0), "EntityList")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(EntityList *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type EntityList");
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->IsMobSpawnedByNpcTypeID(get_id);
|
||||
ST(0) = boolSV(RETVAL);
|
||||
sv_2mortal(ST(0));
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_EntityList_GetNPCByID); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_EntityList_GetNPCByID)
|
||||
{
|
||||
@ -2127,6 +2154,7 @@ XS(boot_EntityList)
|
||||
newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$");
|
||||
newXSproto(strcpy(buf, "IsMobSpawnedByNpcTypeID"), XS_EntityList_IsMobSpawnedByNpcTypeID, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$");
|
||||
|
||||
@ -1433,8 +1433,8 @@ XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_AI_SetRoambox)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 6 || items > 7)
|
||||
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)");
|
||||
if (items < 6 || items > 8)
|
||||
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)");
|
||||
{
|
||||
NPC * THIS;
|
||||
float iDist = (float)SvNV(ST(1));
|
||||
@ -1443,6 +1443,7 @@ XS(XS_NPC_AI_SetRoambox)
|
||||
float iMaxY = (float)SvNV(ST(4));
|
||||
float iMinY = (float)SvNV(ST(5));
|
||||
uint32 iDelay;
|
||||
uint32 iMinDelay;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
@ -1453,13 +1454,20 @@ XS(XS_NPC_AI_SetRoambox)
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
if (items < 7)
|
||||
if (items < 7){
|
||||
iMinDelay = 2500;
|
||||
iDelay = 2500;
|
||||
else {
|
||||
}
|
||||
else if (items < 8){
|
||||
iMinDelay = 2500;
|
||||
iDelay = (uint32)SvUV(ST(6));
|
||||
}
|
||||
else {
|
||||
iDelay = (uint32)SvUV(ST(6));
|
||||
iMinDelay = (uint32)SvUV(ST(7));
|
||||
}
|
||||
|
||||
THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay);
|
||||
THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay, iMinDelay);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
@ -2208,7 +2216,7 @@ XS(boot_NPC)
|
||||
newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$");
|
||||
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$");
|
||||
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$");
|
||||
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$");
|
||||
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$");
|
||||
newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$");
|
||||
newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$");
|
||||
newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$");
|
||||
|
||||
@ -627,7 +627,6 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateRuneFlags();
|
||||
|
||||
//restore their equipment...
|
||||
for(i = 0; i < MAX_WORN_INVENTORY; i++) {
|
||||
|
||||
@ -347,8 +347,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
|
||||
entity_list.AddNPC(npc);
|
||||
entity_list.LimitAddNPC(npc);
|
||||
|
||||
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay)
|
||||
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay);
|
||||
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
|
||||
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
|
||||
if(zone->InstantGrids())
|
||||
{
|
||||
found_spawn->LoadGrid();
|
||||
@ -484,6 +484,29 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) {
|
||||
QTimerList.push_back(QuestTimer(milliseconds, owner, timer_name));
|
||||
}
|
||||
|
||||
void QuestManager::settimerMS(const char *timer_name, int milliseconds, ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->SetTimer(timer_name, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::settimerMS(const char *timer_name, int milliseconds, Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob && cur->name == timer_name)
|
||||
{
|
||||
cur->Timer_.Enable();
|
||||
cur->Timer_.Start(milliseconds, false);
|
||||
return;
|
||||
}
|
||||
++cur;
|
||||
}
|
||||
|
||||
QTimerList.push_back(QuestTimer(milliseconds, mob, timer_name));
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@ -504,6 +527,25 @@ void QuestManager::stoptimer(const char *timer_name) {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name, ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->StopTimer(timer_name);
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name, Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob && cur->name == timer_name) {
|
||||
QTimerList.erase(cur);
|
||||
return;
|
||||
}
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers() {
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@ -523,6 +565,24 @@ void QuestManager::stopalltimers() {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers(ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->ClearTimers();
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers(Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end, tmp;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob)
|
||||
cur = QTimerList.erase(cur);
|
||||
else
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::emote(const char *str) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner) {
|
||||
@ -2557,6 +2617,43 @@ void QuestManager::AssignRaidToInstance(uint16 instance_id)
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::RemoveFromInstance(uint16 instance_id)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
if(initiator) {
|
||||
if(database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) {
|
||||
initiator->Message(MT_Say, "Removed client from instance.");
|
||||
} else {
|
||||
initiator->Message(MT_Say, "Failed to remove client from instance.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::RemoveAllFromInstance(uint16 instance_id)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
if(initiator) {
|
||||
std::list<uint32> charid_list;
|
||||
bool removed_all = true;
|
||||
uint16 fail_count = 0;
|
||||
database.GetCharactersInInstance(instance_id,charid_list);
|
||||
auto iter = charid_list.begin();
|
||||
while(iter != charid_list.end()) {
|
||||
if(!database.RemoveClientFromInstance(instance_id, *iter)) {
|
||||
removed_all = false;
|
||||
++fail_count;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if (removed_all) {
|
||||
initiator->Message(MT_Say, "Removed all players from instance.");
|
||||
} else {
|
||||
// once the expedition system is in, this message it not relevant
|
||||
initiator->Message(MT_Say, "Failed to remove %i player(s) from instance.", fail_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@ -41,6 +41,7 @@ public:
|
||||
|
||||
void StartQuest(Mob *_owner, Client *_initiator = nullptr, ItemInst* _questitem = nullptr);
|
||||
void EndQuest();
|
||||
bool QuestsRunning() { return !quests_running_.empty(); }
|
||||
|
||||
void Process();
|
||||
|
||||
@ -66,9 +67,15 @@ public:
|
||||
void addloot(int item_id, int charges = 0, bool equipitem = true);
|
||||
void Zone(const char *zone_name);
|
||||
void settimer(const char *timer_name, int seconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds, ItemInst *inst);
|
||||
void settimerMS(const char *timer_name, int milliseconds, Mob *mob);
|
||||
void stoptimer(const char *timer_name);
|
||||
void stoptimer(const char *timer_name, ItemInst *inst);
|
||||
void stoptimer(const char *timer_name, Mob *mob);
|
||||
void stopalltimers();
|
||||
void stopalltimers(ItemInst *inst);
|
||||
void stopalltimers(Mob *mob);
|
||||
void emote(const char *str);
|
||||
void shout(const char *str);
|
||||
void shout2(const char *str);
|
||||
@ -211,6 +218,10 @@ public:
|
||||
void AssignToInstance(uint16 instance_id);
|
||||
void AssignGroupToInstance(uint16 instance_id);
|
||||
void AssignRaidToInstance(uint16 instance_id);
|
||||
void RemoveFromInstance(uint16 instance_id);
|
||||
//void RemoveGroupFromInstance(uint16 instance_id); //potentially useful but not implmented at this time.
|
||||
//void RemoveRaidFromInstance(uint16 instance_id); //potentially useful but not implmented at this time.
|
||||
void RemoveAllFromInstance(uint16 instance_id);
|
||||
void MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading);
|
||||
void FlagInstanceByGroupLeader(uint32 zone, int16 version);
|
||||
void FlagInstanceByRaidLeader(uint32 zone, int16 version);
|
||||
|
||||
@ -229,8 +229,8 @@ bool Spawn2::Process() {
|
||||
entity_list.AddNPC(npc);
|
||||
//this limit add must be done after the AddNPC since we need the entity ID.
|
||||
entity_list.LimitAddNPC(npc);
|
||||
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay)
|
||||
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay);
|
||||
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
|
||||
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
|
||||
if(zone->InstantGrids()) {
|
||||
_log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z);
|
||||
LoadGrid();
|
||||
|
||||
@ -35,7 +35,7 @@ SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_lim
|
||||
npc_spawn_limit = in_npc_spawn_limit;
|
||||
}
|
||||
|
||||
SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ) {
|
||||
SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in ) {
|
||||
id = in_id;
|
||||
strn0cpy( name_, name, 120);
|
||||
group_spawn_limit = in_group_spawn_limit;
|
||||
@ -44,6 +44,7 @@ SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, floa
|
||||
roambox[2]=maxy;
|
||||
roambox[3]=miny;
|
||||
roamdist=dist;
|
||||
min_delay=min_delay_in;
|
||||
delay=delay_in;
|
||||
despawn=despawn_in;
|
||||
despawn_timer=despawn_timer_in;
|
||||
@ -150,11 +151,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG
|
||||
|
||||
// CODER new spawn code
|
||||
query = 0;
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
|
||||
{
|
||||
safe_delete_array(query);
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]));
|
||||
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
|
||||
spawn_group_list->AddSpawnGroup(newSpawnGroup);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
@ -205,11 +206,11 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g
|
||||
|
||||
// CODER new spawn code
|
||||
query = 0;
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
|
||||
{
|
||||
safe_delete_array(query);
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]));
|
||||
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
|
||||
spawn_group_list->AddSpawnGroup(newSpawnGroup);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
@ -39,13 +39,14 @@ public:
|
||||
class SpawnGroup
|
||||
{
|
||||
public:
|
||||
SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in );
|
||||
SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in );
|
||||
~SpawnGroup();
|
||||
uint32 GetNPCType();
|
||||
void AddSpawnEntry( SpawnEntry* newEntry );
|
||||
uint32 id;
|
||||
float roamdist;
|
||||
float roambox[4];
|
||||
int min_delay;
|
||||
int delay;
|
||||
int despawn;
|
||||
uint32 despawn_timer;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
@ -830,14 +845,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value);
|
||||
#endif
|
||||
//use resistance value for duration...
|
||||
buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100);
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter))
|
||||
buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter);
|
||||
}
|
||||
|
||||
|
||||
if(RuleB(Combat, EnableFearPathing)){
|
||||
if(IsClient())
|
||||
@ -859,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");
|
||||
@ -991,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;
|
||||
}
|
||||
|
||||
@ -1242,7 +1260,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#endif
|
||||
effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value);
|
||||
buffs[buffslot].melee_rune = effect_value;
|
||||
SetHasRune(true);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1251,17 +1268,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value);
|
||||
#endif
|
||||
if(effect_value > 0) {
|
||||
buffs[buffslot].magic_rune = effect_value;
|
||||
SetHasSpellRune(true);
|
||||
}
|
||||
if(effect_value > 0)
|
||||
buffs[buffslot].magic_rune = effect_value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MitigateMeleeDamage:
|
||||
{
|
||||
buffs[buffslot].melee_rune = GetPartialMeleeRuneAmount(spell_id);
|
||||
SetHasPartialMeleeRune(true);
|
||||
buffs[buffslot].melee_rune = spells[spell_id].max[i];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1279,8 +1294,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
case SE_MitigateSpellDamage:
|
||||
{
|
||||
buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id);
|
||||
SetHasPartialSpellRune(true);
|
||||
buffs[buffslot].magic_rune = spells[spell_id].max[i];
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MitigateDotDamage:
|
||||
{
|
||||
buffs[buffslot].dot_rune = spells[spell_id].max[i];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1296,7 +1316,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SE_DistanceRemoval:
|
||||
{
|
||||
buffs[buffslot].caston_x = int(GetX());
|
||||
buffs[buffslot].caston_y = int(GetY());
|
||||
buffs[buffslot].caston_z = int(GetZ());
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Levitate:
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
@ -1308,6 +1335,20 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_DeathSave: {
|
||||
|
||||
int16 mod = 0;
|
||||
|
||||
if(caster) {
|
||||
mod = caster->aabonuses.UnfailingDivinity +
|
||||
caster->itembonuses.UnfailingDivinity +
|
||||
caster->spellbonuses.UnfailingDivinity;
|
||||
}
|
||||
|
||||
buffs[buffslot].ExtraDIChance = mod;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Illusion:
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
@ -1357,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);
|
||||
@ -1419,10 +1461,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
snprintf(effect_desc, _EDLEN, "Memory Blur: %d", effect_value);
|
||||
#endif
|
||||
int wipechance = spells[spell_id].base[i];
|
||||
int bonus = spellbonuses.IncreaseChanceMemwipe + itembonuses.IncreaseChanceMemwipe + aabonuses.IncreaseChanceMemwipe;
|
||||
int bonus = 0;
|
||||
|
||||
if (caster){
|
||||
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
|
||||
caster->itembonuses.IncreaseChanceMemwipe +
|
||||
caster->aabonuses.IncreaseChanceMemwipe;
|
||||
}
|
||||
|
||||
wipechance += wipechance*bonus/100;
|
||||
|
||||
if(MakeRandomInt(0, 100) < wipechance)
|
||||
if(MakeRandomInt(0, 99) < wipechance)
|
||||
{
|
||||
if(IsAIControlled())
|
||||
{
|
||||
@ -1526,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;
|
||||
}
|
||||
|
||||
@ -1597,12 +1653,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
snprintf(effect_desc, _EDLEN, "Root: %+i", effect_value);
|
||||
#endif
|
||||
rooted = true;
|
||||
rooted_mod = 0;
|
||||
|
||||
if (caster){
|
||||
rooted_mod = caster->aabonuses.RootBreakChance +
|
||||
caster->itembonuses.RootBreakChance +
|
||||
caster->spellbonuses.RootBreakChance;
|
||||
buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance +
|
||||
caster->itembonuses.RootBreakChance +
|
||||
caster->spellbonuses.RootBreakChance;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -1664,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);
|
||||
@ -2361,30 +2422,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_DeathSave: {
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Death Save: %+i", effect_value);
|
||||
#endif
|
||||
uint8 BonusChance = 0;
|
||||
if(caster) {
|
||||
|
||||
BonusChance = caster->aabonuses.UnfailingDivinity +
|
||||
caster->itembonuses.UnfailingDivinity +
|
||||
caster->spellbonuses.UnfailingDivinity;
|
||||
}
|
||||
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
//snprintf(effect_desc, _EDLEN, "Death Save Chance: %+i", SuccessChance);
|
||||
#endif
|
||||
//buffs[buffslot].deathSaveSuccessChance = SuccessChance;
|
||||
//buffs[buffslot].deathsaveCasterAARank = caster->GetAA(aaUnfailingDivinity);
|
||||
buffs[buffslot].deathsaveCasterAARank = BonusChance;
|
||||
//SetDeathSaveChance(true);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SummonAndResAllCorpses:
|
||||
{
|
||||
if(IsClient())
|
||||
@ -2631,7 +2668,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
case SE_MeleeMitigation:
|
||||
case SE_Reflect:
|
||||
case SE_Screech:
|
||||
case SE_SingingSkill:
|
||||
case SE_Amplification:
|
||||
case SE_MagicWeapon:
|
||||
case SE_Hunger:
|
||||
case SE_MagnifyVision:
|
||||
@ -2763,7 +2800,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
case SE_MitigateDamageShield:
|
||||
case SE_FcBaseEffects:
|
||||
case SE_LimitClass:
|
||||
case SE_LimitSpellSubclass:
|
||||
case SE_BlockBehind:
|
||||
case SE_ShieldBlock:
|
||||
case SE_PetCriticalHit:
|
||||
@ -2816,6 +2852,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
case SE_IncreaseChanceMemwipe:
|
||||
case SE_CriticalMend:
|
||||
case SE_LimitCastTimeMax:
|
||||
case SE_TriggerOnReqCaster:
|
||||
case SE_FrenziedDevastation:
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -3219,7 +3257,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
if(caster)
|
||||
effect_value = caster->GetActSpellHealing(spell_id, effect_value);
|
||||
|
||||
HealDamage(effect_value, caster);
|
||||
HealDamage(effect_value, caster, spell_id);
|
||||
//healing aggro would go here; removed for now
|
||||
break;
|
||||
}
|
||||
@ -3277,6 +3315,31 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_WipeHateList:
|
||||
{
|
||||
|
||||
int wipechance = spells[spell_id].base[i];
|
||||
int bonus = 0;
|
||||
|
||||
if (caster){
|
||||
bonus = caster->spellbonuses.IncreaseChanceMemwipe +
|
||||
caster->itembonuses.IncreaseChanceMemwipe +
|
||||
caster->aabonuses.IncreaseChanceMemwipe;
|
||||
}
|
||||
|
||||
wipechance += wipechance*bonus/100;
|
||||
|
||||
if(MakeRandomInt(0, 99) < wipechance)
|
||||
{
|
||||
if(IsAIControlled())
|
||||
{
|
||||
WipeHateList();
|
||||
}
|
||||
Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago...");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Charm: {
|
||||
if (!caster || !PassCharismaCheck(caster, this, spell_id)) {
|
||||
BuffFadeByEffect(SE_Charm);
|
||||
@ -3286,9 +3349,39 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
}
|
||||
|
||||
case SE_Root: {
|
||||
float SpellEffectiveness = ResistSpell(spells[spell_id].resisttype, spell_id, caster);
|
||||
if(SpellEffectiveness < 25) {
|
||||
BuffFadeByEffect(SE_Root);
|
||||
|
||||
/* Root formula derived from extensive personal live parses - Kayen
|
||||
ROOT has a 40% chance to do a resist check to break.
|
||||
Resist check has NO LOWER bounds.
|
||||
If multiple roots on target. Root in first slot will be checked first to break from nukes.
|
||||
If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot.
|
||||
*/
|
||||
|
||||
if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){
|
||||
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true);
|
||||
|
||||
if(resist_check == 100)
|
||||
break;
|
||||
else
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Fear:
|
||||
{
|
||||
if (MakeRandomInt(0, 99) < RuleI(Spells, FearBreakCheckChance)){
|
||||
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster);
|
||||
|
||||
if(resist_check == 100)
|
||||
break;
|
||||
else
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -3378,6 +3471,24 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_DistanceRemoval:
|
||||
{
|
||||
if (spellbonuses.DistanceRemoval){
|
||||
|
||||
int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) +
|
||||
((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
|
||||
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z));
|
||||
|
||||
if (distance > (spells[spell_id].base[i] * spells[spell_id].base[i])){
|
||||
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot , true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// do we need to do anyting here?
|
||||
@ -3640,8 +3751,8 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
|
||||
case SE_Root:
|
||||
{
|
||||
buffs[slot].RootBreakChance = 0;
|
||||
rooted = false;
|
||||
rooted_mod = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -5249,11 +5360,13 @@ bool Mob::TryDeathSave() {
|
||||
|
||||
int SuccessChance = 0;
|
||||
int buffSlot = spellbonuses.DeathSave[1];
|
||||
uint8 UD_HealMod = buffs[buffSlot].deathsaveCasterAARank; //Contains value of UD heal modifier.
|
||||
int16 UD_HealMod = 0;
|
||||
uint32 HealAmt = 300; //Death Pact max Heal
|
||||
|
||||
if(buffSlot >= 0){
|
||||
|
||||
UD_HealMod = buffs[buffSlot].ExtraDIChance;
|
||||
|
||||
SuccessChance = ( (GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; //(CHA Mod Default = 3)
|
||||
|
||||
if (SuccessChance > 95)
|
||||
@ -5318,6 +5431,8 @@ bool Mob::TryDeathSave() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BuffFadeBySlot(buffSlot);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -5337,14 +5452,12 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect)
|
||||
|
||||
float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) {
|
||||
|
||||
ProcBonus = spellbonuses.SpellProcChance + itembonuses.SpellProcChance;
|
||||
ProcChance = 0;
|
||||
|
||||
if(cast_time > 0)
|
||||
{
|
||||
ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f);
|
||||
ProcChance = ProcChance * (float)(ProcRateMod/100);
|
||||
ProcChance = ProcChance+(ProcChance*ProcBonus/100);
|
||||
}
|
||||
return ProcChance;
|
||||
}
|
||||
@ -5580,19 +5693,24 @@ bool Mob::TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier){
|
||||
|
||||
bool Mob::ImprovedTaunt(){
|
||||
|
||||
if (spellbonuses.ImprovedTaunt[2]){
|
||||
if (spellbonuses.ImprovedTaunt[0]){
|
||||
|
||||
if (GetLevel() > spellbonuses.ImprovedTaunt[0])
|
||||
return false;
|
||||
|
||||
target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid);
|
||||
if (spellbonuses.ImprovedTaunt[2] >= 0){
|
||||
|
||||
if (target){
|
||||
SetTarget(target);
|
||||
return true;
|
||||
target = entity_list.GetMob(buffs[spellbonuses.ImprovedTaunt[2]].casterid);
|
||||
|
||||
if (target){
|
||||
SetTarget(target);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if(!TryFadeEffect(spellbonuses.ImprovedTaunt[2]))
|
||||
BuffFadeBySlot(spellbonuses.ImprovedTaunt[2], true); //If caster killed removed effect.
|
||||
}
|
||||
}
|
||||
else
|
||||
BuffFadeBySlot(spellbonuses.ImprovedTaunt[2]); //If caster killed removed effect.
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -5902,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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
359
zone/spells.cpp
359
zone/spells.cpp
@ -123,21 +123,12 @@ void NPC::SpellProcess()
|
||||
{
|
||||
Mob::SpellProcess();
|
||||
|
||||
if(GetSwarmInfo()){
|
||||
Mob *swp_o = GetSwarmInfo()->GetOwner();
|
||||
if(!swp_o)
|
||||
{
|
||||
if (GetSwarmInfo()) {
|
||||
if (GetSwarmInfo()->duration->Check(false))
|
||||
Depop();
|
||||
}
|
||||
|
||||
if(GetSwarmInfo()->duration->Check(false))
|
||||
{
|
||||
Depop();
|
||||
}
|
||||
|
||||
Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target);
|
||||
if(GetSwarmInfo()->target != 0)
|
||||
{
|
||||
if (GetSwarmInfo()->target != 0) {
|
||||
if(!targMob || (targMob && targMob->IsCorpse()))
|
||||
Depop();
|
||||
}
|
||||
@ -220,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++) {
|
||||
@ -445,7 +439,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
|
||||
casting_spell_resist_adjust = resist_adjust;
|
||||
|
||||
mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d", orgcasttime, cast_time, mana_cost);
|
||||
mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d",
|
||||
spell_id, cast_time, orgcasttime, mana_cost);
|
||||
|
||||
// cast time is 0, just finish it right now and be done with it
|
||||
if(cast_time == 0) {
|
||||
@ -461,8 +456,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
if (IsAIControlled())
|
||||
{
|
||||
SetRunAnimSpeed(0);
|
||||
if(this != pMob)
|
||||
this->FaceTarget(pMob);
|
||||
pMob = entity_list.GetMob(target_id);
|
||||
if (pMob && this != pMob)
|
||||
FaceTarget(pMob);
|
||||
}
|
||||
|
||||
// if we got here we didn't fizzle, and are starting our cast
|
||||
@ -561,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);
|
||||
@ -584,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:
|
||||
@ -1816,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);
|
||||
@ -1881,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)
|
||||
@ -1890,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPlayerIllusionSpell(spell_id)
|
||||
&& IsClient()
|
||||
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
||||
@ -2593,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];
|
||||
@ -2637,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",
|
||||
@ -2953,8 +2981,12 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
buffs[emptyslot].numhits = spells[spell_id].numhits;
|
||||
buffs[emptyslot].client = caster ? caster->IsClient() : 0;
|
||||
buffs[emptyslot].persistant_buff = 0;
|
||||
buffs[emptyslot].deathsaveCasterAARank = 0;
|
||||
buffs[emptyslot].deathSaveSuccessChance = 0;
|
||||
buffs[emptyslot].caston_x = 0;
|
||||
buffs[emptyslot].caston_y = 0;
|
||||
buffs[emptyslot].caston_z = 0;
|
||||
buffs[emptyslot].dot_rune = 0;
|
||||
buffs[emptyslot].ExtraDIChance = 0;
|
||||
buffs[emptyslot].RootBreakChance = 0;
|
||||
|
||||
if (level_override > 0) {
|
||||
buffs[emptyslot].UpdateClient = true;
|
||||
@ -3412,14 +3444,25 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
|
||||
// not all unresistable, so changing this to only check certain spells
|
||||
if(IsResistableSpell(spell_id))
|
||||
{
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust);
|
||||
if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id))
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true);
|
||||
else
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust);
|
||||
|
||||
if(spell_effectiveness < 100)
|
||||
{
|
||||
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);
|
||||
@ -4039,7 +4082,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||
// pvp_resist_base
|
||||
// pvp_resist_calc
|
||||
// pvp_resist_cap
|
||||
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck)
|
||||
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot)
|
||||
{
|
||||
|
||||
if(!caster)
|
||||
@ -4078,8 +4121,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
|
||||
//Check for fear resist
|
||||
bool IsFear = false;
|
||||
if(IsFearSpell(spell_id))
|
||||
{
|
||||
IsFear = true;
|
||||
int fear_resist_bonuses = CalcFearResistChance();
|
||||
if(MakeRandomInt(0, 99) < fear_resist_bonuses)
|
||||
{
|
||||
@ -4088,7 +4133,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
if (!CharismaCheck){
|
||||
if (!CharmTick){
|
||||
|
||||
//Check for Spell Effect specific resistance chances (ie AA Mental Fortitude)
|
||||
int se_resist_bonuses = GetSpellEffectResistChance(spell_id);
|
||||
@ -4166,86 +4211,126 @@ 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.
|
||||
//Lulls have a slightly higher chance to resist than normal 15/200 or ~ 7.5%
|
||||
int resist_chance;
|
||||
if(IsHarmonySpell(spell_id))
|
||||
{
|
||||
resist_chance = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_chance = 0;
|
||||
}
|
||||
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)
|
||||
{
|
||||
//For charm chance to break checks, Default 10 CHA = -1 resist mod.
|
||||
int16 cha_resist_modifier = 0;
|
||||
cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness);
|
||||
resist_modifier -= cha_resist_modifier;
|
||||
/*
|
||||
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha)
|
||||
Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist.
|
||||
Mez spells do same initial resist check as a above.
|
||||
Lull spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above.
|
||||
Charisma DOES NOT extend charm durations.
|
||||
Fear resist chance is given a -20 resist modifier if CHA is < 100, from 100-255 it progressively reduces the negative mod to 0.
|
||||
Fears verse undead DO NOT apply a charisma modifer. (Note: unknown Base1 values defined in undead fears do not effect duration).
|
||||
*/
|
||||
int16 charisma = caster->GetCHA();
|
||||
|
||||
if (IsFear && (spells[spell_id].targettype != 10)){
|
||||
|
||||
if (charisma < 100)
|
||||
resist_modifier -= 20;
|
||||
|
||||
else if (charisma <= 255)
|
||||
resist_modifier += (charisma - 100)/8;
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (charisma >= 75){
|
||||
|
||||
if (charisma > RuleI(Spells, CharismaEffectivenessCap))
|
||||
charisma = RuleI(Spells, CharismaEffectivenessCap);
|
||||
|
||||
resist_modifier -= (charisma - 75)/RuleI(Spells, CharismaEffectiveness);
|
||||
}
|
||||
else
|
||||
resist_modifier += ((75 - charisma)/10) * 6; //Increase Resist Chance
|
||||
}
|
||||
}
|
||||
|
||||
//Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this.
|
||||
//Regular resists are used when checking if mob will aggro off of a lull resist.
|
||||
if(!CharismaCheck && IsHarmonySpell(spell_id))
|
||||
target_resist = 15;
|
||||
|
||||
//Add our level, resist and -spell resist modifier to our roll chance
|
||||
resist_chance += level_mod;
|
||||
resist_chance += resist_modifier;
|
||||
@ -4264,6 +4349,26 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
resist_chance = spells[spell_id].MinResist;
|
||||
}
|
||||
|
||||
//Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks.
|
||||
//Minimum resist chance should be caclulated factoring in the RuleI(Spells, CharmBreakCheckChance)
|
||||
if (CharmTick) {
|
||||
|
||||
int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2;
|
||||
|
||||
if (resist_chance < min_charmbreakchance)
|
||||
resist_chance = min_charmbreakchance;
|
||||
}
|
||||
|
||||
//Average root duration agianst mobs with 0% chance to resist on LIVE is ~ 22 ticks (6% resist chance).
|
||||
//Minimum resist chance should be caclulated factoring in the RuleI(Spells, RootBreakCheckChance)
|
||||
if (IsRoot) {
|
||||
|
||||
int min_rootbreakchance = ((100/RuleI(Spells, RootBreakCheckChance))/22 * 100)*2;
|
||||
|
||||
if (resist_chance < min_rootbreakchance)
|
||||
resist_chance = min_rootbreakchance;
|
||||
}
|
||||
|
||||
//Finally our roll
|
||||
int roll = MakeRandomInt(0, 200);
|
||||
if(roll > resist_chance)
|
||||
@ -4329,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;
|
||||
@ -5127,51 +5269,6 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
void Mob::UpdateRuneFlags()
|
||||
{
|
||||
bool Has_SE_Rune = false, Has_SE_AbsorbMagicAtt = false, Has_SE_MitigateMeleeDamage = false, Has_SE_MitigateSpellDamage = false;
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (unsigned int i = 0; i < buff_count; ++i)
|
||||
{
|
||||
if (buffs[i].spellid != SPELL_UNKNOWN)
|
||||
{
|
||||
for (int j = 0; j < EFFECT_COUNT; ++j)
|
||||
{
|
||||
switch(spells[buffs[i].spellid].effectid[j])
|
||||
{
|
||||
case SE_Rune:
|
||||
{
|
||||
Has_SE_Rune = true;
|
||||
break;
|
||||
}
|
||||
case SE_AbsorbMagicAtt:
|
||||
{
|
||||
Has_SE_AbsorbMagicAtt = true;
|
||||
break;
|
||||
}
|
||||
case SE_MitigateMeleeDamage:
|
||||
{
|
||||
Has_SE_MitigateMeleeDamage = true;
|
||||
break;
|
||||
}
|
||||
case SE_MitigateSpellDamage:
|
||||
{
|
||||
Has_SE_MitigateSpellDamage = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetHasRune(Has_SE_Rune);
|
||||
SetHasSpellRune(Has_SE_AbsorbMagicAtt);
|
||||
SetHasPartialMeleeRune(Has_SE_MitigateMeleeDamage);
|
||||
SetHasPartialSpellRune(Has_SE_MitigateSpellDamage);
|
||||
}
|
||||
|
||||
int Client::GetCurrentBuffSlots() const
|
||||
{
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -47,11 +47,11 @@ static inline float ABS(float x) {
|
||||
return(x);
|
||||
}
|
||||
|
||||
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay) {
|
||||
AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay);
|
||||
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) {
|
||||
AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay, iMinDelay);
|
||||
}
|
||||
|
||||
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay) {
|
||||
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay, uint32 iMinDelay) {
|
||||
roambox_distance = iDist;
|
||||
roambox_max_x = iMaxX;
|
||||
roambox_min_x = iMinX;
|
||||
@ -59,6 +59,7 @@ void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, floa
|
||||
roambox_min_y = iMinY;
|
||||
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc
|
||||
roambox_delay = iDelay;
|
||||
roambox_min_delay = iMinDelay;
|
||||
}
|
||||
|
||||
void NPC::DisplayWaypointInfo(Client *c) {
|
||||
|
||||
@ -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++]);
|
||||
@ -1815,7 +1817,7 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
|
||||
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, "
|
||||
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, "
|
||||
"DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);",
|
||||
"dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
|
||||
merc->GetMercID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula,
|
||||
buffs[BuffCount].ticsremaining,
|
||||
CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
@ -1823,8 +1825,12 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
|
||||
CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0,
|
||||
buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune,
|
||||
buffs[BuffCount].deathSaveSuccessChance,
|
||||
buffs[BuffCount].deathsaveCasterAARank, IsPersistent), TempErrorMessageBuffer)) {
|
||||
buffs[BuffCount].dot_rune,
|
||||
buffs[BuffCount].caston_x,
|
||||
IsPersistent,
|
||||
buffs[BuffCount].caston_y,
|
||||
buffs[BuffCount].caston_z,
|
||||
buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) {
|
||||
errorMessage = std::string(TempErrorMessageBuffer);
|
||||
safe_delete(Query);
|
||||
Query = 0;
|
||||
@ -1856,7 +1862,7 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
|
||||
|
||||
bool BuffsLoaded = false;
|
||||
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) {
|
||||
if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) {
|
||||
errorMessage = std::string(TempErrorMessageBuffer);
|
||||
}
|
||||
else {
|
||||
@ -1882,8 +1888,8 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
|
||||
buffs[BuffCount].numhits = atoi(DataRow[8]);
|
||||
buffs[BuffCount].melee_rune = atoi(DataRow[9]);
|
||||
buffs[BuffCount].magic_rune = atoi(DataRow[10]);
|
||||
buffs[BuffCount].deathSaveSuccessChance = atoi(DataRow[11]);
|
||||
buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]);
|
||||
buffs[BuffCount].dot_rune = atoi(DataRow[11]);
|
||||
buffs[BuffCount].caston_x = atoi(DataRow[12]);
|
||||
buffs[BuffCount].casterid = 0;
|
||||
|
||||
bool IsPersistent = false;
|
||||
@ -1891,6 +1897,10 @@ void ZoneDatabase::LoadMercBuffs(Merc *merc) {
|
||||
if(atoi(DataRow[13]))
|
||||
IsPersistent = true;
|
||||
|
||||
buffs[BuffCount].caston_y = atoi(DataRow[13]);
|
||||
buffs[BuffCount].caston_z = atoi(DataRow[14]);
|
||||
buffs[BuffCount].ExtraDIChance = atoi(DataRow[15]);
|
||||
|
||||
buffs[BuffCount].persistant_buff = IsPersistent;
|
||||
|
||||
BuffCount++;
|
||||
@ -2566,11 +2576,11 @@ void ZoneDatabase::SaveBuffs(Client *c) {
|
||||
for (int i = 0; i < buff_count; i++) {
|
||||
if(buffs[i].spellid != SPELL_UNKNOWN) {
|
||||
if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
|
||||
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, death_save_chance, "
|
||||
"death_save_aa_chance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')",
|
||||
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
|
||||
"caston_x, caston_y, caston_z, ExtraDIChance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i')",
|
||||
c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining,
|
||||
buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff,
|
||||
buffs[i].deathSaveSuccessChance, buffs[i].deathsaveCasterAARank),
|
||||
buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z, buffs[i].ExtraDIChance),
|
||||
errbuf)) {
|
||||
LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf);
|
||||
}
|
||||
@ -2592,7 +2602,7 @@ void ZoneDatabase::LoadBuffs(Client *c) {
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, "
|
||||
"numhits, melee_rune, magic_rune, persistent, death_save_chance, death_save_aa_chance FROM `character_buffs` WHERE "
|
||||
"numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance FROM `character_buffs` WHERE "
|
||||
"`character_id`='%u'",
|
||||
c->CharacterID()), errbuf, &result))
|
||||
{
|
||||
@ -2617,8 +2627,11 @@ void ZoneDatabase::LoadBuffs(Client *c) {
|
||||
uint32 melee_rune = atoul(row[7]);
|
||||
uint32 magic_rune = atoul(row[8]);
|
||||
uint8 persistent = atoul(row[9]);
|
||||
uint32 death_save_chance = atoul(row[10]);
|
||||
uint32 death_save_aa_chance = atoul(row[11]);
|
||||
uint32 dot_rune = atoul(row[10]);
|
||||
int32 caston_x = atoul(row[11]);
|
||||
int32 caston_y = atoul(row[12]);
|
||||
int32 caston_z = atoul(row[13]);
|
||||
int32 ExtraDIChance = atoul(row[14]);
|
||||
|
||||
buffs[slot_id].spellid = spell_id;
|
||||
buffs[slot_id].casterlevel = caster_level;
|
||||
@ -2638,21 +2651,14 @@ void ZoneDatabase::LoadBuffs(Client *c) {
|
||||
buffs[slot_id].melee_rune = melee_rune;
|
||||
buffs[slot_id].magic_rune = magic_rune;
|
||||
buffs[slot_id].persistant_buff = persistent ? true : false;
|
||||
buffs[slot_id].deathSaveSuccessChance = death_save_chance;
|
||||
buffs[slot_id].deathsaveCasterAARank = death_save_aa_chance;
|
||||
buffs[slot_id].dot_rune = dot_rune;
|
||||
buffs[slot_id].caston_x = caston_x;
|
||||
buffs[slot_id].caston_y = caston_y;
|
||||
buffs[slot_id].caston_z = caston_z;
|
||||
buffs[slot_id].ExtraDIChance = ExtraDIChance;
|
||||
buffs[slot_id].RootBreakChance = 0;
|
||||
buffs[slot_id].UpdateClient = false;
|
||||
if(IsRuneSpell(spell_id)) {
|
||||
c->SetHasRune(true);
|
||||
}
|
||||
else if(IsMagicRuneSpell(spell_id)) {
|
||||
c->SetHasSpellRune(true);
|
||||
}
|
||||
|
||||
/*
|
||||
if(IsDeathSaveSpell(spell_id)) {
|
||||
c->SetDeathSaveChance(true);
|
||||
}
|
||||
*/
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
@ -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?
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user