Merge branch 'master' into raycast

This commit is contained in:
KimLS 2014-04-27 14:36:05 -07:00
commit 989bffee81
96 changed files with 3391 additions and 745 deletions

View File

@ -99,6 +99,10 @@ IF(UNIX)
ADD_DEFINITIONS(-DFREEBSD)
SET(FREEBSD TRUE)
ENDIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
IF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
ADD_DEFINITIONS(-DDARWIN)
SET(DARWIN TRUE)
ENDIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
ENDIF(UNIX)
#use stdint.h types if they exist for this platform (we have to guess otherwise)
@ -118,6 +122,78 @@ SET(EQEMU_DEBUG_LEVEL 5 CACHE STRING "EQEmu debug level:
10 - More errors than you ever wanted to see"
)
SET(EQEMU_LOG_LEVEL_STATUS 2 CACHE STRING "EQEmu logging level for [Status]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_NORMAL 3 CACHE STRING "EQEmu logging level for [Normal]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_ERROR 2 CACHE STRING "EQEmu logging level for [Error]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_DEBUG 3 CACHE STRING "EQEmu logging level for [Debug]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_QUEST 2 CACHE STRING "EQEmu logging level for [Quest]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_COMMANDS 1 CACHE STRING "EQEmu logging level for [Commands]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
SET(EQEMU_LOG_LEVEL_CRASH 3 CACHE STRING "EQEmu logging level for [Crash]:
0 - Disabled
1 - Ouput to File Enabled
2 - Output to stdout Enabled
3 - Output to File and stdout Enabled
8 - Output to stderr Enabled
9 - Output to File and stderr Enabled
11 - Output to File, stdout and stderr Enabled"
)
MARK_AS_ADVANCED(EQEMU_LOG_LEVEL_STATUS EQEMU_LOG_LEVEL_NORMAL EQEMU_LOG_LEVEL_ERROR EQEMU_LOG_LEVEL_DEBUG EQEMU_LOG_LEVEL_QUEST EQEMU_LOG_LEVEL_COMMANDS EQEMU_LOG_LEVEL_CRASH)
SET(EQEMU_STREAM_SEND_RATE 1048576 CACHE STRING "Advanced: Base amount of data stream can send before throttle.")
SET(EQEMU_STREAM_DECAY_RATE 78642 CACHE STRING "Advanced: Base amount of data stream recovers per tic.")
SET(EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL 3.0 CACHE STRING "Advanced: Multiplier on retransmit timeout.")
@ -182,6 +258,14 @@ ADD_DEFINITIONS(-DDECAYBASE=${EQEMU_STREAM_DECAY_RATE})
ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MULT=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MUL})
ADD_DEFINITIONS(-DRETRANSMIT_TIMEOUT_MAX=${EQEMU_STREAM_RETRANSMIT_TIMEOUT_MAX})
ADD_DEFINITIONS(-DAVERAGE_DELTA_MAX=${EQEMU_STREAM_AVERAGE_DELTA_MAX})
ADD_DEFINITIONS(-DLOG_LEVEL_STATUS=${EQEMU_LOG_LEVEL_STATUS})
ADD_DEFINITIONS(-DLOG_LEVEL_NORMAL=${EQEMU_LOG_LEVEL_NORMAL})
ADD_DEFINITIONS(-DLOG_LEVEL_ERROR=${EQEMU_LOG_LEVEL_ERROR})
ADD_DEFINITIONS(-DLOG_LEVEL_DEBUG=${EQEMU_LOG_LEVEL_DEBUG})
ADD_DEFINITIONS(-DLOG_LEVEL_QUEST=${EQEMU_LOG_LEVEL_QUEST})
ADD_DEFINITIONS(-DLOG_LEVEL_COMMANDS=${EQEMU_LOG_LEVEL_COMMANDS})
ADD_DEFINITIONS(-DLOG_LEVEL_CRASH=${EQEMU_LOG_LEVEL_CRASH})
IF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)
ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=true)
ELSE(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS)

View File

