Compare commits

..

161 Commits

Author SHA1 Message Date
Michael Cook (mackal) 9b70b73759 Correct tell queue related messages 2014-09-19 18:17:42 -04:00
Michael Cook (mackal) 8e43134bda Add Client::Tell_StringID for tell queue messages 2014-09-19 18:16:52 -04:00
Michael Cook (mackal) d26782b093 Nuke #viewmessages 2014-09-18 22:56:16 -04:00
Michael Cook (mackal) a2368b4ea7 Implement tell queues
Default queue size 20 (World:TellQueueSize)
This doe not play well with multiple sessions and a toon crashes and relogs
Normal tells have issues as well.
2014-09-18 22:55:06 -04:00
Michael Cook (mackal) 3d6bb964df Stop nuking MySQLRequestResult Success flag 2014-09-18 14:59:50 -04:00
Michael Cook (mackal) 347ae1bc34 Fix error in 64f5bfd 2014-09-18 01:29:30 -04:00
Michael Cook (mackal) 64f5bfd5ce Make tell message a bit more understandable. 2014-09-17 23:46:54 -04:00
Michael Cook (mackal) 86c1420f6e Fix some effect value calcs and implement more
Derived from the client
2014-09-17 00:01:32 -04:00
Uleat 364ed921ce Fix for LoadBuffs() server crash 2014-09-16 22:57:15 -04:00
Michael Cook (mackal) 85e1518856 Implement spell formula 137
This is only used in the BER AA Desperation
2014-09-16 18:35:49 -04:00
Michael Cook (mackal) 11ce399e0d Mob::ProcessSpecialAbilities pass by reference 2014-09-15 20:31:45 -04:00
Michael Cook (mackal) ec0989454d QGlobalCache::LoadBy to pass by reference 2014-09-15 20:11:56 -04:00
Michael Cook (mackal) e9f6031936 Merge pull request #250 from KayenEQ/Development
Nimbus Updates / Other fixes
2014-09-15 18:57:47 -04:00
KayenEQ 03485ef1e0 Nimbus effects will now be reapplied after zoning.
Nimbus effects will now fade when associated buff is removed.
Fix for ReduceAllDamage function.
2014-09-15 16:05:57 -04:00
Michael Cook (mackal) e256175ce6 Style nits 2014-09-14 16:00:18 -04:00
Michael Cook (mackal) 0f662bf70c Fix ZoneDatabase::LoadTraderItem not returning anything valid 2014-09-14 15:58:08 -04:00
Michael Cook (mackal) 4c959159c2 Fix rogues not starting with Thieves' Cant 2014-09-13 03:20:43 -04:00
Michael Cook (mackal) d51241720a Fix attack_delay for real 2014-09-12 13:10:03 -04:00
Michael Cook (mackal) 873d343529 Fix ZoneDatabase::GetNPCType nuking attack_delay changes 2014-09-12 11:57:09 -04:00
Michael Cook (mackal) 95969ce67b Fix ZoneDatabase::GetZoneCFG for real 2014-09-11 18:28:25 -04:00
Michael Cook (mackal) bd5cdf502e Fix ZoneDatabase::GetZoneCFG errors 2014-09-11 18:24:40 -04:00
Michael Cook (mackal) c6a7d5a96c Revert "GetAccountInfoForLogin_result converted to MySQLRequestResult"
This reverts commit 8369570b50.

Conflicts:
	zone/zonedb.h
2014-09-11 16:57:00 -04:00
Michael Cook (mackal) 6bc4ecf390 Revert "LoadFactionValues converted to QueryDatabase"
This reverts commit 2df823d2db.
2014-09-11 16:56:15 -04:00
Michael Cook (mackal) c94ceb5b1d Revert "LoadFactionValues_result converted to MySQLRequestResult"
This reverts commit 2e84781594.
2014-09-11 16:55:58 -04:00
Alex bb702335e8 Merge pull request #247 from KayenEQ/Development
Development
2014-09-11 00:21:45 -07:00
Alex 863c0c5b58 Merge pull request #241 from addtheice/RunQueryToDatabaseQuery_zone_titles
Run query to database query zone titles
2014-09-11 00:21:00 -07:00
Alex 76e280da4e Merge pull request #239 from addtheice/RunQueryToDatabaseQuery_zone_waypoints
GetHighestGrid converted to QueryDatabase
2014-09-11 00:19:43 -07:00
Alex bcca35b7b7 Merge pull request #238 from addtheice/RunQueryToDatabaseQuery_zone_guild_mgr
AddItem converted to QueryDatabase
2014-09-11 00:18:57 -07:00
Alex 65abaade88 Merge pull request #237 from addtheice/RunQueryToDatabaseQuery_zone_guild
Run query to database query zone guild
2014-09-11 00:18:21 -07:00
Alex 8020e921aa Merge pull request #234 from addtheice/RunQueryToDatabaseQuery_queryserv_database
Run query to database query queryserv database
2014-09-11 00:17:56 -07:00
Alex f2f5b4c1ad Merge pull request #222 from addtheice/RunQueryToDatabaseQuery_zone_client
Run query to database query zone client
2014-09-11 00:16:37 -07:00
Alex 5c6f9fcdc4 Merge pull request #215 from addtheice/RunQueryToDatabaseQuery_zone_zonedb
Run query to database query zone zonedb
2014-09-11 00:16:15 -07:00
KimLS 9e243a2426 bestz will no longer scale off of model size, also it adjusts up less extremely in general. Fixed super duper attack speed on command spawned npcs 2014-09-10 22:33:30 -07:00
Arthur Ice 9a889802d3 merge upstream. yet again 2014-09-09 21:26:26 -07:00
Michael Cook (mackal) 1420987c4c Fix copy paste error 2014-09-10 00:07:16 -04:00
Michael Cook (mackal) ed4d954ba8 Added attackdelay to #npcedit
Also changelog!
2014-09-10 00:05:00 -04:00
Michael Cook (mackal) 881f937a35 Change scale of GetPermaHaste() 2014-09-09 22:50:50 -04:00
Michael Cook (mackal) ed4e762f03 Change NPCs to have their attack delay set in DB
This gives us a much more straight forward way of setting mob
attack delay with respect to live.