@ -1,5 +1,198 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 04/25/2014 ==
cavedude: Corrected a crash in spawn_conditions caused by NPCs on a one way path.
cavedude: Added strict column to spawn_events which will prevent an event from enabling if it's mid-cycle.
cavedude: Prevented disabled or strict spawn_events from enabling when the zone first boots.
cavedude: Fixed the quest function toggle_spawn_event under Perl.
If you're using the quest function toggle_spawn_event (worked on Lua only) it has changed syntax to:
toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base)
Required SQL: utils/sql/git/required/2014_04_25_spawn_events.sql
== 04/23/2014 ==
Kayen: Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc.
Kayen: SE_LimitInstant will now also work when set to include instant spells.
Optional SQL: utils/sql/git/optional/2014_04_23_FocusComabtProcs.sql
Note: Set to false, if enabled will allow all combat procs to receive spell focuses.
== 04/21/2014 ==
Secrets: Crash fix for more hatelist crashes.
Secrets: Hate list fixes, again.
Secrets: Revert of hatelist changes.
== 04/20/2014 ==
Secrets: Changed the functionality of EQDEBUG cmake flag. It now suppresses logs, but shows crashes in any scenario when set to 1. It will also now show crashes even if the log system is disabled.
KLS: Change to how quest signals work, signals with no delay will now be added to the signal queue. This addresses an odd timing issue where NPCs are in a state of life/death flux when a signal from event_death goes off.
KLS: Added cmake flags to define how logging behavior works for each different log type.
Secrets: Crash fix for Hatelist crash observed
== 04/18/2014 ==
Akkadius: Added #command error message suppression for those who don't want to see 'Command is not recognized' constantly
- You need to have rule 'Chat:SuppressCommandErrors' set to true, this is set to false by default
- Required SQL: 2014_04_18_Suppress_Command_Error.sql
== 04/15/2014 ==
Akkadius: Exported $client->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg) - Will be available for simple plugin use
Akkadius: Exported $client->ExpeditionMessage(THIS, ExpdID, Message) - In use with custom expedition mod that will be released soon
== 04/12/2014 ==
Kayen: Fixed an with the slow mitigation code that would cause it to spam the message. Optimized the way the variable is handled for slow mitigation.
Required SQL: utils/sql/git/required/2014_04_12_SlowMitigation.sql
Note: This changes the variable type in the sql table from FLOAT to INT and updates your database.
(When setting slow mitigation 50 = 50%, 100 = 100% ect. You can also set > 100 which will cause slow to become haste now with appropriate in game msg given)
== 04/10/2014 ==
Kayen: Added 'no_target_hotkey' field to npc_types table. This will prevent the NPC from being targeted with F8 (Warning: Will also turn it's name YELLOW)
Kayen: Added a rule to make all (Player cast) Swarm Pets not targetable with F8 (nearest NPC) by default (Warning: Will also turn pets names YELLOW). This is semi-hack but it works.
Kayen: Player cast swarm pets can now be healed and buffed consistent with live.
Optional SQL: utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql
Required SQL: utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql
Note: For the required new npc_types field you DO NOT need to set values for swarm pets if you enable the above rule.
== 04/09/2014 ==
Kayen: Implemented ability to use the actual live spell projectile graphics that are defined in the modern spell file.
*This is disabled by default. Recommend enabling if your server uses an UF+ spell file AND most of your players use UF+ clients.
Kayen: Expanded the PERL ProjectileAnim(mob, item_id, [IsArrow?, speed, angle, tilt, arc, IDFile]) so you can now just set the weapon model graphic IT####
Example: ProjectileAnim($npc, 0, 0, 1, 0, 0, 0, "IT10747") This will shoot an SK 2.0 sword.
Kayen: Updated wizard innate critical damage modifier to be from 20-70% of base damage (based on live parses)
Optional SQL: utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql
Note: This sql also contains a query to check if your spell file is compatible.
== 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.

View File

@ -26,7 +26,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(export_client_files "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(export_client_files "z")
TARGET_LINK_LIBRARIES(export_client_files "m")
TARGET_LINK_LIBRARIES(export_client_files "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(export_client_files "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(export_client_files "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -26,7 +26,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(import_client_files "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(import_client_files "z")
TARGET_LINK_LIBRARIES(import_client_files "m")
TARGET_LINK_LIBRARIES(import_client_files "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(import_client_files "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(import_client_files "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -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"))

View File

@ -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

View File

@ -30,6 +30,10 @@
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
#define MSG_NOSIGNAL 0
#endif
#ifdef DARWIN
#define MSG_NOSIGNAL SO_NOSIGPIPE // Corysia Taware - Sept. 27, 2013
// See http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html
#endif // DARWIN
#ifdef _WINDOWS
InitWinsock winsock;

View File

@ -97,8 +97,12 @@ void BaseTCPServer::ListenNewConnections() {
from.sin_family = AF_INET;
fromlen = sizeof(from);
LockMutex lock(&MSock);
#ifndef DARWIN // Corysia - On OSX, 0 is a valid fd.
if (!sock)
return;
#else
if (sock == -1) return;
#endif
// Check for pending connects
#ifdef _WINDOWS

View File

@ -2522,10 +2522,11 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id)
safe_delete_array(query);
if (mysql_num_rows(result) != 0) {
row = mysql_fetch_row(result);
mysql_free_result(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) {

View File

@ -35,29 +35,20 @@ static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eqemu", "logs/eqemu",
static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command", "Crash" };
EQEMuLog::EQEMuLog() {
// MOpen = new Mutex;
// MLog = new Mutex*[MaxLogID];
// fp = new FILE*[MaxLogID];
// pLogStatus = new uint8[MaxLogID];
for (int i=0; i<MaxLogID; i++) {
fp[i] = 0;
// MLog[i] = new Mutex;
#if EQDEBUG >= 2
pLogStatus[i] = 1 | 2;
#else
pLogStatus[i] = 0;
#endif
logCallbackFmt[i] = nullptr;
logCallbackBuf[i] = nullptr;
logCallbackPva[i] = nullptr;
}
// TODO: Make this read from an ini or something, everyone has different opinions on what it should be
#if EQDEBUG < 2
pLogStatus[Status] = 2;
pLogStatus[Error] = 2;
pLogStatus[Quest] = 2;
pLogStatus[Commands] = 1;
#endif
pLogStatus[Status] = LOG_LEVEL_STATUS;
pLogStatus[Normal] = LOG_LEVEL_NORMAL;
pLogStatus[Error] = LOG_LEVEL_ERROR;
pLogStatus[Debug] = LOG_LEVEL_DEBUG;
pLogStatus[Quest] = LOG_LEVEL_QUEST;
pLogStatus[Commands] = LOG_LEVEL_COMMANDS;
pLogStatus[Crash] = LOG_LEVEL_CRASH;
logFileValid = true;
}
@ -68,10 +59,6 @@ EQEMuLog::~EQEMuLog() {
if (fp[i])
fclose(fp[i]);
}
// safe_delete_array(fp);
// safe_delete_array(MLog);
// safe_delete_array(pLogStatus);
// safe_delete(MOpen);
}
bool EQEMuLog::open(LogIDs id) {

View File

@ -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
**

View File

@ -55,9 +55,21 @@ namespace EQEmu {
std::string final_name = name;
final_name += ".lock";
#ifdef __DARWIN
#if __DARWIN_C_LEVEL < 200809L
imp_->fd_ = open(final_name.c_str(),
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR);
#else
imp_->fd_ = open(final_name.c_str(),
O_RDWR | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR);
#endif
#else
imp_->fd_ = open(final_name.c_str(),
O_RDWR | O_CREAT | O_CLOEXEC,
S_IRUSR | S_IWUSR);
#endif
if(imp_->fd_ == -1) {
EQ_EXCEPT("IPC Mutex", "Could not create mutex.");

View File

@ -5070,8 +5070,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)
{

View File

@ -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;

View File

@ -135,9 +135,11 @@ RULE_CATEGORY_END()
RULE_CATEGORY( Pets )
RULE_REAL( Pets, AttackCommandRange, 150 )
RULE_BOOL( Pets, UnTargetableSwarmPet, false )
RULE_BOOL( Pets, SwarmPetNotTargetableWithHotKey, false ) //On SOF+ clients this a semi-hack to make swarm pets not F8 targetable.
RULE_CATEGORY_END()
RULE_CATEGORY( GM )
RULE_INT ( GM, MinStatusToSummonItem, 250)
RULE_INT ( GM, MinStatusToZoneAnywhere, 250 )
RULE_CATEGORY_END()
@ -290,14 +292,24 @@ 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_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.
RULE_BOOL ( Spells, FocusCombatProcs, false) //Allow all combat procs to receive focus effects.
RULE_CATEGORY_END()
RULE_CATEGORY( Combat )
@ -306,6 +318,9 @@ 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)
@ -384,6 +399,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 +455,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
@ -448,6 +465,7 @@ RULE_BOOL ( Chat, ServerWideAuction, true)
RULE_BOOL ( Chat, EnableVoiceMacros, true)
RULE_BOOL ( Chat, EnableMailKeyIPVerification, true)
RULE_BOOL ( Chat, EnableAntiSpam, true)
RULE_BOOL ( Chat, SuppressCommandErrors, false) // Do not suppress by default
RULE_INT ( Chat, MinStatusToBypassAntiSpam, 100)
RULE_INT ( Chat, MinimumMessagesPerInterval, 4)
RULE_INT ( Chat, MaximumMessagesPerInterval, 12)
@ -531,6 +549,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

View File

@ -390,8 +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))
if (IsPureNukeSpell(spell_id))
return true;
return false;
@ -678,11 +677,9 @@ bool IsCombatSkill(uint16 spell_id)
{
if (!IsValidSpell(spell_id))
return false;
//Check if Discipline OR melee proc (from non-castable spell)
if ((spells[spell_id].mana == 0 &&
(spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)) ||
((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0)))
//Check if Discipline
if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)))
return true;
return false;

View File

@ -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,7 +258,7 @@ 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)
@ -270,7 +270,7 @@ typedef enum {
#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
@ -456,7 +456,7 @@ typedef enum {
//#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

View File

@ -30,7 +30,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(eqlaunch "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(eqlaunch "z")
TARGET_LINK_LIBRARIES(eqlaunch "m")
TARGET_LINK_LIBRARIES(eqlaunch "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(eqlaunch "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(eqlaunch "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -58,7 +58,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(loginserver "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(loginserver "z")
TARGET_LINK_LIBRARIES(loginserver "m")
TARGET_LINK_LIBRARIES(loginserver "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(loginserver "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(loginserver "pthread")
TARGET_LINK_LIBRARIES(loginserver "EQEmuAuthCrypto")
TARGET_LINK_LIBRARIES(loginserver "cryptopp")

View File

@ -36,7 +36,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(queryserv "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(queryserv "z")
TARGET_LINK_LIBRARIES(queryserv "m")
TARGET_LINK_LIBRARIES(queryserv "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(queryserv "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(queryserv "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -38,7 +38,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(shared_memory "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(shared_memory "z")
TARGET_LINK_LIBRARIES(shared_memory "m")
TARGET_LINK_LIBRARIES(shared_memory "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(shared_memory "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(shared_memory "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -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})
@ -30,7 +32,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(tests "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(tests "z")
TARGET_LINK_LIBRARIES(tests "m")
TARGET_LINK_LIBRARIES(tests "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(loginserver "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(tests "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

103
tests/atobool_test.h Normal file
View 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
View 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

View File

@ -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;

View File

@ -38,7 +38,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(ucs "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(ucs "z")
TARGET_LINK_LIBRARIES(ucs "m")
TARGET_LINK_LIBRARIES(ucs "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(ucs "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(ucs "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -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.

View 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.

View 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);

View 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)
)

View File

@ -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.');

View File

@ -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;

View 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.');

View 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.');

View File

@ -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.');

View 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;

View 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.');

View File

@ -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 SOF 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 NPC to use for Fire spell projectile.');

View 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.');

View File

@ -0,0 +1,7 @@
-- Recommend enabling if your server uses an UF+ spell file and your players use UF+ client. This will give the proper graphics for all spell projectiles.
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:UseLiveSpellProjectileGFX', false, ' Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.');
-- Use this query to check if your spell file is compatible
-- If it returns in the player_1 field IT##### it will work.
SELECT id,name,player_1 from spells_new WHERE targettype = 1;

View File

@ -0,0 +1,4 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Pets:SwarmPetNotTargetableWithHotKey', 'false', ' On SOF+ clients this a semi-hack to make swarm pets not F8 targetable. Warning: Turns pet names Yellow');

View File

@ -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');

View 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;

View File

@ -0,0 +1,3 @@
ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`;

View File

@ -0,0 +1,7 @@
-- Convert all values from FLOAT to INT
UPDATE npc_types SET slow_mitigation = slow_mitigation * 100;
-- Change variable type from FLOAT TO INT
ALTER TABLE npc_types MODIFY slow_mitigation smallint(4);

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` VALUES ('0', 'Chat:SuppressCommandErrors', 'true', 'This will suppress "Command is not recognized"');

View File

@ -0,0 +1 @@
alter table spawn_events add column `strict` tinyint(4) not null default 0;

View File

@ -84,7 +84,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(world "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(world "z")
TARGET_LINK_LIBRARIES(world "m")
TARGET_LINK_LIBRARIES(world "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(world "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(world "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -56,7 +56,7 @@
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#ifndef FREEBSD
#if not defined (FREEBSD) && not defined (DARWIN)
union semun {
int val;
struct semid_ds *buf;
@ -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;

View File

@ -232,7 +232,9 @@ IF(UNIX)
TARGET_LINK_LIBRARIES(zone "${CMAKE_DL_LIBS}")
TARGET_LINK_LIBRARIES(zone "z")
TARGET_LINK_LIBRARIES(zone "m")
TARGET_LINK_LIBRARIES(zone "rt")
IF(NOT DARWIN)
TARGET_LINK_LIBRARIES(zone "rt")
ENDIF(NOT DARWIN)
TARGET_LINK_LIBRARIES(zone "pthread")
ADD_DEFINITIONS(-fPIC)
ENDIF(UNIX)

View File

@ -1627,11 +1627,32 @@ void NPC::AI_DoMovement() {
if (gridno > 0 || cur_wp==-2) {
if (movetimercompleted==true) { // time to pause at wp is over
int32 spawn_id = this->GetSpawnPointID();
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
Spawn2 *found_spawn = nullptr;
while(iterator.MoreElements())
{
Spawn2* cur = iterator.GetData();
iterator.Advance();
if(cur->GetID() == spawn_id)
{
found_spawn = cur;
break;
}
}
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and resart spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted=false;

View File

@ -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!
@ -160,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!
@ -201,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.
@ -234,24 +243,26 @@
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
#define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again.
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
#define CORPSEDRAG_SOMEONE_ELSE 4063 //Someone else is dragging %1.
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
#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.
@ -289,8 +300,13 @@
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
#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 SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful.
#define SLOW_SLIGHTLY_SUCCESSFUL 9031 //Your spell was slightly successful.
#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired.
#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2.
#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!
@ -323,6 +339,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.
@ -353,10 +370,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

View File

@ -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();
@ -1196,14 +1204,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.
@ -1213,8 +1228,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
@ -1234,6 +1254,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;
}
@ -1241,3 +1264,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;
}

View File

@ -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));
@ -3188,7 +3194,6 @@ int32 Mob::ReduceDamage(int32 damage)
damage -= damage_to_reduce;
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot);
//UpdateRuneFlags();
}
else
{
@ -3213,7 +3218,6 @@ int32 Mob::ReduceDamage(int32 damage)
damage -= damage_to_reduce;
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot);
UpdateRuneFlags();
}
else
{
@ -3221,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();
}
}
}
@ -3243,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)
@ -3317,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
{
@ -3341,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
{
@ -3349,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();
}
}
}
@ -3372,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)
@ -3616,6 +3616,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
//fade mez if we are mezzed
if (IsMezzed()) {
mlog(COMBAT__HITS, "Breaking mez due to attack.");
entity_list.MessageClose_StringID(this, true, 100, MT_WornOff,
HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName());
BuffFadeByEffect(SE_Mez);
}
@ -3685,29 +3687,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)
{
@ -3829,49 +3810,83 @@ 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;
if (acthealed > 100) {
if (caster) {
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
if (caster != this)
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), itoa(acthealed));
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 {
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();
@ -4072,6 +4087,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
}
}
}
//If OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not.
//This is for some servers that may want to have as many procs triggering from weapons as possible in a single round.
if(!RuleB(Combat, OneProcPerWeapon))
proced = false;
if (!proced && inst) {
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
@ -4096,7 +4115,8 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
}
} else {
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
break;
if (RuleB(Combat, OneProcPerWeapon))
break;
}
}
}
@ -4232,7 +4252,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));
}
}
}
@ -4297,11 +4319,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);
@ -4347,16 +4374,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));
}
}
}
@ -4518,14 +4561,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;
@ -4536,22 +4632,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;
@ -4562,14 +4656,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;
}

View File

@ -1338,19 +1338,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
}
}
else if ((effect_value - 100) < 0) { // Slow
//Slow Mitigation works by taking the amount that would be slowed, and adding a multiplied version of the difference.
int real_slow_value = (100 - effect_value) * -1;
if (slow_mitigation){
int new_effect_value = SlowMitigation(false,caster,real_slow_value);
if (new_effect_value < newbon->haste) {
newbon->haste = new_effect_value;
SlowMitigation(true,caster);
}
}
else {
if (real_slow_value < newbon->haste)
newbon->haste = real_slow_value;
}
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
if (real_slow_value < newbon->haste)
newbon->haste = real_slow_value;
}
break;
}
@ -1358,16 +1349,29 @@ 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;
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
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
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value < newbon->hastetype3)
newbon->hastetype3 = effect_value;
}
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
if (effect_value > newbon->hastetype3) {
newbon->hastetype3 = effect_value;
}
@ -1377,18 +1381,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AttackSpeed4:
{
if (effect_value > 0) {
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);
}
}
else if (effect_value > newbon->inhibitmelee) {
newbon->inhibitmelee = effect_value;
}
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) {
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value > newbon->inhibitmelee)
newbon->inhibitmelee = effect_value;
}
break;
}
@ -2533,6 +2534,50 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
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;
}
}
}
@ -3878,10 +3923,26 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break;
case SE_FrenziedDevastation:
spellbonuses.FrenziedDevastation += effect_value;
aabonuses.FrenziedDevastation += effect_value;
itembonuses.FrenziedDevastation += effect_value;
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;
}
}
}

View File