The attack_delay column is in 10ths of seconds, just like weapons are
The attack_speed is left for references for now.
2014-09-09 22:42:54 -04:00
Michael Cook (mackal) fa1e33783a Split Mob::SetAttackTimer into Client and NPC methods
This was the easiest way to NPCs delay settings to be sane in the database.
The functions are cleaner since there is no specific logic to change
behavior depending on if they're a client or not.
2014-09-09 22:13:30 -04:00
Michael Cook (mackal) 2fa31799f6 Port slow fixes to bots 2014-09-09 17:02:05 -04:00
Michael Cook (mackal) 6c3d5c713c Fix slow effect on NPC special attack reuse timers 2014-09-09 15:34:30 -04:00
Michael Cook (mackal) f16beddf6e Increase NPC bash/kick delay by 3 (8 total by default) 2014-09-09 15:32:02 -04:00
Michael Cook (mackal) 506b3ca4a0 Fix slow calculation
Full details: http://www.eqemulator.org/forums/showthread.php?t=38734
2014-09-08 21:43:25 -04:00
KimLS 80242bd250 Strange crash hack/workaround that occured on peq 2014-09-08 17:02:37 -07:00
KimLS 7f7f99cbe3 BestZ will now adjust for model size when used to adjust movement z 2014-09-08 16:45:20 -07:00
KimLS 579294fbf0 Fixed many bugs with partial resist calculations, targets with higher resist should have a more natural curve when it comes to resisting spells 2014-09-08 03:04:22 -07:00
KimLS a3b54e5cae Fix for forage crash 2014-09-07 22:41:42 -07:00
akkadius b392d16808 Fixed ROF Augment item dupe with not checking for available slots properly and adding items to the virtual instance 2014-09-07 05:43:37 -05:00
KayenEQ 616e13acac Calc fix for spell power distance mod effect. 2014-09-07 03:43:34 -04:00
KayenEQ b50f660339 Merge git://github.com/EQEmu/Server into Development 2014-09-07 03:40:12 -04:00
KimLS 84310ec8f0 Merge branch 'master' of github.com:EQEmu/Server 2014-09-06 17:09:33 -07:00
KimLS ffed5a9e22 SQL Injection fix on inspect message setting 2014-09-06 16:43:36 -07:00
Michael Cook (mackal) 92c9ff6e53 Fix issue with hpregen in command_npcedit 2014-09-06 17:58:42 -04:00
KimLS 986a424322 Fix for perl api illusion 2014-09-06 14:31:02 -07:00
Uleat c3e7c48939 Fix ItemTimerCheck() range criteria typo 2014-09-06 16:19:32 -04:00
Arthur Ice b2aa3262a9 Update client.cpp
fixed bug with BEGIN_GENERAL to MainCursor being skipped before since it was starting at GENERAL_BAGS_BEGIN
2014-09-06 12:56:35 -07:00
Arthur Ice b1587f0326 Delete client.cpp.orig
not supposed to be in there.
2014-09-06 12:51:50 -07:00
Uleat 281b321237 Changed trade behavior to prioritize all main slots before sub slots 2014-09-06 12:45:45 -04:00
KimLS ba0e4bfc1d Bizarre issue reported with SendIllusionPacket corrupting size in lua, dunno if this fixes it but fingers crossed. 2014-09-05 21:18:35 -07:00
Michael Cook (mackal) 3cda62acf4 Fix changelog [skip ci] 2014-09-05 22:38:32 -04:00
Michael Cook (mackal) 2ef43212e1 Fix size issue with Lua_Mob::SendIllusionPacket
Mob::SendIllusionPacket was expecting the size to be 0xFFFFFFFF
to default rather than -1.0f
2014-09-05 22:36:44 -04:00
Michael Cook (mackal) f69eccc42b Add missing QueryDatabase in ZoneDatabase::AddWPForSpawn 2014-09-05 21:57:34 -04:00
Michael Cook (mackal) a1e425f936 Fix indentation of ZoneDatabase::AddWPForSpawn 2014-09-05 21:57:34 -04:00
Uleat 6186c3d866 Fix for gcc failure - Can't fix stupid! 2014-09-05 16:46:03 -04:00
Uleat 1d0a6bdc71 Fix for losing 2nd and 3rd cursor items after zoning 2014-09-05 16:16:56 -04:00
Arthur Ice 399bf96a0c RemoveTitle converted to QueryDatabase 2014-09-03 21:05:31 -07:00
Arthur Ice b7c409e11b CheckTitle converted to QueryDatabase 2014-09-03 21:03:00 -07:00
Arthur Ice fd08e9f2ad EnableTitle converted to QueryDatabase 2014-09-03 21:00:15 -07:00
Arthur Ice 9a4d01da8f CreateNewPlayerSuffix converted to QueryDatabase 2014-09-03 20:57:34 -07:00
Arthur Ice 81cf748b2b CreateNewPlayerTitle converted to QueryDatabase 2014-09-03 20:52:00 -07:00
Arthur Ice 7d8d96c049 LoadTitles converted to QueryDatabase 2014-09-03 20:45:38 -07:00
Arthur Ice f5e49441b6 GetHighestGrid converted to QueryDatabase 2014-09-03 19:28:25 -07:00
Arthur Ice 610f3ed37f AddItem converted to QueryDatabase 2014-09-03 19:17:55 -07:00
Arthur Ice f215874486 SetGuildDoor converted to QueryDatabase 2014-09-03 19:10:27 -07:00
Arthur Ice 8e529105cf CheckGuildDoor converted to QueryDatabase 2014-09-03 19:07:49 -07:00
Arthur Ice 0996570b78 merge from upstream 2014-08-28 15:49:16 -07:00
Arthur Ice 0a9732a267 LogMerchantTransaction converted to QueryDatabase 2014-08-28 00:10:55 -07:00
Arthur Ice 1bc06c9c24 LogPlayerMove converted to QueryDatabase 2014-08-28 00:05:28 -07:00
Arthur Ice e0acc937b3 LogPlayerDelete converted to QueryDatabase 2014-08-28 00:04:20 -07:00
Arthur Ice 3a10a0129a LogPlayerNPCKill converted to QueryDatabase 2014-08-27 23:53:37 -07:00
Arthur Ice 18dbcf16cc LogPlayerHandin converted to QueryDatabase 2014-08-27 23:52:36 -07:00
Arthur Ice bed8dc7d34 LogPlayerTrade converted to QueryDatabase 2014-08-27 23:52:35 -07:00
Arthur Ice a0fc9844fd AddSpeech converted to QueryDatabase 2014-08-27 23:21:09 -07:00
Arthur Ice 9ddb56088e merge from master 2014-08-26 18:49:15 -07:00
KayenEQ 44dcf7af7d Merge git://github.com/EQEmu/Server into Development 2014-08-25 09:23:23 -04:00
Arthur Ice 3262bee6c5 ExpeditionSay converted to QueryDatabase 2014-08-23 23:26:45 -07:00
Arthur Ice 2dbd616725 SetAccountFlag converted to QueryDatabase 2014-08-23 23:23:26 -07:00
Arthur Ice eb98563fa1 LoadAccountFlags converted to QueryDatabase 2014-08-23 23:20:55 -07:00
Arthur Ice 7f92e96ae7 TryReward converted to QueryDatabase 2014-08-23 23:15:49 -07:00
Arthur Ice a48138dfd6 SendRewards converted to QueryDatabase 2014-08-23 23:05:58 -07:00
Arthur Ice 46980e5260 DiscoverItem converted to QueryDatabase 2014-08-23 22:59:58 -07:00
Arthur Ice 44f9e5495e IsDiscovered converted to QueryDatabase 2014-08-23 22:59:53 -07:00
Arthur Ice 97f59282cf KeyRingAdd converted to QueryDatabase 2014-08-23 22:49:27 -07:00
Arthur Ice 2a4a5b1beb KeyRingLoad converted to QueryDatabase 2014-08-23 22:46:29 -07:00
Arthur Ice 8369570b50 GetAccountInfoForLogin_result converted to MySQLRequestResult 2014-08-21 17:35:04 -07:00
Arthur Ice 9ff0c414c1 LoadFactionData converted to QueryDatabase 2014-08-21 17:17:27 -07:00
Arthur Ice 850d1e7c28 SetCharacterFactionLevel converted to QueryDatabase 2014-08-21 17:08:31 -07:00
Arthur Ice 2df823d2db LoadFactionValues converted to QueryDatabase 2014-08-21 17:04:08 -07:00
Arthur Ice 2e84781594 LoadFactionValues_result converted to MySQLRequestResult 2014-08-21 17:00:50 -07:00
Arthur Ice 833227f7f6 LoadPetInfo converted to QueryDatabase 2014-08-21 15:58:11 -07:00
Arthur Ice e731cfd48d RemoveTempFactions converted to QueryDatabase 2014-08-21 15:32:20 -07:00
Arthur Ice cf7574d9b8 SavePetInfo converted to QueryDatabase 2014-08-21 15:30:27 -07:00
Arthur Ice 10d384f131 LoadBuffs converted to QueryDatabase 2014-08-21 13:54:18 -07:00
Arthur Ice 00b8c8ce47 SaveBuffs converted to QueryDatabase 2014-08-21 13:42:02 -07:00
Arthur Ice cab43f41be UpdateAltCurrencyValues converted to QueryDatabase 2014-08-21 13:36:01 -07:00
Arthur Ice fc0d589f12 LoadAltCurrencyValues converted to QueryDatabase 2014-08-21 13:34:19 -07:00
Arthur Ice 743175d4ff InsertDoor converted to QueryDatabase 2014-08-21 13:26:38 -07:00
Arthur Ice b497b07fed QGlobalPurge converted to QueryDatabase 2014-08-21 13:22:21 -07:00
Arthur Ice aab5ed2267 ListAllInstances converted to QueryDatabase 2014-08-21 13:19:37 -07:00
Arthur Ice 46c9fe46e9 UpdateKarma converted to QueryDatabase 2014-08-21 13:09:43 -07:00
Arthur Ice e8c92c6fcc GetKarma converted to QueryDatabase 2014-08-21 13:09:43 -07:00
Arthur Ice 765eaf7f4f getZoneShutDownDelay converted to QueryDatabase 2014-08-21 12:51:09 -07:00
Arthur Ice 699c8cc1eb LoadBlockedSpells converted to QueryDatabase 2014-08-21 12:46:52 -07:00
Arthur Ice 5839921e08 GetBlockedSpellsCount converted to QueryDatabase 2014-08-21 12:39:38 -07:00
Arthur Ice a3bde6e1f1 RaidGroupCount converted to QueryDatabase 2014-08-21 12:35:33 -07:00
Arthur Ice 0ece5bf178 GroupCount converted to QueryDatabase 2014-08-21 12:30:06 -07:00
Arthur Ice fbefad9eaf RefreshGroupFromDB converted to QueryDatabase 2014-08-21 12:25:32 -07:00
Arthur Ice 351a7a52fe GetCharacterInfoForLogin removed, unused, unsupported 2014-08-21 12:01:22 -07:00
Arthur Ice 8441ffda31 GetAccountInfoForLogin removed, unused, unsupported 2014-08-21 11:52:15 -07:00
Arthur Ice e79747c919 SetZoneTZ converted to QueryDatabase 2014-08-20 23:26:41 -07:00
Arthur Ice 8525d819c1 GetZoneTZ converted to QueryDatabase 2014-08-20 23:13:59 -07:00
Arthur Ice fe600bb084 Removed Get/SetServerFilters, unused and unsupported 2014-08-20 23:07:19 -07:00
Arthur Ice babaff1985 GetUseCFGSafeCoords converted to QueryDatabase 2014-08-20 22:14:40 -07:00
Arthur Ice 909ca5440d UpdateZoneSafeCoords converted to QueryDatabase 2014-08-20 22:11:14 -07:00
Arthur Ice e25f64d03b SaveMerchantTemp converted to QueryDatabase 2014-08-20 22:06:56 -07:00
Arthur Ice 2df66bd625 GetGridType converted to QueryDatabase 2014-08-20 22:04:40 -07:00
Arthur Ice 2028a5846c LoadMercEquipment converted to QueryDatabase 2014-08-20 22:00:00 -07:00
Arthur Ice 2fd2cd4cec DeleteMerc converted to QueryDatabase 2014-08-20 21:54:28 -07:00
Arthur Ice 17b175daa4 LoadMercBuffs converted to QueryDatabase 2014-08-20 21:46:23 -07:00
Arthur Ice 5cabe109da SaveMercBuffs converted to QueryDatabase 2014-08-20 21:30:51 -07:00
Arthur Ice c6e82448b6 SaveMerc converted to QueryDatabase 2014-08-20 21:17:17 -07:00
Arthur Ice b846d89b5d LoadCurrentMerc converted to QueryDatabase 2014-08-20 21:04:00 -07:00
Arthur Ice 322cea7342 LoadMercInfo converted to QueryDatabase 2014-08-20 20:58:11 -07:00
Arthur Ice 205e1d404e GetMercType converted to QueryDatabase 2014-08-20 20:50:36 -07:00
Arthur Ice 9769a96ebd GetNPCType converted to QueryDatabase 2014-08-20 20:21:39 -07:00
Arthur Ice 74d3192c2e NoRentExpired converted to QueryDatabase 2014-08-20 19:46:44 -07:00
Arthur Ice 41769a3fa8 UpdateBuyLine converted to QueryDatabase 2014-08-20 19:28:53 -07:00
Arthur Ice a7efa9d4e4 RemoveBuyLine converted to QueryDatabase 2014-08-20 19:20:32 -07:00
Arthur Ice 9ed69999a5 AddBuyLine converted to QueryDatabase 2014-08-20 19:02:28 -07:00
Arthur Ice e19a59b269 DeleteBuyLines converted to QueryDatabase 2014-08-20 18:59:17 -07:00
Arthur Ice d234016224 DeleteTraderItem converted to QueryDatabase 2014-08-20 18:55:42 -07:00
Arthur Ice c160d6d929 DeleteTraderItem converted to QueryDatabase 2014-08-20 18:53:05 -07:00
Arthur Ice 62ad60b4ad UpdateTraderItemPrice converted to QueryDatabase 2014-08-20 18:50:10 -07:00
Arthur Ice d44d7c6bbd UpdateTraderItemCharges converted to QueryDatabase 2014-08-20 18:42:25 -07:00
Arthur Ice efd97bad14 SaveTraderItem converted to QueryDatabase 2014-08-20 18:38:57 -07:00
Arthur Ice bc884f5daf LoadSingleTraderItem converted to QueryDatabase 2014-08-20 18:35:54 -07:00
Arthur Ice 0f47b73a64 LoadTraderItemWithCharges converted to QueryDatabase 2014-08-20 18:22:31 -07:00
Arthur Ice e9c6e96452 LoadTraderItem converted to QueryDatabase 2014-08-20 18:22:18 -07:00
Arthur Ice 09713311f6 DeleteWorldContainer converted to QueryDatabase 2014-08-20 18:16:45 -07:00
Arthur Ice c666d9c553 SaveWorldContainer converted to QueryDatabase 2014-08-20 18:03:02 -07:00
Arthur Ice 3777e8d1ce LoadWorldContainer converted to QueryDatabase 2014-08-20 17:57:44 -07:00
Arthur Ice e60658c684 GetEventLogs converted to QueryDatabase 2014-08-20 17:51:52 -07:00
Arthur Ice 046da9efae SetSpecialAttkFlag converted to QueryDatabase 2014-08-20 17:46:12 -07:00
Arthur Ice 09332f6c26 UpdateBug converted to QueryDatabase 2014-08-20 17:43:16 -07:00
Arthur Ice ce507d891a UpdateBug converted to QueryDatabase 2014-08-20 17:43:16 -07:00
Arthur Ice 96f122f901 logevents converted to QueryDatabase 2014-08-20 17:31:16 -07:00
Arthur Ice 3479525f39 UpdateSpawn2Status converted to QueryDatabase 2014-08-20 17:27:19 -07:00
Arthur Ice 4f9d4b0023 GetSpawnTimeLeft converted to QueryDatabase 2014-08-20 17:25:27 -07:00
Arthur Ice bcdfd32bc0 UpdateSpawn2Timeleft converted to QueryDatabase 2014-08-20 17:21:07 -07:00
Arthur Ice 693dde04e3 GetZoneCFG converted to QueryDatabase 2014-08-20 17:16:30 -07:00
Arthur Ice f1bb019933 SaveZoneCFG converted to QueryDatabase 2014-08-20 16:46:17 -07:00
48 changed files with 2719 additions and 3087 deletions
+46
View File
@@ -1,5 +1,51 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 09/19/2014 ==
demonstar55: Added Client::Tell_StringID (used in tell queue messages)
demonstar55: Tell queues (and offline) messages now show correctly
== 09/18/2014==
demonstar55: Implement tell queues
Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+)
The required SQL nukes the old tell queue table, which may or may not be in your DB
Optional SQL adds the rule to the DB to allow easy of change
Note: this does not play well with multiple sessions with the same name on (crash and relog and have multiple sessions) but normal tells don't play well either
== 09/16/2014 ==
demonstar55: Implement spell formula 137 (BER AA Desperation)
Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded.
demonstar55: Fix some effect calcs + implement more (derived from the client)
== 09/15/2014 ==
Kayen: Nimbus effects will now be reapplied after zoning and will be removed when associated buff fades.
== 09/13/2014 ==
demonstar55: Fix rogues not having Thieves' Cant
== 09/09/2014 ==
demonstar55: Incrase Mob kick/bash timer by 3
see: http://www.eqemulator.org/forums/showthread.php?t=38734
demonstar55: Fix slow effect on NPC special attack reuse timers
see: http://www.eqemulator.org/forums/showthread.php?t=38734
demonstar55: Slow fixes to bots!
demonstar55: Revamped how NPC attack rate is set
SQL: 2014_09_09_attack_delay.sql
demonstar55: Added attackdelay to #npcedit
== 09/08/2014 ==
demonstar55: Fix slow calc
see: http://www.eqemulator.org/forums/showthread.php?t=38734
== 09/07/2014 ==
Akkadius: Fixed ROF Augment item dupe with not checking for available slots properly and adding items to the virtual instance
== 09/06/2014 ==
Uleat: Tweaked 'Smart' trading code to return main slots before sub slots in stackable and free space search processes. (If enough people ask for it, I'll add an optional rule to allow 'bag packing' - the original implementation behavior)
== 09/05/2014 ==
Uleat: Fix for cursor item loss when zoning. (Thanks to the other devs who traced and fixed the 'macro' issue!)
demonstar55: Fix size getting nuked with lua's SendIllusionPacket
== 09/03/2014 ==
Secrets: Identified the routines needed to augment items in RoF. Currently, only Insert and Remove are supported. Swap and Destroy do not work due to missing functions related to the cursor.
demonstar55: Added work around command to show numhits on your buffs (#shownumhits)
+1 -1
View File
@@ -319,7 +319,7 @@ bool Database::DeleteAccount(const char* name) {
}
bool Database::SetLocalPassword(uint32 accid, const char* password) {
std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", password, accid);
std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", EscapeString(password).c_str(), accid);
auto results = QueryDatabase(query);
+22 -1
View File
@@ -683,6 +683,13 @@ int16 Inventory::FindFreeSlotForTradeItem(const ItemInst* inst) {
if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize))
return free_slot;
}
for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) {
const ItemInst* main_inst = m_inv[free_slot];
if (!main_inst)
continue;
if (main_inst->IsType(ItemClassContainer)) { // if item-specific containers already have bad items, we won't fix it here...
for (uint8 free_bag_slot = SUB_BEGIN; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++free_bag_slot) {
@@ -732,8 +739,12 @@ int16 Inventory::FindFreeSlotForTradeItem(const ItemInst* inst) {
if (!main_inst)
return free_slot;
}
if (main_inst->IsType(ItemClassContainer)) {
for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) {
const ItemInst* main_inst = m_inv[free_slot];
if (main_inst && main_inst->IsType(ItemClassContainer)) {
if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == BagTypeBandolier) || (main_inst->GetItem()->BagType == BagTypeQuiver))
continue;
@@ -1569,6 +1580,16 @@ int8 ItemInst::AvailableAugmentSlot(int32 augtype) const
return (i < EmuConstants::ITEM_COMMON_SIZE) ? i : INVALID_INDEX;
}
bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const {
if (m_item->ItemClass != ItemClassCommon || !m_item)
return false;
if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) {
return true;
}
return false;
}
// Retrieve item inside container
ItemInst* ItemInst::GetItem(uint8 index) const
{
+1
View File
@@ -274,6 +274,7 @@ public:
inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0 || m_item->AugSlotType[1]!=0 || m_item->AugSlotType[2]!=0 || m_item->AugSlotType[3]!=0 || m_item->AugSlotType[4]!=0; }
bool AvailableWearSlot(uint32 aug_wear_slots) const;
int8 AvailableAugmentSlot(int32 augtype) const;
bool IsAugmentSlotAvailable(int32 augtype, uint8 slot) const;
inline int32 GetAugmentType() const { return m_item->AugType; }
inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); }
+5 -5
View File
@@ -10,7 +10,7 @@ MySQLRequestResult::MySQLRequestResult()
MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer)
: m_CurrentRow(result), m_OneBeyondRow()
{
m_Result = result;
m_Result = result;
m_RowsAffected = rowsAffected;
m_RowCount = rowCount;
m_ColumnCount = columnCount;
@@ -22,12 +22,12 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u
m_ColumnLengths = nullptr;
m_Fields = nullptr;
if (errorBuffer != nullptr)
m_Success = true;
if (errorBuffer != nullptr)
m_Success = false;
m_Success = true;
m_ErrorNumber = errorNumber;
m_ErrorBuffer = errorBuffer;
m_ErrorNumber = errorNumber;
m_ErrorBuffer = errorBuffer;
}
void MySQLRequestResult::FreeInternals()
+1 -4
View File
@@ -171,6 +171,7 @@ RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = R
RULE_BOOL (World, IsGMPetitionWindowEnabled, false)
RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items.
RULE_BOOL (World, IPLimitDisconnectAll, false)
RULE_INT (World, TellQueueSize, 20)
RULE_CATEGORY_END()
RULE_CATEGORY( Zone )
@@ -212,10 +213,6 @@ RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max
RULE_REAL ( Map, FixPathingZMaxDeltaWaypoint, 20 ) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply.
RULE_REAL ( Map, FixPathingZMaxDeltaSendTo, 20 ) //at runtime in SendTo: max change in Z to allow the BestZ code to apply.
RULE_REAL ( Map, FixPathingZMaxDeltaLoading, 45 ) //while loading each waypoint: max change in Z to allow the BestZ code to apply.
RULE_BOOL ( Map, UseClosestZ, false) // Move mobs to the nearest Z above or below, rather than just the nearest below.
// Only set UseClosestZ true if all your .map files generated from EQGs were created
// with azone2.
//
RULE_INT ( Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position
RULE_CATEGORY_END()
+6
View File
@@ -83,6 +83,7 @@
#define ServerOP_QGlobalUpdate 0x0063
#define ServerOP_QGlobalDelete 0x0064
#define ServerOP_DepopPlayerCorpse 0x0065
#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues
#define ServerOP_RaidAdd 0x0100 //in use
#define ServerOP_RaidRemove 0x0101 //in use
@@ -346,6 +347,7 @@ struct ServerChannelMessage_Struct {
uint16 chan_num;
uint32 guilddbid;
uint16 language;
uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline
char message[0];
};
@@ -1237,6 +1239,10 @@ struct ReloadWorld_Struct{
uint32 Option;
};
struct ServerRequestTellQueue_Struct {
char name[64];
};
#pragma pack()
#endif
+15 -10
View File
@@ -143,19 +143,22 @@ uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) {
bool SharedDatabase::SaveCursor(uint32 char_id, std::list<ItemInst*>::const_iterator &start, std::list<ItemInst*>::const_iterator &end)
{
iter_queue it;
int i;
bool ret=true;
iter_queue it;
int i;
bool ret = true;
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
// Delete cursor items
if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND ( (slotid >=8000 and slotid<=8999) or slotid=%i or (slotid>=%i and slotid<=%i))", char_id, MainCursor,EmuConstants::CURSOR_BAG_BEGIN,EmuConstants::CURSOR_BAG_END), errbuf))) {
for(it=start,i=8000;it!=end;++it,i++) {
ItemInst *inst=*it;
if (!(ret=SaveInventory(char_id,inst,(i==8000) ? MainCursor : i)))
if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid = %i AND ((slotid >= 8000 AND slotid <= 8999) OR slotid = %i OR (slotid >= %i AND slotid <= %i))",
char_id, MainCursor, EmuConstants::CURSOR_BAG_BEGIN, EmuConstants::CURSOR_BAG_END), errbuf))) {
for (it = start, i = 8000; it != end; ++it, i++) {
ItemInst *inst = *it;
if (!(ret = SaveInventory(char_id, inst, (i == 8000) ? MainCursor : i)))
break;
}
} else {
}
else {
std::cout << "Clearing cursor failed: " << errbuf << std::endl;
}
safe_delete_array(query);
@@ -2145,7 +2148,8 @@ void SharedDatabase::SetPlayerInspectMessage(char* playername, const InspectMess
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", message->text, playername), errbuf)) {
std::string msg = EscapeString(message->text);
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", msg.c_str(), playername), errbuf)) {
std::cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl;
}
@@ -2180,7 +2184,8 @@ void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Str
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", message->text, botid), errbuf)) {
std::string msg = EscapeString(message->text);
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", msg.c_str(), botid), errbuf)) {
std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << std::endl;
}
+227 -165
View File
@@ -23,7 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errmsg.h>
#include <errmsg.h>
#include <mysqld_error.h>
#include <limits.h>
#include <ctype.h>
@@ -96,191 +96,253 @@ Close the connection to the database
Database::~Database()
{
}
void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
char *S1 = new char[strlen(from) * 2 + 1];
char *S2 = new char[strlen(to) * 2 + 1];
char *S3 = new char[strlen(message) * 2 + 1];
DoEscapeString(S1, from, strlen(from));
DoEscapeString(S2, to, strlen(to));
DoEscapeString(S3, message, strlen(message));
char *escapedFrom = new char[strlen(from) * 2 + 1];
char *escapedTo = new char[strlen(to) * 2 + 1];
char *escapedMessage = new char[strlen(message) * 2 + 1];
DoEscapeString(escapedFrom, from, strlen(from));
DoEscapeString(escapedTo, to, strlen(to));
DoEscapeString(escapedMessage, message, strlen(message));
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_speech` SET `from`='%s', `to`='%s', `message`='%s', `minstatus`='%i', `guilddbid`='%i', `type`='%i'", S1, S2, S3, minstatus, guilddbid, type), errbuf, 0, 0)) {
_log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
std::string query = StringFormat("INSERT INTO `qs_player_speech` "
"SET `from` = '%s', `to` = '%s', `message`='%s', "
"`minstatus`='%i', `guilddbid`='%i', `type`='%i'",
escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type);
safe_delete_array(escapedFrom);
safe_delete_array(escapedTo);
safe_delete_array(escapedMessage);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
safe_delete_array(query);
safe_delete_array(S1);
safe_delete_array(S2);
safe_delete_array(S3);
}
void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount) {
void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record` SET `time`=NOW(), "
"`char1_id`='%i', `char1_pp`='%i', `char1_gp`='%i', `char1_sp`='%i', `char1_cp`='%i', `char1_items`='%i', "
"`char2_id`='%i', `char2_pp`='%i', `char2_gp`='%i', `char2_sp`='%i', `char2_cp`='%i', `char2_items`='%i'",
QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, QS->char1_money.silver, QS->char1_money.copper, QS->char1_count,
QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, QS->char2_money.silver, QS->char2_money.copper, QS->char2_count),
errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), "
"`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', "
"`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', "
"`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', "
"`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'",
QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold,
QS->char1_money.silver, QS->char1_money.copper, QS->char1_count,
QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold,
QS->char2_money.silver, QS->char2_money.copper, QS->char2_count);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(DetailCount > 0) {
for(int i = 0; i < DetailCount; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record_entries` SET `event_id`='%i', "
"`from_id`='%i', `from_slot`='%i', `to_id`='%i', `to_slot`='%i', `item_id`='%i', "
"`charges`='%i', `aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
lastid, QS->items[i].from_id, QS->items[i].from_slot, QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id,
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
if(detailCount == 0)
return;
int lastIndex = results.LastInsertedID();
for(int i = 0; i < detailCount; i++) {
query = StringFormat("INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', "
"`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', "
"`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', "
"`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].from_id, QS->items[i].from_slot,
QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id,
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5);
results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 detailCount) {
std::string query = StringFormat("INSERT INTO `qs_player_handin_record` SET `time` = NOW(), "
"`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', "
"`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', "
"`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', "
"`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', "
"`npc_items`='%i'",
QS->quest_id, QS->char_id, QS->char_money.platinum,
QS->char_money.gold, QS->char_money.silver, QS->char_money.copper,
QS->char_count, QS->npc_id, QS->npc_money.platinum,
QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper,
QS->npc_count);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(detailCount == 0)
return;
int lastIndex = results.LastInsertedID();
for(int i = 0; i < detailCount; i++) {
query = StringFormat("INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', "
"`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', "
"`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', "
"`aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].action_type, QS->items[i].char_slot,
QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
QS->items[i].aug_5);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 members){
std::string query = StringFormat("INSERT INTO `qs_player_npc_kill_record` "
"SET `npc_id` = '%i', `type` = '%i', "
"`zone_id` = '%i', `time` = NOW()",
QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(members == 0)
return;
int lastIndex = results.LastInsertedID();
for (int i = 0; i < members; i++) {
query = StringFormat("INSERT INTO `qs_player_npc_kill_record_entries` "
"SET `event_id` = '%i', `char_id` = '%i'",
lastIndex, QS->Chars[i].char_id);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record` SET `time`=NOW(), `quest_id`='%i', "
"`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i', "
"`npc_id`='%i', `npc_pp`='%i', `npc_gp`='%i', `npc_sp`='%i', `npc_cp`='%i', `npc_items`='%i'",
QS->quest_id, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count,
QS->npc_id, QS->npc_money.platinum, QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, QS->npc_count),
errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 items) {
std::string query = StringFormat("INSERT INTO `qs_player_delete_record` SET `time` = NOW(), "
"`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'",
QS->char_id, QS->stack_size, QS->char_count, QS->char_count);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(DetailCount > 0) {
for(int i = 0; i < DetailCount; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record_entries` SET `event_id`='%i', "
"`action_type`='%s', `char_slot`='%i', `item_id`='%i', `charges`='%i', "
"`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
lastid, QS->items[i].action_type, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
}
}
if(items == 0)
return;
int lastIndex = results.LastInsertedID();
for(int i = 0; i < items; i++) {
query = StringFormat("INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', "
"`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', "
"`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
QS->items[i].aug_5);
results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members){
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record` SET `npc_id`='%i', `type`='%i', `zone_id`='%i', `time`=NOW()", QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID), errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
if(Members > 0){
for (int i = 0; i < Members; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record_entries` SET `event_id`='%i', `char_id`='%i'", lastid, QS->Chars[i].char_id, errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
}
}
}
void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record` SET `time`=NOW(), "
"`char_id`='%i', `stack_size`='%i', `char_items`='%i'",
QS->char_id, QS->stack_size, QS->char_count, QS->char_count),
errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
if(Items > 0) {
for(int i = 0; i < Items; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record_entries` SET `event_id`='%i', "
"`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', "
"`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
}
}
}
void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) {
void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 items) {
/* These are item moves */
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record` SET `time`=NOW(), "
"`char_id`='%i', `from_slot`='%i', `to_slot`='%i', `stack_size`='%i', `char_items`='%i', `postaction`='%i'",
QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, QS->char_count, QS->postaction),
errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
if(Items > 0) {
for(int i = 0; i < Items; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record_entries` SET `event_id`='%i', "
"`from_slot`='%i', `to_slot`='%i', `item_id`='%i', `charges`='%i', "
"`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", lastid,
QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges,
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
}
std::string query = StringFormat("INSERT INTO `qs_player_move_record` SET `time` = NOW(), "
"`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', "
"`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'",
QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size,
QS->char_count, QS->postaction);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(items == 0)
return;
int lastIndex = results.LastInsertedID();
for(int i = 0; i < items; i++) {
query = StringFormat("INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', "
"`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', "
"`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id,
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5);
results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items) {
void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 items) {
/* Merchant transactions are from the perspective of the merchant, not the player -U */
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record` SET `time`=NOW(), "
"`zone_id`='%i', `merchant_id`='%i', `merchant_pp`='%i', `merchant_gp`='%i', `merchant_sp`='%i', `merchant_cp`='%i', `merchant_items`='%i', "
"`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i'",
QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, QS->merchant_money.gold, QS->merchant_money.silver, QS->merchant_money.copper, QS->merchant_count,
QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count),
errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
std::string query = StringFormat("INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), "
"`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', "
"`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', "
"`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', "
"`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', "
"`char_items` = '%i'",
QS->zone_id, QS->merchant_id, QS->merchant_money.platinum,
QS->merchant_money.gold, QS->merchant_money.silver,
QS->merchant_money.copper, QS->merchant_count, QS->char_id,
QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver,
QS->char_money.copper, QS->char_count);
auto results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
if(Items > 0) {
for(int i = 0; i < Items; i++) {
if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id`='%i', "
"`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', "
"`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'",
lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1,
QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5,
errbuf, 0, 0))) {
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
}
}
}
safe_delete_array(query);
if(items == 0)
return;
int lastIndex = results.LastInsertedID();
for(int i = 0; i < items; i++) {
query = StringFormat("INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', "
"`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', "
"`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges,
QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4,
QS->items[i].aug_5);
results = QueryDatabase(query);
if(!results.Success()) {
_log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", results.ErrorMessage().c_str());
_log(QUERYSERV__ERROR, "%s", query.c_str());
}
}
}
void Database::GeneralQueryReceive(ServerPacket *pack) {
@@ -293,11 +355,11 @@ void Database::GeneralQueryReceive(ServerPacket *pack) {
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
uint32 lastid = 0;
if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) {
if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) {
_log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf);
_log(QUERYSERV__ERROR, "%s", query);
_log(QUERYSERV__ERROR, "%s", query);
}
safe_delete_array(query);
safe_delete(pack);
safe_delete(Query);
safe_delete(pack);
safe_delete(Query);
}
@@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:TellQueueSize', '20', 'Maximum tell queue size.');
@@ -0,0 +1,3 @@
ALTER TABLE `npc_types` ADD `attack_delay` TINYINT(3) UNSIGNED DEFAULT '30' NOT NULL AFTER `attack_speed`;
UPDATE `npc_types` SET `attack_delay` = 36 + 36 * (`attack_speed` / 100);
UPDATE `npc_types` SET `attack_delay` = 30 WHERE `attack_speed` = 0;
@@ -0,0 +1 @@
DROP TABLE `tellque`;
+13
View File
@@ -1446,6 +1446,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
SetRacialLanguages( &pp ); // bUsh
SetRaceStartingSkills( &pp ); // bUsh
SetClassStartingSkills( &pp ); // bUsh
SetClassLanguages(&pp);
pp.skills[SkillSenseHeading] = 200;
// Some one fucking fix this to use a field name. -Doodman
//pp.unknown3596[28] = 15; // @bp: This is to enable disc usage
@@ -2034,3 +2035,15 @@ void Client::SetRacialLanguages( PlayerProfile_Struct *pp )
}
}
void Client::SetClassLanguages(PlayerProfile_Struct *pp)
{
// we only need to handle one class, but custom server might want to do more
switch(pp->class_) {
case ROGUE:
pp->languages[LANG_THIEVES_CANT] = 100;
break;
default:
break;
}
}
+1
View File
@@ -90,6 +90,7 @@ private:
void SetClassStartingSkills( PlayerProfile_Struct *pp );
void SetRaceStartingSkills( PlayerProfile_Struct *pp );
void SetRacialLanguages( PlayerProfile_Struct *pp );
void SetClassLanguages(PlayerProfile_Struct *pp);
ClientListEntry* cle;
Timer CLE_keepalive_timer;
+20
View File
@@ -93,6 +93,7 @@ ClientListEntry::~ClientListEntry() {
Camp(); // updates zoneserver's numplayers
client_list.RemoveCLEReferances(this);
}
tell_queue.clear();
}
void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) {
@@ -233,6 +234,7 @@ void ClientListEntry::ClearVars(bool iAll) {
pLFG = 0;
gm = 0;
pClientVersion = 0;
tell_queue.clear();
}
void ClientListEntry::Camp(ZoneServer* iZS) {
@@ -295,3 +297,21 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) {
return false;
}
void ClientListEntry::ProcessTellQueue()
{
if (!Server())
return;
ServerPacket *pack;
auto it = tell_queue.begin();
while (it != tell_queue.end()) {
pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1);
memcpy(pack->pBuffer, *it, pack->size);
pack->Deflate();
Server()->SendPacket(pack);
safe_delete(pack);
it = tell_queue.erase(it);
}
return;
}
+10
View File
@@ -5,6 +5,8 @@
#include "../common/md5.h"
//#include "../common/eq_packet_structs.h"
#include "../common/servertalk.h"
#include "../common/rulesys.h"
#include <vector>
#define CLE_Status_Never -1
@@ -80,6 +82,11 @@ public:
inline const char* GetLFGComments() const { return pLFGComments; }
inline uint8 GetClientVersion() { return pClientVersion; }
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
void ProcessTellQueue();
private:
void ClearVars(bool iAll = false);
@@ -120,6 +127,9 @@ private:
uint8 pLFGToLevel;
bool pLFGMatchFilter;
char pLFGComments[64];
// Tell Queue -- really a vector :D
std::vector<ServerChannelMessage_Struct *> tell_queue;
};
#endif /*CLIENTENTRY_H_*/
+48 -35
View File
@@ -437,45 +437,48 @@ bool ZoneServer::Process() {
Console* con = 0;
con = console_list.FindByAccountName(&scm->deliverto[1]);
if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply))
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to);
break;
}
ClientListEntry* cle = client_list.FindCharacter(scm->deliverto);
if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
if (!scm->noreply)
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
}
else if (cle->Online() == CLE_Status_Zoning) {
if (!scm->noreply)
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
char *telldate=asctime(timeinfo);
std::string query = StringFormat("SELECT name FROM character_ WHERE name = '%s'",scm->deliverto);
auto results = database.QueryDatabase(query);
if (!results.Success())
break;
if (results.RowCount() == 0) {
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
break;
}
query = StringFormat("INSERT INTO tellque "
"(Date, Receiver, Sender, Message) "
"VALUES('%s', '%s', '%s', '%s')",
telldate, scm->deliverto, scm->from, scm->message);
results = database.QueryDatabase(query);
if (results.Success())
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to);
else
zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
if (cle == 0 || cle->Online() < CLE_Status_Zoning ||
(cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
if (!scm->noreply) {
ClientListEntry* sender = client_list.FindCharacter(scm->from);
if (!sender)
break;
scm->noreply = true;
scm->queued = 3; // offline
strcpy(scm->deliverto, scm->from);
// ideally this would be trimming off the message too, oh well
sender->Server()->SendPacket(pack);
}
} else if (cle->Online() == CLE_Status_Zoning) {
if (!scm->noreply) {
ClientListEntry* sender = client_list.FindCharacter(scm->from);
if (cle->TellQueueFull()) {
if (!sender)
break;
scm->noreply = true;
scm->queued = 2; // queue full
strcpy(scm->deliverto, scm->from);
sender->Server()->SendPacket(pack);
} else {
size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1;
ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size];
memset(temp, 0, struct_size); // just in case, was seeing some corrupt messages, but it shouldn't happen
memcpy(temp, scm, struct_size);
temp->noreply = true;
cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor
if (!sender)
break;
scm->noreply = true;
scm->queued = 1; // queued
strcpy(scm->deliverto, scm->from);
sender->Server()->SendPacket(pack);
}
}
// zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
}
else if (cle->Server() == 0) {
if (!scm->noreply)
@@ -1319,6 +1322,16 @@ bool ZoneServer::Process() {
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_RequestTellQueue:
{
ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer;
ClientListEntry *cle = client_list.FindCharacter(rtq->name);
if (!cle || cle->TellQueueEmpty())
break;
cle->ProcessTellQueue();
break;
}
default:
{
zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size);
+144 -2
View File
@@ -3516,7 +3516,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
if(spell_id == SPELL_UNKNOWN) {
damage = ReduceDamage(damage);
mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage);
ReduceAllDamage(damage);
damage = ReduceAllDamage(damage);
TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker);
} else {
int32 origdmg = damage;
@@ -3529,7 +3529,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
//Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes.
Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName());
}
ReduceAllDamage(damage);
damage = ReduceAllDamage(damage);
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
}
@@ -4803,3 +4803,145 @@ void Mob::CommonBreakInvisible()
hidden = false;
improved_hidden = false;
}
void Mob::SetAttackTimer()
{
attack_timer.SetAtTrigger(4000, true);
}
void Client::SetAttackTimer()
{
float PermaHaste = GetPermaHaste();
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
const Item_Struct *PrimaryWeapon = nullptr;
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if (i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
const Item_Struct *ItemToUse = nullptr;
//find our item
ItemInst *ci = GetInv().GetItem(i);
if (ci)
ItemToUse = ci->GetItem();
//special offhand stuff
if (i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if (PrimaryWeapon != nullptr) {
if (PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
attack_dw_timer.Disable();
continue;
}
}
//see if we have a valid weapon
if (ItemToUse != nullptr) {
//check type and damage/delay
if (ItemToUse->ItemClass != ItemClassCommon
|| ItemToUse->Damage == 0
|| ItemToUse->Delay == 0) {
//no weapon
ItemToUse = nullptr;
}
// Check to see if skill is valid
else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) &&
(ItemToUse->ItemType != ItemTypeMartial) &&
(ItemToUse->ItemType != ItemType2HPiercing)) {
//no weapon
ItemToUse = nullptr;
}
}
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
int speed = 0;
//if we have no weapon..
if (ItemToUse == nullptr) {
//above checks ensure ranged weapons do not fall into here
// Work out if we're a monk
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
speed = static_cast<int>((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste);
else
speed = static_cast<int>((36 * (100 + DelayMod) / 100) * PermaHaste);
} else {
//we have a weapon, use its delay
// Convert weapon delay to timer resolution (milliseconds)
//delay * 100
speed = static_cast<int>((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste);
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) {
float quiver_haste = GetQuiverHaste();
if (quiver_haste > 0)
speed *= quiver_haste;
}
}
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
if (i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
void NPC::SetAttackTimer()
{
float PermaHaste = GetPermaHaste();
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if (i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
//special offhand stuff
if (i == MainSecondary) {
//NPCs get it for free at 13
if(GetLevel() < 13) {
attack_dw_timer.Disable();
continue;
}
}
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
// Technically NPCs should do some logic for weapons, but the effect is minimal
// What they do is take the lower of their set delay and the weapon's
// ex. Mob's delay set to 20, weapon set to 19, delay 19
// Mob's delay set to 20, weapon set to 21, delay 20
int speed = static_cast<int>((attack_delay * (100 + DelayMod) / 100) * PermaHaste);
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
}
}
+4 -8
View File
@@ -8350,12 +8350,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
return;
float HasteModifier = 0;
if(GetHaste() >= 0){
if (GetHaste())
HasteModifier = 10000 / (100 + GetHaste());
}
else {
HasteModifier = (100 - GetHaste());
}
else
HasteModifier = 100;
int32 dmg = 0;
uint16 skill_to_use = -1;
@@ -8981,10 +8979,8 @@ int32 Bot::CalcMaxMana() {
void Bot::SetAttackTimer() {
float PermaHaste;
if(GetHaste() > 0)
if (GetHaste())
PermaHaste = 1 / (1 + (float)GetHaste()/100);
else if(GetHaste() < 0)
PermaHaste = 1 * (1 - (float)GetHaste()/100);
else
PermaHaste = 1.0f;
+51 -43
View File
@@ -3134,6 +3134,14 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil
safe_delete(outapp);
}
void Client::Tell_StringID(uint32 string_id, const char *who, const char *message)
{
char string_id_str[10];
snprintf(string_id_str, 10, "%d", string_id);
Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message);
}
void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color;
new_color.color = color;
@@ -4002,54 +4010,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
void Client::KeyRingLoad()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id);
if (database.RunQuery(query, strlen(query), errbuf, &result))
{
safe_delete_array(query);
while(0 != (row = mysql_fetch_row(result))){
keyring.push_back(atoi(row[0]));
}
mysql_free_result(result);
}else {
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
std::string query = StringFormat("SELECT item_id FROM keyring "
"WHERE char_id = '%i' ORDER BY item_id", character_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl;
return;
}
}
for (auto row = results.begin(); row != results.end(); ++row)
keyring.push_back(atoi(row[0]));
}
void Client::KeyRingAdd(uint32 item_id)
{
if(0==item_id)return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
query = new char[256];
bool bFound = KeyRingCheck(item_id);
if(!bFound){
sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id);
if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) {
Message(4,"Added to keyring.");
if(0==item_id)
return;
/* QS: PlayerLogKeyringAddition */
if (RuleB(QueryServ, PlayerLogKeyringAddition)){
std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID());
QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc);
}
safe_delete_array(query);
}
else {
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return;
}
keyring.push_back(item_id);
}
bool found = KeyRingCheck(item_id);
if (found)
return;
std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl;
return;
}
Message(4,"Added to keyring.");
keyring.push_back(item_id);
}
bool Client::KeyRingCheck(uint32 item_id)
@@ -7978,7 +7970,7 @@ void Client::ItemTimerCheck()
TryItemTimer(i);
}
for(i = EmuConstants::GENERAL_BAGS_BEGIN; i <= MainCursor; i++)
for(i = EmuConstants::GENERAL_BEGIN; i <= MainCursor; i++)
{
TryItemTimer(i);
}
@@ -8346,3 +8338,19 @@ void Client::ShowNumHits()
return;
}
float Client::GetQuiverHaste()
{
float quiver_haste = 0;
for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) {
const ItemInst *pi = GetInv().GetItem(r);
if (!pi)
continue;
if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) {
float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv));
quiver_haste = std::max(temp_wr, quiver_haste);
}
}
if (quiver_haste > 0)
quiver_haste = 1.0f / (1.0f + static_cast<float>(quiver_haste) / 100.0f);
return quiver_haste;
}
+6 -3
View File
@@ -222,6 +222,8 @@ public:
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer();
float GetQuiverHaste();
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@@ -258,6 +260,7 @@ public:
const char *message5 = nullptr, const char *message6 = nullptr,
const char *message7 = nullptr, const char *message8 = nullptr,
const char *message9 = nullptr);
void Tell_StringID(uint32 string_id, const char *who, const char *message);
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
void SendTraderItem(uint32 item_id,uint16 quantity);
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
@@ -1194,9 +1197,9 @@ public:
int32 mod_client_xp(int32 in_exp, NPC *npc);
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
int mod_client_haste_cap(int cap);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
+13 -5
View File
@@ -6307,12 +6307,12 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
tobe_auged = user_inv.GetItem(slot_id);
auged_with = user_inv.GetItem(MainCursor);
if(tobe_auged && auged_with)
if (tobe_auged && auged_with)
{
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) &&
if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
{
tobe_auged->PutAugment(slot, *auged_with);
tobe_auged->PutAugment(in_augment->augment_index, *auged_with);
ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index);
if(aug) {
@@ -6325,7 +6325,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
}
else
{
Message(13, "Error: Could not find augmentation at index %i. Aborting.");
Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index);
return;
}
@@ -9574,11 +9574,17 @@ void Client::CompleteConnect() {
//reapply some buffs
uint32 buff_count = GetMaxTotalSlots();
for (uint32 j1 = 0; j1 < buff_count; j1++) {
if (buffs[j1].spellid >(uint32)SPDAT_RECORDS)
if (!IsValidSpell(buffs[j1].spellid))
continue;
const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid];
int NimbusEffect = GetNimbusEffect(buffs[j1].spellid);
if(NimbusEffect) {
if(!IsNimbusEffectActive(NimbusEffect))
SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true);
}
for (int x1 = 0; x1 < EFFECT_COUNT; x1++) {
switch (spell.effectid[x1]) {
case SE_IllusionCopy:
@@ -9828,6 +9834,8 @@ void Client::CompleteConnect() {
}
entity_list.RefreshClientXTargets(this);
worldserver.RequestTellQueue(GetName());
}
void Client::Handle_OP_KeyRing(const EQApplicationPacket *app)
-2
View File
@@ -836,8 +836,6 @@ void Client::BulkSendInventoryItems() {
}
}
// Where are cursor buffer items processed? They need to be validated as well... -U
bool deletenorent = database.NoRentExpired(GetName());
if(deletenorent){ RemoveNoRent(false); } //client was offline for more than 30 minutes, delete no rent items
+11 -50
View File
@@ -333,8 +333,6 @@ int command_init(void) {
command_add("guilds",nullptr,0,command_guild) ||
command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) ||
command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) ||
command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) ||
command_add("viewmessages",nullptr,0,command_viewmessage) ||
command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) ||
command_add("randomfeatures","- Temporarily randomizes the Facial Features of your target",80,command_randomfeatures) ||
command_add("rf",nullptr,80,command_randomfeatures) ||
@@ -5294,53 +5292,6 @@ void command_manaburn(Client *c, const Seperator *sep)
}
}
void command_viewmessage(Client *c, const Seperator *sep)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
if(sep->arg[1][0]==0)
{
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where receiver='%s'",c->GetName()), errbuf, &result))
{
if (mysql_num_rows(result)>0)
{
c->Message(0,"You have messages waiting for you to view.");
c->Message(0,"Type #Viewmessage <Message ID> to view the message.");
c->Message(0," ID , Message Sent Date, Message Sender");
while ((row = mysql_fetch_row(result)))
c->Message(0,"ID: %s Sent Date: %s Sender: %s ",row[0],row[1],row[3]);
}
else
c->Message(0,"You have no new messages");
mysql_free_result(result);
}
safe_delete_array(query);
}
else
{
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where id=%s",sep->argplus[1]), errbuf, &result))
{
if (mysql_num_rows(result)==1)
{
row = mysql_fetch_row(result);
mysql_free_result(result);
if (strcasecmp((const char *) c->GetName(), (const char *) row[2]) == 0)
{
c->Message(15,"ID: %s,Sent Date: %s,Sender: %s,Message: %s",row[0],row[1],row[3],row[4]);
database.RunQuery(query, MakeAnyLenString(&query, "Delete from tellque where id=%s",row[0]), errbuf);
}
else
c->Message(13,"Invalid Message Number, check the number and try again.");
}
else
c->Message(13,"Invalid Message Number, check the number and try again.");
}
safe_delete_array(query);
}
}
void command_doanim(Client *c, const Seperator *sep)
{
if (!sep->IsNumber(1))
@@ -6658,6 +6609,7 @@ void command_npcedit(Client *c, const Seperator *sep)
c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag");
c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter");
c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier");
c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay");
c->Message(0, "#npcedit findable - Sets an NPC's findable flag");
c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model");
c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model");
@@ -6768,7 +6720,7 @@ void command_npcedit(Client *c, const Seperator *sep)
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
c->Message(15,"NPCID %u now regens %i hitpoints per tick.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp_regen_rate=%i where hp_regen_rate=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp_regen_rate=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
c->LogSQL(query);
safe_delete_array(query);
}
@@ -7159,6 +7111,15 @@ void command_npcedit(Client *c, const Seperator *sep)
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "findable" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];
-1
View File
@@ -217,7 +217,6 @@ void command_guild(Client *c, const Seperator *sep);
bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
void command_viewmessage(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
void command_face(Client *c, const Seperator *sep);
+10 -6
View File
@@ -356,9 +356,11 @@ void Client::GoFish()
inst = m_inv.GetItem(MainCursor);
}
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
if(inst) {
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args);
}
}
}
else
@@ -470,9 +472,11 @@ void Client::ForageItem(bool guarantee) {
inst = m_inv.GetItem(MainCursor);
}
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
if(inst) {
std::vector<EQEmu::Any> args;
args.push_back(inst);
parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args);
}
}
int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems;
+12
View File
@@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender)
}
}
/* This may seem pointless but the case above does not cover the following situation:
* Group has Leader a, member b, member c
* b and c are out of zone
* a disconnects/quits
* b or c zone back in and disconnects/quits
* a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b)
* Ultimately we should think up a better solution to this.
*/
if(oldmember == GetLeader()) {
SetLeader(nullptr);
}
ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
gl->gid = GetID();
+22 -43
View File
@@ -165,7 +165,7 @@ void Client::SendGuildSpawnAppearance() {
switch (rank) {
case 0: { rank = 5; break; } // GUILD_MEMBER 0
case 1: { rank = 3; break; } // GUILD_OFFICER 1
case 2: { rank = 1; break; } // GUILD_LEADER 2
case 2: { rank = 1; break; } // GUILD_LEADER 2
default: { break; } // GUILD_NONE
}
}
@@ -417,57 +417,36 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank,
}*/
bool ZoneDatabase::CheckGuildDoor(uint8 doorid,uint16 guild_id,const char* zone) {
MYSQL_ROW row;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
if (!RunQuery(query, MakeAnyLenString(&query,
"SELECT guild FROM doors where doorid=%i AND zone='%s'",
doorid-128, zone), errbuf, &result))
{
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query, errbuf);
safe_delete_array(query);
return false;
} else {
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
if (atoi(row[0]) == guild_id)
{
mysql_free_result(result);
return true;
}
else
{
mysql_free_result(result);
return false;
}
bool ZoneDatabase::CheckGuildDoor(uint8 doorid, uint16 guild_id, const char* zone) {
// code below will never be reached
mysql_free_result(result);
return false;
}
std::string query = StringFormat("SELECT guild FROM doors WHERE doorid = %i AND zone = '%s'",
doorid-128, zone);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
return false;
if (results.RowCount() != 1)
return false;
auto row = results.begin();
return atoi(row[0]) == guild_id;
}
bool ZoneDatabase::SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
if (doorid > 127)
doorid = doorid - 128;
if (!RunQuery(query, MakeAnyLenString(&query,
"UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
guild_id, doorid, zone), errbuf, 0,&affected_rows))
{
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",
guild_id, doorid, zone);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
return(affected_rows > 0);
return (results.RowsAffected() > 0);
}
+7 -15
View File
@@ -903,24 +903,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32
return false;
}
const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')";
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf))
{
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf);
safe_delete_array(query);
std::string query = StringFormat("INSERT INTO `guild_bank` "
"(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) "
"VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')",
GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
_log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
const Item_Struct *Item = database.GetItem(ItemID);
GuildBankItemUpdate_Struct gbius;
+122 -23
View File
@@ -2002,11 +2002,10 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) {
}
void Client::RemoveNoRent(bool client_update) {
int16 slot_id = 0;
int16 slot_id;
// personal
for(slot_id = MAIN_BEGIN; slot_id < EmuConstants::MAP_POSSESSIONS_SIZE; slot_id++) {
// equipment
for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) {
const ItemInst* inst = m_inv[slot_id];
if(inst && !inst->GetItem()->NoRent) {
mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id);
@@ -2014,11 +2013,22 @@ void Client::RemoveNoRent(bool client_update) {
}
}
// general
for (slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; slot_id++) {
const ItemInst* inst = m_inv[slot_id];
if (inst && !inst->GetItem()->NoRent) {
mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id);
DeleteItemInInventory(slot_id, 0, client_update);
}
}
// power source
const ItemInst* inst = m_inv[MainPowerSource];
if(inst && !inst->GetItem()->NoRent) {
mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, MainPowerSource);
DeleteItemInInventory(MainPowerSource, 0, (GetClientVersion() >= EQClientSoF) ? client_update : false); // Ti slot non-existent
if (m_inv[MainPowerSource]) {
const ItemInst* inst = m_inv[MainPowerSource];
if (inst && !inst->GetItem()->NoRent) {
mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, MainPowerSource);
DeleteItemInInventory(MainPowerSource, 0, (GetClientVersion() >= EQClientSoF) ? client_update : false); // Ti slot non-existent
}
}
// containers
@@ -2065,15 +2075,42 @@ void Client::RemoveNoRent(bool client_update) {
DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank Container slots
}
}
// cursor & limbo
if (!m_inv.CursorEmpty()) {
std::list<ItemInst*> local;
ItemInst* inst = nullptr;
while (!m_inv.CursorEmpty()) {
inst = m_inv.PopItem(MainCursor);
if (inst)
local.push_back(inst);
}
std::list<ItemInst*>::iterator iter = local.begin();
while (iter != local.end()) {
inst = *iter;
if (!inst->GetItem()->NoRent)
mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from `Limbo`", inst->GetItem()->Name);
else
m_inv.PushCursor(**iter);
safe_delete(*iter);
iter = local.erase(iter);
}
std::list<ItemInst*>::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end();
database.SaveCursor(this->CharacterID(), s, e);
local.clear();
}
}
// Two new methods to alleviate perpetual login desyncs
void Client::RemoveDuplicateLore(bool client_update) {
// Split-charge stacking may be added at some point -U
int16 slot_id;
int16 slot_id = 0;
// personal
for(slot_id = MAIN_BEGIN; slot_id < EmuConstants::MAP_POSSESSIONS_SIZE; slot_id++) {
// equipment
for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) {
ItemInst* inst = m_inv.PopItem(slot_id);
if(inst) {
if(CheckLoreConflict(inst->GetItem())) {
@@ -2087,17 +2124,34 @@ void Client::RemoveDuplicateLore(bool client_update) {
}
}
// general
for (slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; slot_id++) {
ItemInst* inst = m_inv.PopItem(slot_id);
if (inst) {
if (CheckLoreConflict(inst->GetItem())) {
mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, slot_id);
}
else {
m_inv.PutItem(slot_id, *inst);
}
safe_delete(inst);
}
}
// power source
ItemInst* inst = m_inv.PopItem(MainPowerSource);
if(inst) {
if(CheckLoreConflict(inst->GetItem())) {
mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, MainPowerSource);
if (m_inv[MainPowerSource]) {
ItemInst* inst = m_inv.PopItem(MainPowerSource);
if (inst) {
if (CheckLoreConflict(inst->GetItem())) {
mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id);
database.SaveInventory(character_id, nullptr, MainPowerSource);
}
else {
m_inv.PutItem(MainPowerSource, *inst);
}
safe_delete(inst);
}
else {
m_inv.PutItem(MainPowerSource, *inst);
}
safe_delete(inst);
}
// containers
@@ -2146,11 +2200,56 @@ void Client::RemoveDuplicateLore(bool client_update) {
}
// Shared Bank and Shared Bank Containers are not checked due to their allowing duplicate lore items -U
// cursor & limbo
if (!m_inv.CursorEmpty()) {
std::list<ItemInst*> local;
ItemInst* inst = nullptr;
while (!m_inv.CursorEmpty()) {
inst = m_inv.PopItem(MainCursor);
if (inst)
local.push_back(inst);
}
std::list<ItemInst*>::iterator iter = local.begin();
while (iter != local.end()) {
inst = *iter;
if (CheckLoreConflict(inst->GetItem())) {
mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name);
safe_delete(*iter);
iter = local.erase(iter);
}
else {
++iter;
}
}
iter = local.begin();
while (iter != local.end()) {
inst = *iter;
if (!inst->GetItem()->LoreFlag ||
((inst->GetItem()->LoreGroup == -1) && (m_inv.HasItem(inst->GetID(), 0, invWhereCursor) == INVALID_INDEX)) ||
(inst->GetItem()->LoreGroup && ~inst->GetItem()->LoreGroup && (m_inv.HasItemByLoreGroup(inst->GetItem()->LoreGroup, invWhereCursor) == INVALID_INDEX))) {
m_inv.PushCursor(**iter);
}
else {
mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name);
}
safe_delete(*iter);
iter = local.erase(iter);
}
std::list<ItemInst*>::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end();
database.SaveCursor(this->CharacterID(), s, e);
local.clear();
}
}
void Client::MoveSlotNotAllowed(bool client_update) {
int16 slot_id;
int16 slot_id = 0;
// equipment
for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) {
+14 -157
View File
@@ -174,8 +174,9 @@ Mob::Mob(const char* in_name,
drakkin_heritage = in_drakkin_heritage;
drakkin_tattoo = in_drakkin_tattoo;
drakkin_details = in_drakkin_details;
attack_speed= 0;
slow_mitigation= 0;
attack_speed = 0;
attack_delay = 0;
slow_mitigation = 0;
findable = false;
trackable = true;
has_shieldequiped = false;
@@ -1460,7 +1461,7 @@ void Mob::SendIllusionPacket(uint16 in_race, uint8 in_gender, uint8 in_texture,
else
this->drakkin_details = in_drakkin_details;
if (in_size == 0xFFFFFFFF)
if (in_size <= 0.0f)
this->size = GetSize();
else
this->size = in_size;
@@ -1939,159 +1940,6 @@ void Mob::Kill() {
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
}
void Mob::SetAttackTimer() {
float PermaHaste;
if(GetHaste() > 0)
PermaHaste = 1 / (1 + (float)GetHaste()/100);
else if(GetHaste() < 0)
PermaHaste = 1 * (1 - (float)GetHaste()/100);
else
PermaHaste = 1.0f;
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer* TimerToUse = nullptr;
const Item_Struct* PrimaryWeapon = nullptr;
for (int i=MainRange; i<=MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if(i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
const Item_Struct* ItemToUse = nullptr;
//find our item
if (IsClient()) {
ItemInst* ci = CastToClient()->GetInv().GetItem(i);
if (ci)
ItemToUse = ci->GetItem();
} else if(IsNPC())
{
//The code before here was fundementally flawed because equipment[]
//isn't the same as PC inventory and also:
//NPCs don't use weapon speed to dictate how fast they hit anyway.
ItemToUse = nullptr;
}
//special offhand stuff
if(i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if(PrimaryWeapon != nullptr) {
if( PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//clients must have the skill to use it...
if(IsClient()) {
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
attack_dw_timer.Disable();
continue;
}
} else {
//NPCs get it for free at 13
if(GetLevel() < 13) {
attack_dw_timer.Disable();
continue;
}
}
}
//see if we have a valid weapon
if(ItemToUse != nullptr) {
//check type and damage/delay
if(ItemToUse->ItemClass != ItemClassCommon
|| ItemToUse->Damage == 0
|| ItemToUse->Delay == 0) {
//no weapon
ItemToUse = nullptr;
}
// Check to see if skill is valid
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
//no weapon
ItemToUse = nullptr;
}
}
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
if (DelayMod < -99)
DelayMod = -99;
//if we have no weapon..
if (ItemToUse == nullptr) {
//above checks ensure ranged weapons do not fall into here
// Work out if we're a monk
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
//we are a monk, use special delay
int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
// 1200 seemed too much, with delay 10 weapons available
if(speed < RuleI(Combat, MinHastedDelay)) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic
} else {
//not a monk... using fist, regular delay
int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36
}
} else {
//we have a weapon, use its delay
// Convert weapon delay to timer resolution (milliseconds)
//delay * 100
int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay))
speed = RuleI(Combat, MinHastedDelay);
if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing))
{
if(IsClient())
{
float max_quiver = 0;
for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++)
{
const ItemInst *pi = CastToClient()->GetInv().GetItem(r);
if(!pi)
continue;
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver)
{
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
if(temp_wr > max_quiver)
{
max_quiver = temp_wr;
}
}
}
if(max_quiver > 0)
{
float quiver_haste = 1 / (1 + max_quiver / 100);
speed *= quiver_haste;
}
}
}
TimerToUse->SetAtTrigger(speed, true);
}
if(i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
bool Mob::CanThisClassDualWield(void) const {
if(!IsClient()) {
return(GetSkill(SkillDualWield) > 0);
@@ -4527,6 +4375,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
void Mob::RemoveNimbusEffect(int effectid)
{
if (effectid == nimbus_effect1)
nimbus_effect1 = 0;
else if (effectid == nimbus_effect2)
nimbus_effect2 = 0;
else if (effectid == nimbus_effect3)
nimbus_effect3 = 0;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct));
RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer;
rne->spawnid = GetID();
@@ -5067,7 +4924,7 @@ void Mob::ClearSpecialAbilities() {
}
}
void Mob::ProcessSpecialAbilities(const std::string str) {
void Mob::ProcessSpecialAbilities(const std::string &str) {
ClearSpecialAbilities();
std::vector<std::string> sp = SplitString(str, '^');
+4 -2
View File
@@ -563,7 +563,7 @@ public:
uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF,
uint8 in_hairstyle = 0xFF, uint8 in_luclinface = 0xFF, uint8 in_beard = 0xFF, uint8 in_aa_title = 0xFF,
uint32 in_drakkin_heritage = 0xFFFFFFFF, uint32 in_drakkin_tattoo = 0xFFFFFFFF,
uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = 0xFFFFFFFF);
uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = -1.0f);
virtual void Stun(int duration);
virtual void UnStun();
inline void Silence(bool newval) { silenced = newval; }
@@ -684,6 +684,7 @@ public:
inline bool GetInvul(void) { return invulnerable; }
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
virtual int GetHaste();
inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast<float>(GetHaste()) / 100.0f) : 100.0f; }
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
uint16 GetDamageTable(SkillUseTypes skillinuse);
@@ -842,7 +843,7 @@ public:
void StopSpecialAbilityTimer(int ability);
Timer *GetSpecialAbilityTimer(int ability);
void ClearSpecialAbilities();
void ProcessSpecialAbilities(const std::string str);
void ProcessSpecialAbilities(const std::string &str);
Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade;
@@ -1052,6 +1053,7 @@ protected:
Timer attack_dw_timer;
Timer ranged_timer;
float attack_speed; //% increase/decrease in attack speed (not haste)
int8 attack_delay; //delay between attacks in 10ths of seconds
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
Timer tic_timer;
Timer mana_timer;
+3
View File
@@ -248,6 +248,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
delaytimer = false;
combat_event = false;
attack_speed = d->attack_speed;
attack_delay = d->attack_delay;
slow_mitigation = d->slow_mitigation;
EntityList::RemoveNumbers(name);
@@ -935,6 +936,8 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z,
npc_type->WIS = 150;
npc_type->CHA = 150;
npc_type->attack_delay = 30;
npc_type->prim_melee_type = 28;
npc_type->sec_melee_type = 28;
+8 -8
View File
@@ -134,7 +134,6 @@ public:
void CalcNPCRegen();
void CalcNPCDamage();
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;}
@@ -158,6 +157,7 @@ public:
virtual void InitializeBuffSlots();
virtual void UninitializeBuffSlots();
virtual void SetAttackTimer();
virtual void RangedAttack(Mob* other);
virtual void ThrowingAttack(Mob* other) { }
int32 GetNumberOfAttacks() const { return attack_count; }
@@ -388,16 +388,16 @@ public:
inline void SetHealScale(float amt) { healscale = amt; }
inline float GetHealScale() { return healscale; }
uint32 GetSpawnKillCount();
int GetScore();
void SetMerchantProbability(uint8 amt) { probability = amt; }
uint32 GetSpawnKillCount();
int GetScore();
void SetMerchantProbability(uint8 amt) { probability = amt; }
uint8 GetMerchantProbability() { return probability; }
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c);
void AISpellsList(Client *c);
bool IsRaidTarget() const { return raid_target; };
protected:
+1 -1
View File
@@ -7140,7 +7140,7 @@ XS(XS_Mob_SendIllusion)
uint32 drakkin_heritage = 0xFFFFFFFF;
uint32 drakkin_tattoo = 0xFFFFFFFF;
uint32 drakkin_details = 0xFFFFFFFF;
float size = 0xFFFFFFFF;
float size = -1.0f;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
+6 -7
View File
@@ -165,13 +165,12 @@ void QGlobalCache::LoadByGlobalContext()
LoadBy(query);
}
void QGlobalCache::LoadBy(const std::string query)
void QGlobalCache::LoadBy(const std::string &query)
{
auto results = database.QueryDatabase(query);
if (!results.Success())
return;
for (auto row = results.begin(); row != results.end(); ++row)
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF));
auto results = database.QueryDatabase(query);
if (!results.Success())
return;
for (auto row = results.begin(); row != results.end(); ++row)
AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF));
}
+1 -1
View File
@@ -42,7 +42,7 @@ public:
void LoadByZoneID(uint32 zoneID); //zone
void LoadByGlobalContext(); //zone
protected:
void LoadBy(const std::string query);
void LoadBy(const std::string &query);
std::list<QGlobal> qGlobalBucket;
};
+5 -7
View File
@@ -1413,10 +1413,8 @@ void NPC::DoClassAttacks(Mob *target) {
return;
float HasteModifier = 0;
if(GetHaste() > 0)
if (GetHaste())
HasteModifier = 10000 / (100 + GetHaste());
else if(GetHaste() < 0)
HasteModifier = (100 - GetHaste());
else
HasteModifier = 100;
@@ -1464,7 +1462,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = KickReuseTime * 1000;
reuse = (KickReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1484,7 +1482,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = BashReuseTime * 1000;
reuse = (BashReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1537,7 +1535,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = KickReuseTime * 1000;
reuse = (KickReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
@@ -1562,7 +1560,7 @@ void NPC::DoClassAttacks(Mob *target) {
}
}
reuse = BashReuseTime * 1000;
reuse = (BashReuseTime + 3) * 1000;
DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse);
did_attack = true;
}
+117 -11
View File
@@ -202,7 +202,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
effect_value = GetMaxHP();
if (GetSpellPowerDistanceMod())
effect_value = effect_value*(GetSpellPowerDistanceMod()/100);
effect_value = effect_value*GetSpellPowerDistanceMod()/100;
#ifdef SPELL_EFFECT_SPAM
effect_desc[0] = 0;
@@ -3127,26 +3127,42 @@ snare has both of them negative, yet their range should work the same:
case 110: // confirmed 2/6/04
//is there a reason we dont use updownsign here???
result = ubase + (caster_level / 5); break;
result = ubase + (caster_level / 6);
break;
case 111:
result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break;
result = updownsign * (ubase + 6 * (caster_level - 16));
break;
case 112:
result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break;
result = updownsign * (ubase + 8 * (caster_level - 24));
break;
case 113:
result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break;
result = updownsign * (ubase + 10 * (caster_level - 34));
break;
case 114:
result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break;
result = updownsign * (ubase + 15 * (caster_level - 44));
break;
//these formula were updated according to lucy 10/16/04
case 115: // this is only in symbol of transal
result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
result = ubase;
if (caster_level > 15)
result += 7 * (caster_level - 15);
break;
case 116: // this is only in symbol of ryltan
result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
result = ubase;
if (caster_level > 24)
result += 10 * (caster_level - 24);
break;
case 117: // this is only in symbol of pinzarn
result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
result = ubase;
if (caster_level > 34)
result += 13 * (caster_level - 34);
break;
case 118: // used in naltron and a few others
result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
result = ubase;
if (caster_level > 44)
result += 20 * (caster_level - 44);
break;
case 119: // confirmed 2/6/04
result = ubase + (caster_level / 8); break;
@@ -3166,6 +3182,93 @@ snare has both of them negative, yet their range should work the same:
result = MakeRandomInt(ubase, abs(max));
break;
case 124: // check sign
result = ubase;
if (caster_level > 50)
result += caster_level - 50;
break;
case 125: // check sign
result = ubase;
if (caster_level > 50)
result += 2 * (caster_level - 50);
break;
case 126: // check sign
result = ubase;
if (caster_level > 50)
result += 3 * (caster_level - 50);
break;
case 127: // check sign
result = ubase;
if (caster_level > 50)
result += 4 * (caster_level - 50);
break;
case 128: // check sign
result = ubase;
if (caster_level > 50)
result += 5 * (caster_level - 50);
break;
case 129: // check sign
result = ubase;
if (caster_level > 50)
result += 10 * (caster_level - 50);
break;
case 130: // check sign
result = ubase;
if (caster_level > 50)
result += 15 * (caster_level - 50);
break;
case 131: // check sign
result = ubase;
if (caster_level > 50)
result += 20 * (caster_level - 50);
break;
case 132: // check sign
result = ubase;
if (caster_level > 50)
result += 25 * (caster_level - 50);
break;
case 137: // used in berserker AA desperation
result = ubase - (ubase * (GetHPRatio() / 100.0f));
break;
case 138: { // unused on live?
int maxhps = GetMaxHP() / 2;
if (GetHP() <= maxhps)
result = -(ubase * GetHP() / maxhps);
else
result = -ubase;
break;
}
case 139: // check sign
result = ubase + (caster_level > 30 ? (caster_level - 30) / 2 : 0);
break;
case 140: // check sign
result = ubase + (caster_level > 30 ? caster_level - 30 : 0);
break;
case 141: // check sign
result = ubase + (caster_level > 30 ? (3 * caster_level - 90) / 2 : 0);
break;
case 142: // check sign
result = ubase + (caster_level > 30 ? 2 * caster_level - 60 : 0);
break;
case 143: // check sign
result = ubase + (3 * caster_level / 4);
break;
//these are used in stacking effects... formula unknown
case 201:
case 203:
@@ -4016,6 +4119,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if (!found_numhits)
Numhits(false);
}
if (spells[buffs[slot].spellid].NimbusEffect > 0)
RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect);
buffs[slot].spellid = SPELL_UNKNOWN;
if(IsPet() && GetOwner() && GetOwner()->IsClient()) {
+7 -9
View File
@@ -4501,13 +4501,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
else
{
resist_chance -= roll;
if(resist_chance < 1)
{
resist_chance = 1;
}
int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance);
int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance);
if(IsNPC())
{
@@ -4535,17 +4534,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
}
if(partial_modifier < 0)
if(partial_modifier <= 0)
{
return 100;
}
else if(partial_modifier >= 100)
{
return 0;
}
if(partial_modifier > 100)
{
return 100;
}
return partial_modifier;
return (100.0f - partial_modifier);
}
}
}
+4
View File
@@ -249,6 +249,8 @@
#define PLAYER_CHARMED 1461 //You lose control of yourself!
#define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished.
#define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction.
#define QUEUED_TELL 2458 //[queued]
#define QUEUE_TELL_FULL 2459 //[zoing and queue is full]
#define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...'
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
@@ -269,6 +271,8 @@
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away.
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3'
#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time.
#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!
+83 -133
View File
@@ -31,25 +31,17 @@ bool TitleManager::LoadTitles()
{
Titles.clear();
TitleEntry Title;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = nullptr;
MYSQL_RES *result;
MYSQL_ROW row;
if (!database.RunQuery(query, MakeAnyLenString(&query,
"SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, "
"`char_id`, `status`, `item_id`, `prefix`, `suffix`, `title_set` from titles"), errbuf, &result))
{
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query, errbuf);
safe_delete_array(query);
return(false);
std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, "
"`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, "
"`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles";
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
safe_delete_array(query);
while ((row = mysql_fetch_row(result))) {
for (auto row = results.begin(); row != results.end(); ++row) {
TitleEntry Title;
Title.TitleID = atoi(row[0]);
Title.SkillID = (SkillUseTypes) atoi(row[1]);
Title.MinSkillValue = atoi(row[2]);
@@ -66,9 +58,8 @@ bool TitleManager::LoadTitles()
Title.TitleSet = atoi(row[13]);
Titles.push_back(Title);
}
mysql_free_result(result);
return(true);
return true;
}
EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c)
@@ -244,92 +235,70 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue)
return false;
}
void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title)
void TitleManager::CreateNewPlayerTitle(Client *client, const char *title)
{
if(!c || !Title)
if(!client || !title)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = nullptr;
MYSQL_RES *result;
char *escTitle = new char[strlen(title) * 2 + 1];
char *EscTitle = new char[strlen(Title) * 2 + 1];
client->SetAATitle(title);
c->SetAATitle(Title);
database.DoEscapeString(EscTitle, Title, strlen(Title));
if (database.RunQuery(query, MakeAnyLenString(&query,
"SELECT `id` from titles where `prefix` = '%s' and char_id = %i", EscTitle, c->CharacterID()), errbuf, &result))
{
if(mysql_num_rows(result) > 0)
{
mysql_free_result(result);
safe_delete_array(query);
safe_delete_array(EscTitle);
return;
}
mysql_free_result(result);
database.DoEscapeString(escTitle, title, strlen(title));
auto query = StringFormat("SELECT `id` FROM titles "
"WHERE `prefix` = '%s' AND char_id = %i",
escTitle, client->CharacterID());
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0){
safe_delete_array(escTitle);
return;
}
safe_delete_array(query);
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `prefix`) VALUES(%i, '%s')",
c->CharacterID(), EscTitle), errbuf))
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query, errbuf);
else
{
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
safe_delete_array(query);
safe_delete_array(EscTitle);
query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')",
client->CharacterID(), escTitle);
safe_delete_array(escTitle);
results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix)
void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix)
{
if(!c || !Suffix)
if(!client || !suffix)
return;
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = nullptr;
MYSQL_RES *result;
client->SetTitleSuffix(suffix);
char *EscSuffix = new char[strlen(Suffix) * 2 + 1];
char *escSuffix = new char[strlen(suffix) * 2 + 1];
database.DoEscapeString(escSuffix, suffix, strlen(suffix));
c->SetTitleSuffix(Suffix);
database.DoEscapeString(EscSuffix, Suffix, strlen(Suffix));
if (database.RunQuery(query, MakeAnyLenString(&query,
"SELECT `id` from titles where `suffix` = '%s' and char_id = %i", EscSuffix, c->CharacterID()), errbuf, &result))
{
if(mysql_num_rows(result) > 0)
{
mysql_free_result(result);
safe_delete_array(query);
safe_delete_array(EscSuffix);
std::string query = StringFormat("SELECT `id` FROM titles "
"WHERE `suffix` = '%s' AND char_id = %i",
escSuffix, client->CharacterID());
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0) {
safe_delete_array(escSuffix);
return;
}
mysql_free_result(result);
}
}
safe_delete_array(query);
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `suffix`) VALUES(%i, '%s')",
c->CharacterID(), EscSuffix), errbuf))
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query, errbuf);
else
{
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
safe_delete_array(query);
safe_delete_array(EscSuffix);
query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')",
client->CharacterID(), escSuffix);
safe_delete_array(escSuffix);
results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Client::SetAATitle(const char *Title)
@@ -368,67 +337,48 @@ void Client::SetTitleSuffix(const char *Suffix)
safe_delete(outapp);
}
void Client::EnableTitle(int titleset) {
void Client::EnableTitle(int titleSet) {
if (CheckTitle(titleset)) {
if (CheckTitle(titleSet))
return;
}
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
std::string query = StringFormat("INSERT INTO player_titlesets "
"(char_id, title_set) VALUES (%i, %i)",
CharacterID(), titleSet);
auto results = database.QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID());
if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO player_titlesets (char_id, title_set) VALUES (%i, %i)", CharacterID(), titleset), errbuf)) {
LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleset, CharacterID());
safe_delete_array(query);
return;
}
else {
safe_delete_array(query);
return;
}
}
bool Client::CheckTitle(int titleset) {
bool Client::CheckTitle(int titleSet) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT `id` FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", titleset, CharacterID()), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) >= 1) {
mysql_free_result(result);
return(true);
}
mysql_free_result(result);
std::string query = StringFormat("SELECT `id` FROM player_titlesets "
"WHERE `title_set`=%i AND `char_id`=%i LIMIT 1",
titleSet, CharacterID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
else {
LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf);
safe_delete_array(query);
}
if (results.RowCount() == 0)
return false;
return(false);
return true;
}
void Client::RemoveTitle(int titleset) {
void Client::RemoveTitle(int titleSet) {
if (!CheckTitle(titleset)) {
if (!CheckTitle(titleSet))
return;
}
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
std::string query = StringFormat("DELETE FROM player_titlesets "
"WHERE `title_set` = %i AND `char_id` = %i",
titleSet, CharacterID());
auto results = database.QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i", titleset, CharacterID()), errbuf)) {
safe_delete_array(query);
}
else {
LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query, errbuf);
safe_delete_array(query);
}
return;
}
+35 -41
View File
@@ -567,7 +567,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -696,7 +696,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -821,7 +821,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
{
Map::Vertex dest(x_pos, y_pos, z_pos);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
@@ -1007,26 +1007,19 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
}
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
char *query = 0;
char errbuff[MYSQL_ERRMSG_SIZE];
MYSQL_RES *result;
MYSQL_ROW row;
int res = 0;
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i",
zoneid),errbuff,&result)) {
safe_delete_array(query);
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
res = atoi( row[0] );
}
mysql_free_result(result);
} else {
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query, errbuff);
safe_delete_array(query);
}
return(res);
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) {
@@ -1245,45 +1238,45 @@ void ZoneDatabase::DeleteWaypoint(Client *client, uint32 grid_num, uint32 wp_num
uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) {
uint32 grid_num; // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating)
uint32 next_wp_num; // The waypoint number we should be assigning to the new waypoint
uint32 next_wp_num; // The waypoint number we should be assigning to the new waypoint
bool createdNewGrid; // Did we create a new grid in this function?
// See what grid number our spawn is assigned
std::string query = StringFormat("SELECT pathgrid FROM spawn2 WHERE id = %i", spawn2id);
auto results = QueryDatabase(query);
if (!results.Success()) {
// Query error
// Query error
LogFile->write(EQEMuLog::Error, "Error setting pathgrid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() == 0)
return 0;
return 0;
auto row = results.begin();
grid_num = atoi(row[0]);
auto row = results.begin();
grid_num = atoi(row[0]);
if (grid_num == 0)
{ // Our spawn doesn't have a grid assigned to it -- we need to create a new grid and assign it to the spawn
createdNewGrid = true;
grid_num = GetFreeGrid(zoneid);
if(grid_num == 0) // There are no grids for the current zone -- create Grid #1
grid_num = 1;
grid_num = 1;
query = StringFormat("INSERT INTO grid SET id = '%i', zoneid = %i, type ='%i', type2 = '%i'",
grid_num, zoneid, type1, type2);
results = QueryDatabase(query);
query = StringFormat("INSERT INTO grid SET id = '%i', zoneid = %i, type ='%i', type2 = '%i'",
grid_num, zoneid, type1, type2);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error adding grid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
client->LogSQL(query.c_str());
query = StringFormat("UPDATE spawn2 SET pathgrid = '%i' WHERE id = '%i'", grid_num, spawn2id);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error updating spawn2 pathing '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
client->LogSQL(query.c_str());
}
else // NPC had a grid assigned to it
createdNewGrid = false;
@@ -1291,25 +1284,26 @@ uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos,
// Find out what the next waypoint is for this grid
query = StringFormat("SELECT max(`number`) FROM grid_entries WHERE zoneid = '%i' AND gridid = '%i'", zoneid, grid_num);
results = QueryDatabase(query);
if(!results.Success()) { // Query error
LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
row = results.begin();
if(row[0] != 0)
next_wp_num = atoi(row[0]) + 1;
else // No waypoints in this grid yet
row = results.begin();
if(row[0] != 0)
next_wp_num = atoi(row[0]) + 1;
else // No waypoints in this grid yet
next_wp_num = 1;
query = StringFormat("INSERT INTO grid_entries(gridid, zoneid, `number`, x, y, z, pause, heading) "
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
grid_num, zoneid, next_wp_num, xpos, ypos, zpos, pause, heading);
results = QueryDatabase(query);
"VALUES (%i, %i, %i, %f, %f, %f, %i, %f)",
grid_num, zoneid, next_wp_num, xpos, ypos, zpos, pause, heading);
results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error adding grid entry '%s': '%s'", query.c_str(), results.ErrorMessage().c_str());
else if(client)
client->LogSQL(query.c_str());
client->LogSQL(query.c_str());
return createdNewGrid? grid_num: 0;
}
+31 -8
View File
@@ -168,18 +168,24 @@ void WorldServer::Process() {
break;
}
case ServerOP_ChannelMessage: {
if (!ZoneLoaded) break;
if (!ZoneLoaded)
break;
ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer;
if (scm->deliverto[0] == 0) {
entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message);
}
else {
Client* client;
client = entity_list.GetClientByName(scm->deliverto);
if (client != 0) {
} else {
Client* client = entity_list.GetClientByName(scm->deliverto);
if (client) {
if (client->Connected()) {
client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message);
if (!scm->noreply && scm->chan_num!=2) { //dont echo on group chat
if (scm->queued == 1) // tell was queued
client->Tell_StringID(QUEUED_TELL, scm->to, scm->message);
else if (scm->queued == 2) // tell queue was full
client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message);
else if (scm->queued == 3) // person was offline
client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE);
else // normal stuff
client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message);
if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat
// if it's a tell, echo back so it shows up
scm->noreply = true;
scm->chan_num = 14;
@@ -1856,6 +1862,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu
scm->chan_num = chan_num;
scm->guilddbid = guilddbid;
scm->language = language;
scm->queued = 0;
strcpy(scm->message, buffer);
pack->Deflate();
@@ -2180,3 +2187,19 @@ void WorldServer::HandleLFPMatches(ServerPacket *pack) {
safe_delete(outapp);
}
}
void WorldServer::RequestTellQueue(const char *who)
{
if (!who)
return;
ServerPacket* pack = new ServerPacket(ServerOP_RequestTellQueue, sizeof(ServerRequestTellQueue_Struct));
ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer;
strn0cpy(rtq->name, who, sizeof(rtq->name));
SendPacket(pack);
safe_delete(pack);
return;
}
+2
View File
@@ -57,6 +57,8 @@ public:
void HandleLFGMatches(ServerPacket *pack);
void HandleLFPMatches(ServerPacket *pack);
void RequestTellQueue(const char *who);
private:
virtual void OnConnected();
+1573 -2271
View File
File diff suppressed because it is too large Load Diff
+1 -9
View File
@@ -246,11 +246,7 @@ public:
/*
* General Character Related Stuff
*/
void StoreCharacterLookup(uint32 char_id);
bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
bool GetAccountInfoForLogin(uint32 account_id, int16* admin = 0, char* account_name = 0,
uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = 0);
void StoreCharacterLookup(uint32 char_id);
bool GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin = 0, char* account_name = 0,
uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr,
uint32* account_creation = 0);
@@ -258,10 +254,6 @@ public:
PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0,
uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_= 0, uint8 *level = 0, bool *LFP = 0,
bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0);
bool GetCharacterInfoForLogin(const char* name, uint32* character_id = 0, char* current_zone = 0,
PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0,
uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_ = 0, uint8 *level = 0, bool *LFP = 0,
bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0);
void SaveBuffs(Client *c);
void LoadBuffs(Client *c);
void LoadPetInfo(Client *c);
+1
View File
@@ -111,6 +111,7 @@ struct NPCType
uint8 spawn_limit; //only this many may be in zone at a time (0=no limit)
uint8 mount_color; //only used by horse class
float attack_speed; //%+- on attack delay of the mob.
uint8 attack_delay; //delay between attacks in 10ths of a second
int accuracy_rating; //10 = 1% accuracy
int avoidance_rating; //10 = 1% avoidance
bool findable; //can be found with find command