@ -3151,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
@ -3237,6 +3237,9 @@ void Bot::BotRangedAttack(Mob* other) {
invisible_animals = false;
}
if (spellbonuses.NegateIfCombat)
BuffFadeByEffect(SE_NegateIfCombat);
if(hidden || improved_hidden){
hidden = false;
improved_hidden = false;
@ -5281,6 +5284,28 @@ uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) {
return Result;
}
void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
// This essentially performs a '#bot update,' with appearance packets, based on the current methods.
// This should not be called outside of Client::SetEXP() due to it's lack of rule checks.
if(client) {
std::list<Bot*> blist = entity_list.GetBotsByBotOwnerCharacterID(client->CharacterID());
for(std::list<Bot*>::iterator biter = blist.begin(); biter != blist.end(); ++biter) {
Bot* bot = *biter;
if(bot && (bot->GetLevel() != client->GetLevel())) {
bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
bot->CalcBotStats(false);
if(sendlvlapp)
bot->SendLevelAppearance();
// modified from Client::SetLevel()
bot->SendAppearancePacket(AT_WhoLevel, level, true, true); // who level change
}
}
blist.clear();
}
}
std::string Bot::ClassIdToString(uint16 classId) {
std::string Result;
@ -6640,6 +6665,9 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
safe_delete(outapp);
}
if (spellbonuses.NegateIfCombat)
BuffFadeByEffect(SE_NegateIfCombat);
if(GetTarget())
TriggerDefensiveProcs(weapon, other, Hand, damage);
@ -14967,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))

View File

@ -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);

View File

@ -1022,11 +1022,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
if(command_dispatch(this, message) == -2) {
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
if(i == 0) {
if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
Message(13, "Command '%s' not recognized.", message);
}
} else {
Message(13, "Command '%s' not recognized.", message);
if(!RuleB(Chat, SuppressCommandErrors))
Message(13, "Command '%s' not recognized.", message);
}
}
break;
@ -2306,6 +2307,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 +2352,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 +2859,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 +2878,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
@ -2983,8 +3024,18 @@ bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
return false;
} else if (mode == FilterShowGroupOnly) {
Group *g = GetGroup();
if (!g || !g->IsGroupMember(sender))
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;
}
}
}
@ -3027,7 +3078,7 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil
type = 4;
if (!message1) {
Message_StringID(type, string_id); // use the simple message instead
FilteredMessage_StringID(sender, type, filter, string_id); // use the simple message instead
return;
}
@ -6666,9 +6717,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; }
}
}
@ -8111,7 +8162,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
if(!auto_consume) //no message if the client consumed for us
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name);
#if EQDEBUG >= 1
#if EQDEBUG >= 5
LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)slot);
#endif
}
@ -8128,7 +8179,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
if(!auto_consume) //no message if the client consumed for us
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name);
#if EQDEBUG >= 1
#if EQDEBUG >= 5
LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)slot);
#endif
}
@ -8163,3 +8214,30 @@ void Client::PlayMP3(const char* fname)
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::ExpeditionSay(const char *str, int ExpID) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){
safe_delete_array(query);
return;
}
safe_delete_array(query);
if(result)
this->Message(14, "You say to the expedition, '%s'", str);
while((row = mysql_fetch_row(result))) {
const char* CharName = row[0];
if(strcmp(CharName, this->GetCleanName()) != 0)
worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str);
// ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3]));
}
mysql_free_result(result);
}

View File

@ -305,6 +305,7 @@ public:
void SetHideMe(bool hm);
inline uint16 GetPort() const { return port; }
bool IsDead() const { return(dead); }
bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); }
inline bool IsLFP() { return LFP; }
void UpdateLFP();
@ -794,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);
@ -1169,6 +1170,7 @@ public:
std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes);
void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume);
void PlayMP3(const char* fname);
void ExpeditionSay(const char *str, int ExpID);
int mod_client_damage(int damage, SkillUseTypes skillinuse, int hand, const ItemInst* weapon, Mob* other);
bool mod_client_message(char* message, uint8 chan_num);
bool mod_can_increase_skill(SkillUseTypes skillid, Mob* against_who);

View File

@ -3478,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);
@ -5419,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;
@ -12466,7 +12474,15 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app)
//
char *GuildName = (char *)app->pBuffer;
#ifdef DARWIN
#if __DARWIN_C_LEVEL < 200809L
if (strlen(GuildName) > 60)
#else
if(strnlen(GuildName, 64) > 60)
#endif // __DARWIN_C_LEVEL
#else
if(strnlen(GuildName, 64) > 60)
#endif // DARWIN
{
Message(clientMessageError, "Guild name too long.");
return;
@ -12924,7 +12940,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct);
LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer;
#ifdef DARWIN
#if __DARWIN_C_LEVEL < 200809L
if (strlen(pts->Comment) > 256)
#else
if(strnlen(pts->Comment, 256) > 256)
#endif // __DARWIN_C_LEVEL
#else
if(strnlen(pts->Comment, 256) > 256)
#endif // DARWIN
return;
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38);
@ -12951,7 +12975,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct);
LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer;
if(strnlen(gts->Comment, 256) > 256)
#ifdef DARWIN
#if __DARWIN_C_LEVEL < 200809L
if (strlen(gts->Comment) > 256)
#else
if(strnlen(gts->Comment, 256) > 256)
#endif // __DARWIN_C_LEVEL
#else
if(strnlen(gts->Comment, 256) > 256)
#endif // __DARWIN
return;
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43);

View File

@ -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();

View File

@ -449,7 +449,11 @@ int command_init(void) {
command_add("questerrors", "Shows quest errors.", 100, command_questerrors) ||
command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) ||
command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) ||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache)
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) ||
command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) ||
command_add("open_shop", nullptr, 100, command_merchantopenshop) ||
command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) ||
command_add("close_shop", nullptr, 100, command_merchantcloseshop)
)
{
command_deinit();
@ -2638,19 +2642,35 @@ void command_makepet(Client *c, const Seperator *sep)
void command_level(Client *c, const Seperator *sep)
{
uint16 level = atoi(sep->arg[1]);
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap)) )
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) {
c->Message(0, "Error: #Level: Invalid Level");
else if (c->Admin() < 100)
}
else if (c->Admin() < 100) {
c->SetLevel(level, true);
else if (!c->GetTarget())
#ifdef BOTS
if(RuleB(Bots, BotLevelsWithOwner))
Bot::LevelBotWithClient(c, level, true);
#endif
}
else if (!c->GetTarget()) {
c->Message(0, "Error: #Level: No target");
else
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel))))
}
else {
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) {
c->Message(0, "Error: #Level: Invalid Level");
else
}
else {
c->GetTarget()->SetLevel(level, true);
if(c->GetTarget() && c->GetTarget()->IsClient())
c->GetTarget()->CastToClient()->SendLevelAppearance();
if(c->GetTarget()->IsClient()) {
c->GetTarget()->CastToClient()->SendLevelAppearance();
#ifdef BOTS
if(RuleB(Bots, BotLevelsWithOwner))
Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true);
#endif
}
}
}
}
void command_spawn(Client *c, const Seperator *sep)
@ -11465,3 +11485,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();
}

View File

@ -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);

View File

@ -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())
@ -162,7 +163,8 @@ struct Buffs_Struct {
int32 caston_x;
int32 caston_y;
int32 caston_z;
int32 ExtraDIChance;
int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase
bool persistant_buff;
bool client; //True if the caster is a client
bool UpdateClient;
@ -339,9 +341,12 @@ struct StatBonuses {
bool DivineAura; // invulnerability
bool DistanceRemoval; // Check if Cancle if Moved effect is present
int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
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.
//bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect
//bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect
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%

View File

@ -79,12 +79,12 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
int chance = RuleI(Spells, BaseCritChance);
int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
if (chance > 0){
if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) {
ratio = MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
ratio += MakeRandomInt(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
Critical = true;
}

View File

@ -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;
}

View File

@ -114,6 +114,7 @@ private:
std::map<std::string, std::string> vars_;
SV *_empty_sv;
std::map<std::string, int> clear_vars_;
};
#endif

View File

@ -1678,14 +1678,15 @@ XS(XS__toggle_spawn_event);
XS(XS__toggle_spawn_event)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)");
if (items != 4)
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, strict, reset_base)");
uint32 event_id = (int)SvIV(ST(0));
bool enabled = ((int)SvIV(ST(1))) == 0?false:true;
bool reset_base = ((int)SvIV(ST(1))) == 0?false:true;
bool strict = ((int)SvIV(ST(2))) == 0?false:true;
bool reset_base = ((int)SvIV(ST(3))) == 0?false:true;
quest_manager.toggle_spawn_event(event_id, enabled, reset_base);
quest_manager.toggle_spawn_event(event_id, enabled, strict, reset_base);
XSRETURN_EMPTY;
}

View File

@ -3016,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;
@ -3025,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;
@ -3039,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;
}
}
@ -3418,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;
}

View File

@ -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

View File

@ -60,14 +60,16 @@ void HateList::Wipe()
while(iterator != list.end())
{
Mob* m = (*iterator)->ent;
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
//iterator
if(m)
{
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
if(m->IsClient())
m->CastToClient()->DecrementAggroCount();
}
delete (*iterator);
iterator = list.erase(iterator);
if(m->IsClient())
m->CastToClient()->DecrementAggroCount();
}
}
@ -203,6 +205,9 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd
bool HateList::RemoveEnt(Mob *ent)
{
if (!ent)
return false;
bool found = false;
auto iterator = list.begin();
@ -210,15 +215,18 @@ bool HateList::RemoveEnt(Mob *ent)
{
if((*iterator)->ent == ent)
{
if(ent)
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0);
delete (*iterator);
iterator = list.erase(iterator);
found = true;
if(ent->IsClient())
if(ent && ent->IsClient())
ent->CastToClient()->DecrementAggroCount();
}
delete (*iterator);
iterator = list.erase(iterator);
}
else
++iterator;
}
@ -445,6 +453,17 @@ Mob *HateList::GetMostHate(){
Mob *HateList::GetRandom()
{
int count = list.size();
if(count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
return NULL;
if(count == 1) //No need to do all that extra work if we only have one hate entry
{
if(*list.begin()) // Just in case tHateEntry is invalidated somehow...
return (*list.begin())->ent;
return NULL;
}
auto iterator = list.begin();
int random = MakeRandomInt(0, count - 1);
for (int i = 0; i < random; i++)

View File

@ -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);
}

View File

@ -168,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);
}
@ -348,8 +372,8 @@ int lua_get_spawn_condition(const char *zone, uint32 instance_id, int condition_
return quest_manager.get_spawn_condition(zone, instance_id, condition_id);
}
void lua_toggle_spawn_event(int event_id, bool enable, bool reset) {
quest_manager.toggle_spawn_event(event_id, enable, reset);
void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset) {
quest_manager.toggle_spawn_event(event_id, enable, strict, reset);
}
void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) {
@ -659,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);
}
@ -1091,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),
@ -1195,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),

View File

@ -1816,6 +1816,52 @@ void Lua_Mob::SetDestructibleObject(bool set) {
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<>())
@ -2125,7 +2171,16 @@ luabind::scope lua_register_mob() {
.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("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject);
.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() {

View File

@ -345,6 +345,15 @@ public:
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

View File

@ -408,7 +408,7 @@ void Lua_NPC::SetSpellFocusHeal(int focus) {
}
float Lua_NPC::GetSlowMitigation() {
Lua_Safe_Call_Real();
Lua_Safe_Call_Int();
return self->GetSlowMitigation();
}
@ -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")
@ -516,11 +526,13 @@ luabind::scope lua_register_npc() {
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
.def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG)
.def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal)
.def("GetSlowMitigation", (float(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
.def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
.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

View File

@ -112,7 +112,9 @@ public:
int GetAccuracyRating();
int GetSpawnKillCount();
int GetScore();
void MerchantOpenShop();
void MerchantCloseShop();
};
#endif
#endif
#endif

View File

@ -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;
@ -889,7 +891,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
ns->spawn.NPC = IsClient() ? 0 : 1;
ns->spawn.IsMercenary = IsMerc() ? 1 : 0;
ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0;
ns->spawn.petOwnerId = ownerid;
ns->spawn.haircolor = haircolor;
@ -1624,7 +1627,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32
la->parm4 = parm4;
la->parm5 = parm5;
// Note that setting the b values to 0 will disable the related effect from the corresponding parameter.
// Setting the a value appears to have no affect at all.
// Setting the a value appears to have no affect at all.s
la->value1a = 1;
la->value1b = 1;
la->value2a = 1;
@ -2086,27 +2089,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 +2467,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) {
@ -4349,6 +4365,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;
@ -4582,33 +4641,21 @@ void Mob::CastOnNumHitFade(uint32 spell_id)
}
}
int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value)
void Mob::SlowMitigation(Mob* caster)
{
float int_slow_mitigation = slow_mitigation * 100.0f;
if (int_slow_mitigation > 100.0f)
return 0;
if (slow_msg)
if (GetSlowMitigation() && caster && caster->IsClient())
{
if (caster && caster->IsClient())
{
if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f))
caster->Message(262, "Your spell was mostly successful");
if ((GetSlowMitigation() > 0) && (GetSlowMitigation() < 26))
caster->Message_StringID(MT_SpellFailure, SLOW_MOSTLY_SUCCESSFUL);
else if ((int_slow_mitigation >= 26.0f) && (int_slow_mitigation < 74.0f))
caster->Message(262, "Your spell was partially successful");
else if ((GetSlowMitigation() >= 26) && (GetSlowMitigation() < 74))
caster->Message_StringID(MT_SpellFailure, SLOW_PARTIALLY_SUCCESSFUL);
else if ((int_slow_mitigation >= 74.0f) && (int_slow_mitigation < 101.0f))
caster->Message(262, "Your spell was slightly successful");
}
return 0;
}
else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101))
caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL);
else
{
slow_value -= (slow_value * static_cast<int>(int_slow_mitigation) / 100);
return slow_value;
else if (GetSlowMitigation() > 100)
caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT);
}
}
@ -4751,11 +4798,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) {

View File

@ -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();
@ -160,7 +161,7 @@ public:
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color);
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0,
float angle = 0, float tilt = 0, float arc = 0);
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr);
void ChangeSize(float in_size, bool bNoRestriction = false);
inline uint8 SeeInvisible() const { return see_invis; }
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
@ -169,7 +170,7 @@ public:
bool IsInvisible(Mob* other = 0) const;
void SetInvisible(uint8 state);
bool AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon);
//Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
@ -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; }
@ -502,6 +499,7 @@ public:
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false);
bool HasProcs() const;
bool IsCombatProc(uint16 spell_id);
//Logging
bool IsLoggingEnabled() const { return(logging_enabled); }
@ -582,7 +580,7 @@ public:
void CastOnCurer(uint32 spell_id);
void CastOnCure(uint32 spell_id);
void CastOnNumHitFade(uint32 spell_id);
int SlowMitigation(bool slow_msg=false, Mob *caster = nullptr,int slow_value = 0);
void SlowMitigation(Mob* caster);
int16 GetCritDmgMob(uint16 skill);
int16 GetMeleeDamageMod_SE(uint16 skill);
int16 GetMeleeMinDamageMod_SE(uint16 skill);
@ -599,6 +597,8 @@ public:
void MeleeLifeTap(int32 damage);
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
bool ImprovedTaunt();
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
int16 GetSlowMitigation() const {return slow_mitigation;}
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
@ -920,6 +920,7 @@ protected:
int16 DR;
int16 PR;
int16 Corrup;
int16 PhR;
bool moving;
int targeted;
bool findable;
@ -945,6 +946,7 @@ protected:
int16 petpower;
uint32 follow;
uint32 follow_dist;
bool no_target_hotkey;
uint8 gender;
uint16 race;
@ -990,7 +992,6 @@ protected:
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
Map::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);
@ -1023,7 +1024,7 @@ protected:
Timer attack_dw_timer;
Timer ranged_timer;
float attack_speed; //% increase/decrease in attack speed (not haste)
float slow_mitigation; // Allows for a slow mitigation based on a % in decimal form. IE, 1 = 100% mitigation, .5 is 50%
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
Timer tic_timer;
Timer mana_timer;
@ -1046,6 +1047,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;
@ -1082,7 +1089,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;
@ -1190,11 +1196,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;

View File

@ -447,7 +447,7 @@ int main(int argc, char** argv) {
if (InterserverTimer.Check()) {
InterserverTimer.Start();
database.ping();
AsyncLoadVariables(dbasync, &database);
// AsyncLoadVariables(dbasync, &database);
entity_list.UpdateWho();
if (worldserver.TryReconnect() && (!worldserver.Connected()))
worldserver.AsyncConnect();

View File

@ -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;
@ -222,6 +224,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
p_depop = false;
loottable_id = d->loottable_id;
no_target_hotkey = d->no_target_hotkey;
primary_faction = 0;
SetNPCFactionID(d->npc_faction_id);
@ -658,6 +662,9 @@ bool NPC::Process()
viral_timer_counter = 0;
}
if(projectile_timer.Check())
SpellProjectileEffect();
if(spellbonuses.GravityEffect == 1) {
if(gravity_timer.Check())
DoGravityEffect();
@ -1714,6 +1721,22 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
{
Mob::FillSpawnStruct(ns, ForWho);
//Basic settings to make sure swarm pets work properly.
if (GetSwarmOwner()) {
Client *c = entity_list.GetClientByID(GetSwarmOwner());
if(c) {
SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed.
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
ns->spawn.IsMercenary = 1;
}
//NPC cast swarm pets should still be targetable with F8.
else
ns->spawn.IsMercenary = 0;
}
//Not recommended if using above (However, this will work better on older clients).
if (RuleB(Pets, UnTargetableSwarmPet)) {
if(GetOwnerID() || GetSwarmOwner()) {
ns->spawn.is_pet = 1;
@ -1862,6 +1885,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
return;
}
if(id == "PhR")
{
PhR = atoi(val.c_str());
return;
}
if(id == "runspeed")
{
runspeed = (float)atof(val.c_str());
@ -1975,7 +2004,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
if(id == "slow_mitigation")
{
slow_mitigation = atof(val.c_str());
slow_mitigation = atoi(val.c_str());
return;
}
if(id == "loottable_id")
@ -2058,6 +2087,8 @@ void NPC::CalcNPCResists() {
PR = (GetLevel() * 11)/10;
if (!Corrup)
Corrup = 15;
if (!PhR)
PhR = 10;
return;
}

View File

@ -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();
@ -229,7 +233,7 @@ public:
uint32 GetMaxDMG() const {return max_dmg;}
uint32 GetMinDMG() const {return min_dmg;}
float GetSlowMitigation() const {return slow_mitigation;}
int16 GetSlowMitigation() const {return slow_mitigation;}
float GetAttackSpeed() const {return attack_speed;}
bool IsAnimal() const { return(bodytype == BT_Animal); }
uint16 GetPetSpellID() const {return pet_spell_id;}
@ -241,6 +245,7 @@ public:
void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false);
virtual void DoClassAttacks(Mob *target);
void CheckSignal();
inline bool IsTargetableWithHotkey() const { return no_target_hotkey; }
//waypoint crap
int GetMaxWp() const { return max_wp; }

View File

@ -5898,6 +5898,64 @@ XS(XS_Client_PlayMP3)
XSRETURN_EMPTY;
}
XS(XS_Client_ExpeditionMessage); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_ExpeditionMessage)
{
dXSARGS;
if (items != 3)
Perl_croak(aTHX_ "Usage: Client::ExpeditionMessage(THIS, ExpdID, Message)");
{
Client * THIS;
int ExpdID = (int)SvUV(ST(1));
const char * Message = (const char *)SvPV_nolen(ST(2));
dXSTARG;
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 == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->ExpeditionSay(Message, ExpdID);
}
XSRETURN_EMPTY;
}
//Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg)
XS(XS_Client_SendMarqueeMessage); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SendMarqueeMessage)
{
dXSARGS;
if (items != 7)
Perl_croak(aTHX_ "Usage: Client::SendMarqueeMessage(THIS, type, priority, fade_in, fade_out, duration, msg)");
{
Client * THIS;
uint32 type = (uint32)SvUV(ST(1));
uint32 priority = (uint32)SvUV(ST(2));
uint32 fade_in = (uint32)SvUV(ST(3));
uint32 fade_out = (uint32)SvUV(ST(4));
uint32 duration = (uint32)SvUV(ST(5));
std::string msg = (std::string)SvPV_nolen(ST(6));
dXSTARG;
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 == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg);
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
#endif
@ -6134,6 +6192,8 @@ XS(boot_Client)
newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$");
newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$");
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
XSRETURN_YES;
}

View File

@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ProjectileAnim)
{
dXSARGS;
if (items < 3 || items > 8)
if (items < 3 || items > 9)
Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)");
{
@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim)
float angle = 0;
float tilt = 0;
float arc = 0;
char * IDFile = nullptr;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim)
arc = (float)SvNV(ST(7));
}
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc);
if (items > 8) { IDFile = (char *)SvPV_nolen(ST(8)); }
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc, IDFile);
}
XSRETURN_EMPTY;
@ -8389,7 +8392,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$");
newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$");
newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$");
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$");
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$");
newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$");
newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$");
newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$");

View File

@ -2023,7 +2023,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
{
NPC * THIS;
float RETVAL;
int16 RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
@ -2036,7 +2036,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSlowMitigation();
XSprePUSH; PUSHn((double)RETVAL);
XSprePUSH; PUSHn((UV)RETVAL);
}
XSRETURN(1);
}

View 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++) {

View File

@ -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) {
@ -1217,16 +1277,9 @@ void QuestManager::signalwith(int npc_id, int signal_id, int wait_ms) {
if(wait_ms > 0) {
STimerList.push_back(SignalTimer(wait_ms, npc_id, signal_id));
return;
}
if (npc_id<1)
{
printf("signal() bad npcid=%i\n",npc_id);
}
else
{
//initiator* signalnpc=0;
entity_list.SignalMobsByNPCID(npc_id, signal_id);
} else {
STimerList.push_back(SignalTimer(0, npc_id, signal_id));
return;
}
}
@ -1680,8 +1733,8 @@ short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_
}
//toggle a spawn event
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) {
zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base);
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) {
zone->spawn_conditions.ToggleEvent(event_id, enable, strict, reset_base);
}
bool QuestManager::has_zone_flag(int zone_id) {
@ -2557,6 +2610,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();

View File

@ -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);
@ -143,7 +150,7 @@ public:
void showgrid(int gridid);
void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value);
short get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id);
void toggle_spawn_event(int event_id, bool enable, bool reset_base);
void toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base);
bool has_zone_flag(int zone_id);
void set_zone_flag(int zone_id);
void clear_zone_flag(int zone_id);
@ -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);

View File

@ -303,11 +303,13 @@ void Spawn2::ForceDespawn()
{
npcthis->Depop(true);
IsDespawned = true;
npcthis = nullptr;
return;
}
else
{
npcthis->Depop(false);
npcthis = nullptr;
}
}
}
@ -555,6 +557,7 @@ SpawnEvent::SpawnEvent() {
action = ActionSet;
argument = 0;
period = 0xFFFFFFFF;
strict = false;
memset(&next, 0, sizeof(next));
}
@ -586,26 +589,28 @@ void SpawnConditionManager::Process() {
for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur;
if(!cevent.enabled)
continue;
if(cevent.enabled)
{
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
//this event has been triggered.
//execute the event
if(!cevent.strict || (cevent.strict && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year))
ExecEvent(cevent, true);
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
//this event has been triggered.
//execute the event
ExecEvent(cevent, true);
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
std::string t;
EQTime::ToString(&cevent.next, t);
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
//save the next event time in the DB
UpdateDBEvent(cevent);
//find the next closest event timer.
FindNearestEvent();
//minor optimization, if theres no more possible events,
//then stop trying... I dunno how worth while this is.
if(EQTime::IsTimeBefore(&next_event, &tod))
return;
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
std::string t;
EQTime::ToString(&cevent.next, t);
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
//save the next event time in the DB
UpdateDBEvent(cevent);
//find the next closest event timer.
FindNearestEvent();
//minor optimization, if theres no more possible events,
//then stop trying... I dunno how worth while this is.
if(EQTime::IsTimeBefore(&next_event, &tod))
return;
}
}
}
}
@ -619,6 +624,14 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
return; //unable to find the spawn condition to operate on
}
TimeOfDay_Struct tod;
zone->zone_time.getEQTimeOfDay(&tod);
if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year))
{
_log(SPAWNS__CONDITIONS, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id);
return;
}
SpawnCondition &cond = condi->second;
int16 new_value = cond.value;
@ -666,10 +679,10 @@ void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) {
len = MakeAnyLenString(&query,
"UPDATE spawn_events SET "
"next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, "
"next_year=%d, enabled=%d "
"next_year=%d, enabled=%d, strict=%d "
"WHERE id=%d",
event.next.minute, event.next.hour, event.next.day, event.next.month,
event.next.year, event.enabled?1:0, event.id
event.next.year, event.enabled?1:0, event.strict?1:0,event.id
);
if(!database.RunQuery(query, len, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf);
@ -703,7 +716,7 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
bool ret = false;
len = MakeAnyLenString(&query,
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,zone "
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone "
"FROM spawn_events WHERE id=%d", event_id);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
@ -721,12 +734,13 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
event.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
zone_name = row[11];
event.strict = atoi(row[11])==0?false:true;
zone_name = row[12];
std::string t;
EQTime::ToString(&event.next, t);
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d. Will trigger at %s",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, t.c_str());
_log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str());
ret = true;
}
@ -794,7 +808,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
//load spawn events
SpawnEvent event;
len = MakeAnyLenString(&query,
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument "
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict "
"FROM spawn_events WHERE zone='%s'", zone_name);
if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query);
@ -816,10 +830,11 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
event.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]);
event.strict = atoi(row[11])==0?false:true;
spawn_events.push_back(event);
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument);
_log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d",
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
}
mysql_free_result(result);
} else {
@ -847,34 +862,48 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur;
if(!cevent.enabled)
continue;
bool StrictCheck = false;
if(cevent.strict &&
cevent.next.hour == tod.hour &&
cevent.next.day == tod.day &&
cevent.next.month == tod.month &&
cevent.next.year == tod.year)
StrictCheck = true;
//watch for special case of all 0s, which means to reset next to now
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
memcpy(&cevent.next, &tod, sizeof(cevent.next));
//add one period
EQTime::AddMinutes(cevent.period, &cevent.next);
//save it in the db.
UpdateDBEvent(cevent);
continue; //were done with this event.
}
//If event is disabled, or we failed the strict check, set initial spawn_condition to 0.
if(!cevent.enabled || !StrictCheck)
SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0);
ran = false;
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
//this event has been triggered.
//execute the event
ExecEvent(cevent, false);
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
ran = true;
}
//only write it out if the event actually ran
if(ran) {
//save the event in the DB
UpdateDBEvent(cevent);
if(cevent.enabled)
{
//watch for special case of all 0s, which means to reset next to now
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
memcpy(&cevent.next, &tod, sizeof(cevent.next));
//add one period
EQTime::AddMinutes(cevent.period, &cevent.next);
//save it in the db.
UpdateDBEvent(cevent);
continue; //were done with this event.
}
ran = false;
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
//this event has been triggered.
//execute the event
if(!cevent.strict || StrictCheck)
ExecEvent(cevent, false);
//add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next);
ran = true;
}
//only write it out if the event actually ran
if(ran) {
//save the event in the DB
UpdateDBEvent(cevent);
}
}
}
@ -894,14 +923,14 @@ void SpawnConditionManager::FindNearestEvent() {
int next_id = -1;
for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur;
if(!cevent.enabled)
continue;
//see if this event is before our last nearest
if(EQTime::IsTimeBefore(&next_event, &cevent.next)) {
memcpy(&next_event, &cevent.next, sizeof(next_event));
next_id = cevent.id;
if(cevent.enabled)
{
//see if this event is before our last nearest
if(EQTime::IsTimeBefore(&next_event, &cevent.next))
{
memcpy(&next_event, &cevent.next, sizeof(next_event));
next_id = cevent.id;
}
}
}
if(next_id == -1)
@ -1035,7 +1064,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) {
FindNearestEvent();
}
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool reset_base) {
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base) {
_log(SPAWNS__CONDITIONS, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without ");
@ -1048,8 +1077,9 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool rese
if(cevent.id == event_id) {
//make sure were actually changing something
if(cevent.enabled != enabled || reset_base) {
if(cevent.enabled != enabled || reset_base || cevent.strict != strict) {
cevent.enabled = enabled;
cevent.strict = strict;
if(reset_base) {
_log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period);
//start with the time now

View File

@ -67,6 +67,7 @@ public:
bool NPCPointerValid() { return (npcthis!=nullptr); }
void SetNPCPointer(NPC* n) { npcthis = n; }
void SetNPCPointerNull() { npcthis = nullptr; }
void SetTimer(uint32 duration) { timer.Start(duration); }
uint32 GetKillCount() { return killcount; }
protected:
@ -134,6 +135,7 @@ public:
bool enabled;
Action action;
int16 argument;
bool strict;
uint32 period; //eq minutes (3 seconds) between events
TimeOfDay_Struct next; //next time this event triggers
@ -148,7 +150,7 @@ public:
int16 GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id);
void SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update = false);
void ToggleEvent(uint32 event_id, bool enabled, bool reset_base);
void ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base);
bool Check(uint16 condition, int16 min_value);
void ReloadEvent(uint32 event_id);

View File

@ -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;
@ -1337,7 +1346,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
safe_delete(outapp);
}
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc) {
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) {
const Item_Struct* item = nullptr;
uint8 item_type = 0;
@ -1371,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
arc = 50;
}
const char *item_IDFile = item->IDFile;
if (IDFile && (strncmp(IDFile, "IT", 2) == 0))
item_IDFile = IDFile;
// See SendItemAnimation() for some notes on this struct
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct));
@ -1384,7 +1397,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
as->item_id = item->ID;
as->item_type = item_type;
as->skill = 0; // Doesn't seem to have any effect
strn0cpy(as->model_name, item->IDFile, 16);
strn0cpy(as->model_name, item_IDFile, 16);
as->velocity = speed;
as->launch_angle = angle;
as->tilt = tilt;
@ -1397,7 +1410,6 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
}
void NPC::DoClassAttacks(Mob *target) {
if(target == nullptr)
return; //gotta have a target for all these

View File

@ -284,20 +284,31 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value);
#endif
//im not 100% sure about this implementation.
//the spell value forumula dosent work for these... at least spell 3232 anyways
int32 val = spell.max[i];
int32 val = GetMaxHP() * spell.base[i] / 100;
if(caster)
val = caster->GetActSpellHealing(spell_id, val, this);
//This effect can also do damage by percent.
if (val < 0) {
int32 mhp = GetMaxHP();
int32 cap = mhp * spell.base[i] / 100;
if (-val > spell.max[i])
val = -spell.max[i];
if(cap < val)
val = cap;
if (caster)
val = caster->GetActSpellDamage(spell_id, val, this);
if(val > 0)
}
else
{
if (val > spell.max[i])
val = spell.max[i];
if(caster)
val = caster->GetActSpellHealing(spell_id, val, this);
}
if (val < 0)
Damage(caster, -val, spell_id, spell.skill, false, buffslot, false);
else
HealDamage(val, caster);
break;
@ -326,7 +337,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 +413,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 +444,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 +467,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 +483,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 +515,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 +856,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 +883,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 +1015,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 +1271,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 +1279,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 +1305,7 @@ 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;
}
@ -1384,7 +1409,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);
@ -1560,8 +1586,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;
}
@ -1631,12 +1664,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;
@ -1651,6 +1683,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
{
CastToClient()->SummonHorse(spell_id);
}
break;
}
@ -1698,19 +1732,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);
@ -2597,14 +2637,37 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
HealDamage(dmg, caster);
}
}
break;
}
case SE_Taunt:
{
if (IsNPC())
caster->Taunt(this->CastToNPC(), false, spell.base[i]);
break;
}
case SE_AttackSpeed:
if (spell.base[i] < 100)
SlowMitigation(caster);
break;
case SE_AttackSpeed2:
if (spell.base[i] < 100)
SlowMitigation(caster);
break;
case SE_AttackSpeed3:
if (spell.base[i] < 0)
SlowMitigation(caster);
break;
case SE_AttackSpeed4:
SlowMitigation(caster);
break;
// Handled Elsewhere
case SE_ImmuneFleeing:
case SE_NegateSpellEffect:
@ -2690,10 +2753,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_DivineSave:
case SE_Accuracy:
case SE_Flurry:
case SE_AttackSpeed:
case SE_AttackSpeed2:
case SE_AttackSpeed3:
case SE_AttackSpeed4:
case SE_ImprovedDamage:
case SE_ImprovedHeal:
case SE_IncreaseSpellHaste:
@ -3230,7 +3289,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;
}
@ -3322,9 +3381,36 @@ 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 70% chance to do a resist check to break.
*/
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;
@ -3694,8 +3780,8 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
case SE_Root:
{
buffs[slot].RootBreakChance = 0;
rooted = false;
rooted_mod = 0;
break;
}
@ -3913,6 +3999,15 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
bool LimitFailure = false;
bool LimitInclude[MaxLimitInclude] = { false };
/* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells.
0/1 SE_LimitResist
2/3 SE_LimitSpell
4/5 SE_LimitEffect
6/7 SE_LimitTarget
8/9 SE_LimitSpellGroup:
10/11 SE_LimitCastingSkill:
Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes
*/
int FocusCount = 0;
std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID);
@ -3927,7 +4022,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
base1 = iter->second.base1;
base2 = iter->second.base2;
slot = iter->second.slot;
/*
AA Foci's can contain multiple focus effects within the same AA.
To handle this we will not automatically return zero if a limit is found.
@ -3981,8 +4076,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
break;
case SE_LimitInstant:
if(spell.buffduration)
if(base1 == 1 && spell.buffduration) //Fail if not instant
LimitFailure = true;
if(base1 == 0 && (spell.buffduration == 0)) //Fail if instant
LimitFailure = true;
break;
case SE_LimitMaxLevel:
@ -4030,13 +4128,14 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
case SE_LimitMinDur:
if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration))
LimitFailure = true;
break;
break;
case SE_LimitEffect:
if(base1 < 0){
if(IsEffectInSpell(spell_id,-base1)) //Exclude
LimitFailure = true;
}
}
else{
LimitInclude[4] = true;
if(IsEffectInSpell(spell_id,base1)) //Include
@ -4081,10 +4180,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
break;
case SE_LimitCombatSkills:
if (base1 == 0 && IsCombatSkill(spell_id)) //Exclude Discs
if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
LimitFailure = true;
else if (base1 == 1 && !IsCombatSkill(spell_id)) //Exclude Spells
else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
LimitFailure = true;
break;
case SE_LimitSpellGroup:
@ -4115,7 +4215,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
if (!PassLimitClass(base1, GetClass()))
LimitFailure = true;
break;
break;
case SE_LimitRace:
if (base1 != GetRace())
@ -4376,8 +4476,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
break;
case SE_LimitInstant:
if(spell.buffduration)
if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant
return 0;
if(focus_spell.base[i] == 0 && (spell.buffduration == 0)) //Fail if instant
return 0;
break;
case SE_LimitMaxLevel:
@ -4482,10 +4585,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
break;
case SE_LimitCombatSkills:
if (focus_spell.base[i] == 0 && IsCombatSkill(spell_id)) //Exclude Disc
return 0;
else if (focus_spell.base[i] == 1 && !IsCombatSkill(spell_id)) //Include Spells
if (focus_spell.base[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
return 0;
else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
return 0;
break;
case SE_LimitSpellGroup:
@ -5963,3 +6067,82 @@ 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);
}
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1);
}
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
else {
//Only use fire graphic for fire spells.
if (spells[spell_id].resisttype == RESIST_FIRE) {
if (IsClient()){
if (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);
}
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
else
ProjectileAnimation(spell_target,0, 1, 1.5);
}
if (spells[spell_id].CastingAnim == 64)
anim = 44; //Corrects for animation error.
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
return true;
}

View File

@ -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++) {
@ -563,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);
@ -586,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:
@ -1818,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);
@ -1883,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)
@ -1892,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
}
}
if(IsPlayerIllusionSpell(spell_id)
&& IsClient()
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
@ -2595,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];
@ -2639,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",
@ -2960,6 +2986,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
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;
@ -3417,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);
@ -4044,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)
@ -4083,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)
{
@ -4093,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);
@ -4171,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;
@ -4269,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)
@ -4334,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;
@ -4829,6 +4966,28 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
return false;
}
bool Mob::IsCombatProc(uint16 spell_id) {
if (RuleB(Spells, FocusCombatProcs))
return false;
if(spell_id == SPELL_UNKNOWN)
return(false);
if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0))
{
for (int i = 0; i < MAX_PROCS; i++){
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
|| SkillProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){
return true;
}
}
}
return false;
}
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) {
if(spell_id == SPELL_UNKNOWN)
return(false);
@ -5132,51 +5291,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
{

View File

@ -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.

View File

@ -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,"
@ -1098,7 +1099,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.underwater,"
"npc_types.emoteid,"
"npc_types.spellscale,"
"npc_types.healscale";
"npc_types.healscale,"
"npc_types.no_target_hotkey";
MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id);
@ -1143,6 +1145,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++]);
@ -1189,7 +1192,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8;
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF);
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
int i;
if (armor_tint_id > 0)
{
@ -1270,7 +1273,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true;
tmpNPCType->ATK = atoi(row[r++]);
tmpNPCType->accuracy_rating = atoi(row[r++]);
tmpNPCType->slow_mitigation = atof(row[r++]);
tmpNPCType->slow_mitigation = atoi(row[r++]);
tmpNPCType->maxlevel = atoi(row[r++]);
tmpNPCType->scalerate = atoi(row[r++]);
tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false;
@ -1279,7 +1282,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->emoteid = atoi(row[r++]);
tmpNPCType->spellscale = atoi(row[r++]);
tmpNPCType->healscale = atoi(row[r++]);
tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false;
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end())
@ -2654,13 +2658,8 @@ void ZoneDatabase::LoadBuffs(Client *c) {
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);
}
}
mysql_free_result(result);

View File

@ -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?
@ -110,7 +111,7 @@ struct NPCType
int accuracy_rating; //10 = 1% accuracy
bool findable; //can be found with find command
bool trackable;
float slow_mitigation; // Slow mitigation % in decimal form.
int16 slow_mitigation;
uint8 maxlevel;
uint32 scalerate;
bool private_corpse;
@ -119,6 +120,7 @@ struct NPCType
uint32 emoteid;
float spellscale;
float healscale;
bool no_target_hotkey;
};
/*