Compare commits

...

7 Commits

Author SHA1 Message Date
Akkadius dc64561b3c [Release] 22.2.0 2023-01-27 01:13:38 -06:00
Akkadius cb2aee2713 [Release] 22.2.0 2023-01-27 01:12:30 -06:00
Chris Miles 0730b6b588 [Crash] Fix crash issue with log formatting during character creation (#2798) 2023-01-26 20:40:58 -06:00
Aeadoin 826550acac [Bots] Add EVENT_UNEQUIP_ITEM_BOT & EVENT_EQUIP_ITEM_BOT (#2796)
* Initial Commit, need to test.

* Add unequip events.

* const auto
2023-01-26 19:49:20 -05:00
Alex King b71b3f5be0 [Bots] ^create and ^viewcombos popup messages fix. (#2797)
# Notes
- These messages were showing weirdly and inconsistently, cleaned them up to show at max 4 per line.
- `^viewcombos` now shows class name and ID in the popup title.
- `^create help` now shows proper class names and IDs instead of `{}`.
2023-01-26 18:44:21 -05:00
Natedog2012 1fe79f430c [Feature] ResetItemCooldown added to lua/perl and fix item re-cast times to show properly (#2793)
* Testing.

* Add ResetItemCooldown and port it over to perl

* This flag needs to be set for updating shared item cooldowns

* Properly set item recast for all item types, on corpses, on looting

* SummonItem properly sets recast timers of summoned items

* Rename variable to avoid confusion and change manifest to be more specific

* Sanity check item_d

* Recast -1 added as RECAST_TYPE_UNLINKED_ITEM
ResetItemCooldown will still remove cooldown of item that we don't have so when we acquire it the cooldown is reset

* change magic numbers

* more magic numbers

* More constants yay

* Remove unneeded export DeleteItemRecastTimer

* Remove duplicate message, this is handled by the client in this part of the code

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2023-01-25 20:09:08 -06:00
Alex King e5dabe0afc [Git] Add CMake Files to .gitignore (#2792)
* [Git] Add CMake Files to GitIgnore

* Update .drone.yml
2023-01-24 18:37:14 -06:00
31 changed files with 499 additions and 110 deletions
-6
View File
@@ -4,9 +4,6 @@ kind: pipeline
type: docker type: docker
name: Build Linux name: Build Linux
clone:
depth: 1
# Limits how many of these builds can run on the drone runner at a time, this isn't about cores # Limits how many of these builds can run on the drone runner at a time, this isn't about cores
concurrency: concurrency:
limit: 1 limit: 1
@@ -43,9 +40,6 @@ name: Build Windows
concurrency: concurrency:
limit: 1 limit: 1
clone:
depth: 1
platform: platform:
os: windows os: windows
arch: amd64 arch: amd64
+3
View File
@@ -68,3 +68,6 @@ compile_flags.txt
!utils/scripts/build/ !utils/scripts/build/
!utils/scripts/build/should-release/should-release !utils/scripts/build/should-release/should-release
!utils/scripts/build/should-release/should-release.exe !utils/scripts/build/should-release/should-release.exe
# CMake Files
cmake-build-relwithdebinfo/*
+23
View File
@@ -1,3 +1,26 @@
## [22.2.0] - 01/27/2023
### Bots
* Add EVENT_UNEQUIP_ITEM_BOT & EVENT_EQUIP_ITEM_BOT ([#2796](https://github.com/EQEmu/Server/pull/2796)) ([Aeadoin](https://github.com/Aeadoin)) 2023-01-27
* ^create and ^viewcombos popup messages fix. ([#2797](https://github.com/EQEmu/Server/pull/2797)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-26
### Code Cleanup
* Cleanup #door Command. ([#2783](https://github.com/EQEmu/Server/pull/2783)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-24
### Crash
* Fix crash issue with log formatting during character creation ([#2798](https://github.com/EQEmu/Server/pull/2798)) ([Akkadius](https://github.com/Akkadius)) 2023-01-27
### Feature
* ResetItemCooldown added to lua/perl and fix item re-cast times to show properly ([#2793](https://github.com/EQEmu/Server/pull/2793)) ([Natedog2012](https://github.com/Natedog2012)) 2023-01-26
### Git
* Add CMake Files to .gitignore ([#2792](https://github.com/EQEmu/Server/pull/2792)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-01-25
## [22.1.2] - 01/24/2023 ## [22.1.2] - 01/24/2023
### CI/CD ### CI/CD
+2
View File
@@ -79,6 +79,8 @@
#define ANIM_DEATH 0x73 #define ANIM_DEATH 0x73
#define ANIM_LOOT 0x69 #define ANIM_LOOT 0x69
constexpr int16 RECAST_TYPE_UNLINKED_ITEM = -1;
typedef enum { typedef enum {
eaStanding = 0, eaStanding = 0,
eaSitting, //1 eaSitting, //1
+1 -1
View File
@@ -4545,7 +4545,7 @@ struct ItemVerifyReply_Struct {
struct ItemRecastDelay_Struct { struct ItemRecastDelay_Struct {
/*000*/ uint32 recast_delay; // in seconds /*000*/ uint32 recast_delay; // in seconds
/*004*/ uint32 recast_type; /*004*/ uint32 recast_type;
/*008*/ uint32 unknown008; /*008*/ bool ignore_casting_requirement; //Ignores recast times allows items to be reset?
/*012*/ /*012*/
}; };
+6 -2
View File
@@ -721,11 +721,15 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
inst->SetCharges(charges); inst->SetCharges(charges);
if (item->RecastDelay) { if (item->RecastDelay) {
if (timestamps.count(item->RecastType)) if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) {
inst->SetRecastTimestamp(timestamps.at(item->RecastType)); inst->SetRecastTimestamp(timestamps.at(item->RecastType));
else } else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) {
inst->SetRecastTimestamp(timestamps.at(item->ID));
}
else {
inst->SetRecastTimestamp(0); inst->SetRecastTimestamp(0);
} }
}
if (item->IsClassCommon()) { if (item->IsClassCommon()) {
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables // Build variables
// these get injected during the build pipeline // these get injected during the build pipeline
#define CURRENT_VERSION "22.1.2-dev" // always append -dev to the current version for custom-builds #define CURRENT_VERSION "22.2.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9217 #define CURRENT_BINARY_DATABASE_VERSION 9218
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
#endif #endif
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "eqemu-server", "name": "eqemu-server",
"version": "22.1.2", "version": "22.2.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EQEmu/Server.git" "url": "https://github.com/EQEmu/Server.git"
+1
View File
@@ -471,6 +471,7 @@
9215|2023_01_08_zone_max_level.sql|SHOW COLUMNS FROM `zone` LIKE 'max_level'|empty| 9215|2023_01_08_zone_max_level.sql|SHOW COLUMNS FROM `zone` LIKE 'max_level'|empty|
9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty| 9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty|
9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty| 9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty|
9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not
@@ -0,0 +1,2 @@
ALTER TABLE `character_item_recast`
CHANGE COLUMN `recast_type` `recast_type` INT(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `id`;
+33 -6
View File
@@ -1778,12 +1778,39 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
pp.binds[0].heading = pp.heading; pp.binds[0].heading = pp.heading;
} }
LogInfo("Current location [{}] [{}] [{}] [{}] [{}] [{}]", if (GetZone(pp.zone_id)) {
ZoneName(pp.zone_id), pp.zone_id, pp.x, pp.y, pp.z, pp.heading); LogInfo(
LogInfo("Bind location [{}] [{}] [{}] [{}] [{}]", "Current location [{}] [{}] [{:.2f}] [{:.2f}] [{:.2f}] [{:.2f}]",
ZoneName(pp.binds[0].zone_id), pp.binds[0].zone_id, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); ZoneName(pp.zone_id),
LogInfo("Home location [{}] [{}] [{}] [{}] [{}]", pp.zone_id,
ZoneName(pp.binds[4].zone_id), pp.binds[4].zone_id, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); pp.x,
pp.y,
pp.z,
pp.heading
);
}
if (GetZone(pp.binds[0].zone_id)) {
LogInfo(
"Bind location [{}] [{}] [{:.2f}] [{:.2f}] [{:.2f}]",
ZoneName(pp.binds[0].zone_id),
pp.binds[0].zone_id,
pp.binds[0].x,
pp.binds[0].y,
pp.binds[0].z
);
}
if (GetZone(pp.binds[4].zone_id)) {
LogInfo(
"Home location [{}] [{}] [{:.2f}] [{:.2f}] [{:.2f}]",
ZoneName(pp.binds[4].zone_id),
pp.binds[4].zone_id,
pp.binds[4].x,
pp.binds[4].y,
pp.binds[4].z
);
}
/* Starting Items inventory */ /* Starting Items inventory */
content_db.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); content_db.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin());
+16
View File
@@ -5074,6 +5074,13 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
BotRemoveEquipItem(return_iterator.from_bot_slot); BotRemoveEquipItem(return_iterator.from_bot_slot);
const auto export_string = fmt::format(
"{} {}",
return_iterator.return_item_instance->IsStackable() ? return_iterator.return_item_instance->GetCharges() : 1,
return_iterator.from_bot_slot
);
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, this, nullptr, export_string , return_iterator.return_item_instance->GetID());
if (return_instance) { if (return_instance) {
EQ::SayLinkEngine linker; EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst); linker.SetLinkType(EQ::saylink::SayLinkItemInst);
@@ -5122,6 +5129,15 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance); m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance);
BotAddEquipItem(trade_iterator.to_bot_slot, (trade_iterator.trade_item_instance ? trade_iterator.trade_item_instance->GetID() : 0)); BotAddEquipItem(trade_iterator.to_bot_slot, (trade_iterator.trade_item_instance ? trade_iterator.trade_item_instance->GetID() : 0));
const auto export_string = fmt::format(
"{} {}",
trade_iterator.trade_item_instance->IsStackable() ? trade_iterator.trade_item_instance->GetCharges() : 1,
trade_iterator.to_bot_slot
);
parse->EventBot(EVENT_EQUIP_ITEM_BOT, this, nullptr, export_string , trade_iterator.trade_item_instance->GetID());
trade_iterator.trade_item_instance = nullptr; // actual deletion occurs in client delete below trade_iterator.trade_item_instance = nullptr; // actual deletion occurs in client delete below
client->DeleteItemInInventory(trade_iterator.from_client_slot, 0, (trade_iterator.from_client_slot == EQ::invslot::slotCursor)); client->DeleteItemInInventory(trade_iterator.from_client_slot, 0, (trade_iterator.from_client_slot == EQ::invslot::slotCursor));
+142 -64
View File
@@ -5374,40 +5374,54 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
void bot_command_view_combos(Client *c, const Seperator *sep) void bot_command_view_combos(Client *c, const Seperator *sep)
{ {
const std::string class_substrs[17] = { "", const std::string class_substrs[17] = {
"%u (WAR)", "%u (CLR)", "%u (PAL)", "%u (RNG)", "",
"%u (SHD)", "%u (DRU)", "%u (MNK)", "%u (BRD)", "WAR", "CLR", "PAL", "RNG",
"%u (ROG)", "%u (SHM)", "%u (NEC)", "%u (WIZ)", "SHD", "DRU", "MNK", "BRD",
"%u (MAG)", "%u (ENC)", "%u (BST)", "%u (BER)" "ROG", "SHM", "NEC", "WIZ",
"MAG", "ENC", "BST", "BER"
}; };
const std::string race_substrs[17] = { "", const std::string race_substrs[17] = {
"%u (HUM)", "%u (BAR)", "%u (ERU)", "%u (ELF)", "",
"%u (HIE)", "%u (DEF)", "%u (HEF)", "%u (DWF)", "HUM", "BAR", "ERU", "ELF",
"%u (TRL)", "%u (OGR)", "%u (HFL)", "%u (GNM)", "HIE", "DEF", "HEF", "DWF",
"%u (IKS)", "%u (VAH)", "%u (FRG)", "%u (DRK)" "TRL", "OGR", "HFL", "GNM",
"IKS", "VAH", "FRG", "DRK"
}; };
const uint16 race_values[17] = { 0, const uint16 race_values[17] = {
HUMAN, BARBARIAN, ERUDITE, WOOD_ELF, RACE_DOUG_0,
HIGH_ELF, DARK_ELF, HALF_ELF, DWARF, RACE_HUMAN_1, RACE_BARBARIAN_2, RACE_ERUDITE_3, RACE_WOOD_ELF_4,
TROLL, OGRE, HALFLING, GNOME, RACE_HIGH_ELF_5, RACE_DARK_ELF_6, RACE_HALF_ELF_7, RACE_DWARF_8,
IKSAR, VAHSHIR, FROGLOK, DRAKKIN RACE_TROLL_9, RACE_OGRE_10, RACE_HALFLING_11, RACE_GNOME_12,
RACE_IKSAR_128, RACE_VAH_SHIR_130, RACE_FROGLOK_330, RACE_DRAKKIN_522
}; };
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos"))
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
std::string window_title = "Bot Races";
std::string window_text; std::string window_text;
std::string message_separator = " "; std::string message_separator = " ";
c->Message(Chat::White, "Usage: %s [bot_race]", sep->arg[0]); c->Message(Chat::White, fmt::format("Usage: {} [Race]", sep->arg[0]).c_str());
window_text.append("<c \"#FFFFFF\">Races:<c \"#FFFF\">");
window_text.append("<c \"#FFFF\">");
for (int race_id = 0; race_id <= 15; ++race_id) { for (int race_id = 0; race_id <= 15; ++race_id) {
window_text.append(message_separator); window_text.append(message_separator);
window_text.append(StringFormat(race_substrs[race_id + 1].c_str(), race_values[race_id + 1])); window_text.append(
fmt::format(
"{} ({})",
race_substrs[race_id + 1],
race_values[race_id + 1]
)
);
message_separator = ", "; message_separator = ", ";
} }
c->SendPopupToClient(window_title.c_str(), window_text.c_str()); c->SendPopupToClient("Bot Races", window_text.c_str());
return; return;
} }
@@ -5415,53 +5429,92 @@ void bot_command_view_combos(Client *c, const Seperator *sep)
c->Message(Chat::White, "Invalid Race!"); c->Message(Chat::White, "Invalid Race!");
return; return;
} }
uint16 bot_race = atoi(sep->arg[1]);
auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race); const uint16 bot_race = static_cast<uint16>(std::stoul(sep->arg[1]));
auto race_name = GetRaceIDName(bot_race); const std::string race_name = GetRaceIDName(bot_race);
std::string window_title = "Bot Classes";
if (!Mob::IsPlayerRace(bot_race)) {
c->Message(
Chat::White,
fmt::format(
"{} ({}) is not a race bots can use.",
race_name,
bot_race
).c_str()
);
return;
}
const auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race);
std::string window_text; std::string window_text;
std::string message_separator = " "; std::string message_separator = " ";
c->Message(Chat::White, "%s can be these classes.", race_name);
window_text.append("<c \"#FFFFFF\">Classes:<c \"#FFFF\">"); window_text.append("<c \"#FFFF\">");
const int object_max = 4;
auto object_count = 0;
for (int class_id = 0; class_id <= 15; ++class_id) { for (int class_id = 0; class_id <= 15; ++class_id) {
if (classes_bitmask & GetPlayerClassBit(class_id)) { if (classes_bitmask & GetPlayerClassBit(class_id)) {
window_text.append(message_separator); window_text.append(message_separator);
window_text.append(StringFormat(class_substrs[class_id].c_str(), class_id));
if (object_count >= object_max) {
window_text.append(DialogueWindow::Break());
object_count = 0;
}
window_text.append(
fmt::format(
"{} ({})",
class_substrs[class_id],
class_id
)
);
++object_count;
message_separator = ", "; message_separator = ", ";
} }
} }
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return; c->SendPopupToClient(
fmt::format(
"Bot Classes for {} ({})",
race_name,
bot_race
).c_str(),
window_text.c_str()
);
} }
void bot_subcommand_bot_create(Client *c, const Seperator *sep) void bot_subcommand_bot_create(Client *c, const Seperator *sep)
{ {
const std::string class_substrs[17] = { const std::string class_substrs[17] = {
"", "",
"{} (WAR)", "{} (CLR)", "{} (PAL)", "{} (RNG)", "WAR", "CLR", "PAL", "RNG",
"{} (SHD)", "{} (DRU)", "{} (MNK)", "{} (BRD)", "SHD", "DRU", "MNK", "BRD",
"{} (ROG)", "{} (SHM)", "{} (NEC)", "{} (WIZ)", "ROG", "SHM", "NEC", "WIZ",
"{} (MAG)", "{} (ENC)", "{} (BST)", "{} (BER)" "MAG", "ENC", "BST", "BER"
}; };
const std::string race_substrs[17] = { const std::string race_substrs[17] = {
"", "",
"{} (HUM)", "{} (BAR)", "{} (ERU)", "{} (ELF)", "HUM", "BAR", "ERU", "ELF",
"{} (HIE)", "{} (DEF)", "{} (HEF)", "{} (DWF)", "HIE", "DEF", "HEF", "DWF",
"{} (TRL)", "{} (OGR)", "{} (HFL)", "{} (GNM)", "TRL", "OGR", "HFL", "GNM",
"{} (IKS)", "{} (VAH)", "{} (FRG)", "{} (DRK)" "IKS", "VAH", "FRG", "DRK"
}; };
const uint16 race_values[17] = { const uint16 race_values[17] = {
0, RACE_DOUG_0,
HUMAN, BARBARIAN, ERUDITE, WOOD_ELF, RACE_HUMAN_1, RACE_BARBARIAN_2, RACE_ERUDITE_3, RACE_WOOD_ELF_4,
HIGH_ELF, DARK_ELF, HALF_ELF, DWARF, RACE_HIGH_ELF_5, RACE_DARK_ELF_6, RACE_HALF_ELF_7, RACE_DWARF_8,
TROLL, OGRE, HALFLING, GNOME, RACE_TROLL_9, RACE_OGRE_10, RACE_HALFLING_11, RACE_GNOME_12,
IKSAR, VAHSHIR, FROGLOK, DRAKKIN RACE_IKSAR_128, RACE_VAH_SHIR_130, RACE_FROGLOK_330, RACE_DRAKKIN_522
}; };
const std::string gender_substrs[2] = { const std::string gender_substrs[2] = {
"{} (M)", "{} (F)", "Male", "Female",
}; };
if (helper_command_alias_fail(c, "bot_subcommand_bot_create", sep->arg[0], "botcreate")) { if (helper_command_alias_fail(c, "bot_subcommand_bot_create", sep->arg[0], "botcreate")) {
@@ -5472,7 +5525,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Usage: {} [bot_name] [bot_class] [bot_race] [bot_gender]", "Usage: {} [Name] [Class] [Race] [Gender]",
sep->arg[0] sep->arg[0]
).c_str() ).c_str()
); );
@@ -5480,22 +5533,27 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
std::string window_text; std::string window_text;
std::string message_separator; std::string message_separator;
int object_count = 0; int object_count = 0;
const int object_max = 5; const int object_max = 4;
window_text.append("<c \"#FFFFFF\">Classes:<c \"#FFFF\">"); window_text.append(
fmt::format(
"Classes{}<c \"#FFFF\">",
DialogueWindow::Break()
)
);
message_separator = " "; message_separator = " ";
object_count = 1; object_count = 0;
for (int i = 0; i <= 15; ++i) { for (int i = 0; i <= 15; ++i) {
window_text.append(message_separator); window_text.append(message_separator);
if (object_count >= object_max) { if (object_count >= object_max) {
window_text.append("<br>"); window_text.append(DialogueWindow::Break());
object_count = 0; object_count = 0;
} }
window_text.append( window_text.append(
fmt::format("{} {}", fmt::format("{} ({})",
class_substrs[i + 1], class_substrs[i + 1],
(i + 1) (i + 1)
) )
@@ -5505,22 +5563,27 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
message_separator = ", "; message_separator = ", ";
} }
window_text.append("<br><br>"); window_text.append(DialogueWindow::Break(2));
window_text.append("<c \"#FFFFFF\">Races:<c \"#FFFF\">"); window_text.append(
fmt::format(
"<c \"#FFFFFF\">Races{}<c \"#FFFF\">",
DialogueWindow::Break()
)
);
message_separator = " "; message_separator = " ";
object_count = 1; object_count = 0;
for (int i = 0; i <= 15; ++i) { for (int i = 0; i <= 15; ++i) {
window_text.append(message_separator); window_text.append(message_separator);
if (object_count >= object_max) { if (object_count >= object_max) {
window_text.append("<br>"); window_text.append(DialogueWindow::Break());
object_count = 0; object_count = 0;
} }
window_text.append( window_text.append(
fmt::format("{}, {}", fmt::format("{} ({})",
race_substrs[i + 1], race_substrs[i + 1],
race_values[i + 1] race_values[i + 1]
) )
@@ -5530,16 +5593,21 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
message_separator = ", "; message_separator = ", ";
} }
window_text.append("<br><br>"); window_text.append(DialogueWindow::Break(2));
window_text.append("<c \"#FFFFFF\">Genders:<c \"#FFFF\">"); window_text.append(
fmt::format(
"<c \"#FFFFFF\">Genders{}<c \"#FFFF\">",
DialogueWindow::Break()
)
);
message_separator = " "; message_separator = " ";
for (int i = 0; i <= 1; ++i) { for (int i = 0; i <= 1; ++i) {
window_text.append(message_separator); window_text.append(message_separator);
window_text.append( window_text.append(
fmt::format("{}, {}", fmt::format("{} ({})",
gender_substrs[i], gender_substrs[i],
i i
) )
@@ -5548,13 +5616,12 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
message_separator = ", "; message_separator = ", ";
} }
c->SendPopupToClient("Bot Create Options", window_text.c_str()); c->SendPopupToClient("Bot Creation Options", window_text.c_str());
return; return;
} }
auto arguments = sep->argnum; const auto arguments = sep->argnum;
if (!arguments || sep->IsNumber(1)) { if (!arguments || sep->IsNumber(1)) {
c->Message(Chat::White, "You must name your bot!"); c->Message(Chat::White, "You must name your bot!");
return; return;
@@ -5581,15 +5648,18 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
return; return;
} }
auto bot_gender = 0; auto bot_gender = MALE;
if (sep->IsNumber(4)) { if (sep->IsNumber(4)) {
bot_gender = static_cast<uint8>(std::stoul(sep->arg[4])); bot_gender = static_cast<uint8>(std::stoul(sep->arg[4]));
if (bot_gender == NEUTER) {
bot_gender = MALE;
}
} else { } else {
if (!strcasecmp(sep->arg[4], "m") || !strcasecmp(sep->arg[4], "male")) { if (!strcasecmp(sep->arg[4], "m") || !strcasecmp(sep->arg[4], "male")) {
bot_gender = 0; bot_gender = MALE;
} else if (!strcasecmp(sep->arg[4], "f") || !strcasecmp(sep->arg[4], "female")) { } else if (!strcasecmp(sep->arg[4], "f") || !strcasecmp(sep->arg[4], "female")) {
bot_gender = 1; bot_gender = FEMALE;
} }
} }
@@ -9407,6 +9477,14 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
slot_id slot_id
) )
); );
const auto export_string = fmt::format(
"{} {}",
inst->IsStackable() ? inst->GetCharges() : 1,
slot_id
);
parse->EventBot(EVENT_UNEQUIP_ITEM_BOT, my_bot, nullptr, export_string, inst->GetID());
} }
} }
+43 -4
View File
@@ -10446,8 +10446,8 @@ int Client::CountItem(uint32 item_id)
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
}; };
const size_t size = sizeof(slots) / sizeof(slots[0]); const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
for (int slot_index = 0; slot_index < size; ++slot_index) { for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
item = GetInv().GetItem(slot_id); item = GetInv().GetItem(slot_id);
if (item && item->GetID() == item_id) { if (item && item->GetID() == item_id) {
@@ -10459,6 +10459,45 @@ int Client::CountItem(uint32 item_id)
return quantity; return quantity;
} }
void Client::ResetItemCooldown(uint32 item_id)
{
EQ::ItemInstance *item = nullptr;
const EQ::ItemData* item_d = database.GetItem(item_id);
if (!item_d) {
return;
}
int recast_type = item_d->RecastType;
bool found_item = false;
static const int16 slots[][2] = {
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
{ EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END},
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
{ EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END },
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
};
const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
item = GetInv().GetItem(slot_id);
if (item) {
item_d = item->GetItem();
if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) {
item->SetRecastTimestamp(0);
DeleteItemRecastTimer(item_d->ID);
SendItemPacket(slot_id, item, ItemPacketCharmUpdate);
found_item = true;
}
}
}
}
if (!found_item) {
DeleteItemRecastTimer(item_id); //We didn't find the item but we still want to remove the timer
}
}
void Client::RemoveItem(uint32 item_id, uint32 quantity) void Client::RemoveItem(uint32 item_id, uint32 quantity)
{ {
EQ::ItemInstance *item = nullptr; EQ::ItemInstance *item = nullptr;
@@ -10472,8 +10511,8 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity)
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
}; };
int16 removed_count = 0; int16 removed_count = 0;
const size_t size = sizeof(slots) / sizeof(slots[0]); const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
for (int slot_index = 0; slot_index < size; ++slot_index) { for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
if (removed_count == quantity) { if (removed_count == quantity) {
break; break;
+3 -1
View File
@@ -966,6 +966,7 @@ public:
void SendCursorBuffer(); void SendCursorBuffer();
void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true);
int CountItem(uint32 item_id); int CountItem(uint32 item_id);
void ResetItemCooldown(uint32 item_id);
void RemoveItem(uint32 item_id, uint32 quantity = 1); void RemoveItem(uint32 item_id, uint32 quantity = 1);
bool SwapItem(MoveItem_Struct* move_in); bool SwapItem(MoveItem_Struct* move_in);
void SwapItemResync(MoveItem_Struct* move_slots); void SwapItemResync(MoveItem_Struct* move_slots);
@@ -1516,8 +1517,9 @@ public:
void SendReloadCommandMessages(); void SendReloadCommandMessages();
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0, bool in_ignore_casting_requirement = false);
void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot);
void DeleteItemRecastTimer(uint32 item_id);
bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot);
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
+11 -3
View File
@@ -8927,9 +8927,12 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
{ {
if (item->RecastDelay > 0) if (item->RecastDelay > 0)
{ {
if (!GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) { if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) {
SendItemRecastTimer(item->RecastType); //Problem: When you loot corpse, recast display is not present. This causes it to display again. Could not get to display when sending from looting. SendItemRecastTimer(item->RecastType); //Problem: When you loot corpse, recast display is not present. This causes it to display again. Could not get to display when sending from looting.
MessageString(Chat::Red, SPELL_RECAST); SendSpellBarEnable(item->Click.Effect);
LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id);
return;
} else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerNegativeItemReuse * item->ID), false)) {
SendSpellBarEnable(item->Click.Effect); SendSpellBarEnable(item->Click.Effect);
LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id);
return; return;
@@ -8972,11 +8975,16 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
{ {
if (augitem->RecastDelay > 0) if (augitem->RecastDelay > 0)
{ {
if (!GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) { if (augitem->RecastType != RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) {
LogSpells("Casting of [{}] canceled: item spell reuse timer from augment not expired", spell_id); LogSpells("Casting of [{}] canceled: item spell reuse timer from augment not expired", spell_id);
MessageString(Chat::Red, SPELL_RECAST); MessageString(Chat::Red, SPELL_RECAST);
SendSpellBarEnable(augitem->Click.Effect); SendSpellBarEnable(augitem->Click.Effect);
return; return;
} else if (augitem->RecastType == RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerNegativeItemReuse * augitem->ID), false)) {
MessageString(Chat::Red, SPELL_RECAST);
SendSpellBarEnable(augitem->Click.Effect);
LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id);
return;
} }
} }
+22 -2
View File
@@ -1209,8 +1209,13 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges); auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges);
if (pkinst) { if (pkinst) {
if (pkitem->RecastDelay) if (pkitem->RecastDelay) {
if (pkitem->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0); pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0);
} else {
pkinst->SetRecastTimestamp(timestamps.count(pkitem->ID) ? timestamps.at(pkitem->ID) : 0);
}
}
LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", EQ::invslot::CORPSE_BEGIN, pkitem->Name); LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", EQ::invslot::CORPSE_BEGIN, pkitem->Name);
@@ -1264,8 +1269,13 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
if (!inst) if (!inst)
continue; continue;
if (item->RecastDelay) if (item->RecastDelay) {
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0);
} else {
inst->SetRecastTimestamp(timestamps.count(item->ID) ? timestamps.at(item->ID) : 0);
}
}
LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", loot_slot, item->Name); LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", loot_slot, item->Name);
@@ -1477,6 +1487,16 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
// get count for task update before it's mutated by AutoPutLootInInventory // get count for task update before it's mutated by AutoPutLootInInventory
int count = inst->IsStackable() ? inst->GetCharges() : 1; int count = inst->IsStackable() ? inst->GetCharges() : 1;
//Set recast on item when looting it!
auto timestamps = database.GetItemRecastTimestamps(client->CharacterID());
const auto* d = inst->GetItem();
if (d->RecastDelay) {
if (d->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
inst->SetRecastTimestamp(timestamps.count(d->RecastType) ? timestamps.at(d->RecastType) : 0);
} else {
inst->SetRecastTimestamp(timestamps.count(d->ID) ? timestamps.at(d->ID) : 0);
}
}
/* First add it to the looter - this will do the bag contents too */ /* First add it to the looter - this will do the bag contents too */
if (lootitem->auto_loot > 0) { if (lootitem->auto_loot > 0) {
+11
View File
@@ -169,6 +169,8 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_BOT_CREATE", "EVENT_BOT_CREATE",
"EVENT_AUGMENT_INSERT_CLIENT", "EVENT_AUGMENT_INSERT_CLIENT",
"EVENT_AUGMENT_REMOVE_CLIENT", "EVENT_AUGMENT_REMOVE_CLIENT",
"EVENT_EQUIP_ITEM_BOT",
"EVENT_UNEQUIP_ITEM_BOT",
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT", "EVENT_SPELL_EFFECT_BOT",
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT" "EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
@@ -1901,6 +1903,15 @@ void PerlembParser::ExportEventVariables(
break; break;
} }
case EVENT_EQUIP_ITEM_BOT:
case EVENT_UNEQUIP_ITEM_BOT: {
Seperator sep(data);
ExportVar(package_name.c_str(), "item_id", extradata);
ExportVar(package_name.c_str(), "item_quantity", sep.arg[0]);
ExportVar(package_name.c_str(), "slot_id", sep.arg[1]);
break;
}
case EVENT_AUGMENT_INSERT_CLIENT: case EVENT_AUGMENT_INSERT_CLIENT:
case EVENT_AUGMENT_REMOVE_CLIENT: { case EVENT_AUGMENT_REMOVE_CLIENT: {
Seperator sep(data); Seperator sep(data);
+2
View File
@@ -114,6 +114,8 @@ typedef enum {
EVENT_BOT_CREATE, EVENT_BOT_CREATE,
EVENT_AUGMENT_INSERT_CLIENT, EVENT_AUGMENT_INSERT_CLIENT,
EVENT_AUGMENT_REMOVE_CLIENT, EVENT_AUGMENT_REMOVE_CLIENT,
EVENT_EQUIP_ITEM_BOT,
EVENT_UNEQUIP_ITEM_BOT,
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT, EVENT_SPELL_EFFECT_BOT,
EVENT_SPELL_EFFECT_BUFF_TIC_BOT, EVENT_SPELL_EFFECT_BUFF_TIC_BOT,
+9
View File
@@ -714,6 +714,15 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
// in any other situation just use charges as passed // in any other situation just use charges as passed
EQ::ItemInstance* inst = database.CreateItem(item, charges); EQ::ItemInstance* inst = database.CreateItem(item, charges);
auto timestamps = database.GetItemRecastTimestamps(CharacterID());
const auto* d = inst->GetItem();
if (d->RecastDelay) {
if (d->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
inst->SetRecastTimestamp(timestamps.count(d->RecastType) ? timestamps.at(d->RecastType) : 0);
} else {
inst->SetRecastTimestamp(timestamps.count(d->ID) ? timestamps.at(d->ID) : 0);
}
}
if(inst == nullptr) { if(inst == nullptr) {
Message(Chat::Red, "An unknown server error has occurred and your item was not created."); Message(Chat::Red, "An unknown server error has occurred and your item was not created.");
+7
View File
@@ -3014,6 +3014,12 @@ void Lua_Client::CampAllBots(uint8 class_id)
self->CampAllBots(class_id); self->CampAllBots(class_id);
} }
void Lua_Client::ResetItemCooldown(uint32 item_id)
{
Lua_Safe_Call_Void();
self->ResetItemCooldown(item_id);
}
luabind::scope lua_register_client() { luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client") return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -3373,6 +3379,7 @@ luabind::scope lua_register_client() {
.def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot) .def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot)
.def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID) .def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID)
.def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer) .def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer)
.def("ResetItemCooldown", (void(Lua_Client::*)(uint32))&Lua_Client::ResetItemCooldown)
.def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade) .def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade)
.def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction) .def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction)
.def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save) .def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save)
+1
View File
@@ -462,6 +462,7 @@ public:
bool CanEnterZone(std::string zone_short_name); bool CanEnterZone(std::string zone_short_name);
bool CanEnterZone(std::string zone_short_name, int16 instance_version); bool CanEnterZone(std::string zone_short_name, int16 instance_version);
void SendPath(Lua_Mob target); void SendPath(Lua_Mob target);
void ResetItemCooldown(uint32 item_id);
void ApplySpell(int spell_id); void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration); void ApplySpell(int spell_id, int duration);
+3 -1
View File
@@ -4625,7 +4625,9 @@ luabind::scope lua_register_events() {
luabind::value("despawn_zone", static_cast<int>(EVENT_DESPAWN_ZONE)), luabind::value("despawn_zone", static_cast<int>(EVENT_DESPAWN_ZONE)),
luabind::value("bot_create", static_cast<int>(EVENT_BOT_CREATE)), luabind::value("bot_create", static_cast<int>(EVENT_BOT_CREATE)),
luabind::value("augment_insert_client", static_cast<int>(EVENT_AUGMENT_INSERT_CLIENT)), luabind::value("augment_insert_client", static_cast<int>(EVENT_AUGMENT_INSERT_CLIENT)),
luabind::value("augment_remove_client", static_cast<int>(EVENT_AUGMENT_REMOVE_CLIENT)) luabind::value("augment_remove_client", static_cast<int>(EVENT_AUGMENT_REMOVE_CLIENT)),
luabind::value("equip_item_bot", static_cast<int>(EVENT_EQUIP_ITEM_BOT)),
luabind::value("unequip_item_bot", static_cast<int>(EVENT_UNEQUIP_ITEM_BOT))
)]; )];
} }
+4
View File
@@ -156,6 +156,8 @@ const char *LuaEvents[_LargestEventID] = {
"event_bot_create", "event_bot_create",
"event_augment_insert_client", "event_augment_insert_client",
"event_augment_remove_client", "event_augment_remove_client",
"event_equip_item_bot",
"event_unequip_item_bot",
}; };
extern Zone *zone; extern Zone *zone;
@@ -308,6 +310,8 @@ LuaParser::LuaParser() {
BotArgumentDispatch[EVENT_TRADE] = handle_bot_trade; BotArgumentDispatch[EVENT_TRADE] = handle_bot_trade;
BotArgumentDispatch[EVENT_USE_SKILL] = handle_bot_use_skill; BotArgumentDispatch[EVENT_USE_SKILL] = handle_bot_use_skill;
BotArgumentDispatch[EVENT_PAYLOAD] = handle_bot_payload; BotArgumentDispatch[EVENT_PAYLOAD] = handle_bot_payload;
BotArgumentDispatch[EVENT_EQUIP_ITEM_BOT] = handle_bot_equip_item;
BotArgumentDispatch[EVENT_UNEQUIP_ITEM_BOT] = handle_bot_equip_item;
#endif #endif
L = nullptr; L = nullptr;
+53
View File
@@ -1951,4 +1951,57 @@ void handle_bot_use_skill(
lua_setfield(L, -2, "skill_level"); lua_setfield(L, -2, "skill_level");
} }
void handle_bot_equip_item(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushnumber(L, extra_data);
lua_setfield(L, -2, "item_id");
Seperator sep(data.c_str());
lua_pushnumber(L, std::stoi(sep.arg[0]));
lua_setfield(L, -2, "item_quantity");
lua_pushnumber(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "slot_id");
Lua_ItemInst l_item(extra_data);
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
l_item_o.push(L);
lua_setfield(L, -2, "item");
}
void handle_bot_unequip_item(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushnumber(L, extra_data);
lua_setfield(L, -2, "item_id");
Seperator sep(data.c_str());
lua_pushnumber(L, std::stoi(sep.arg[0]));
lua_setfield(L, -2, "item_quantity");
lua_pushnumber(L, std::stoi(sep.arg[1]));
lua_setfield(L, -2, "slot_id");
Lua_ItemInst l_item(extra_data);
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
l_item_o.push(L);
lua_setfield(L, -2, "item");
}
#endif #endif
+20
View File
@@ -955,5 +955,25 @@ void handle_bot_payload(
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
void handle_bot_equip_item(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_bot_unequip_item(
QuestInterface *parse,
lua_State* L,
Bot* bot,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
#endif #endif
#endif #endif
+7 -1
View File
@@ -507,9 +507,15 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
cursordelete = true; // otherwise, we delete the new one cursordelete = true; // otherwise, we delete the new one
} }
if (item->RecastDelay) if (item->RecastDelay) {
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM) {
m_inst->SetRecastTimestamp( m_inst->SetRecastTimestamp(
database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType)); database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType));
} else {
m_inst->SetRecastTimestamp(
database.GetItemRecastTimestamp(sender->CharacterID(), item->ID));
}
}
std::string export_string = fmt::format("{}", item->ID); std::string export_string = fmt::format("{}", item->ID);
std::vector<std::any> args; std::vector<std::any> args;
+6
View File
@@ -2872,6 +2872,11 @@ void Perl_Client_CampAllBots(Client* self, uint8 class_id)
self->CampAllBots(class_id); self->CampAllBots(class_id);
} }
void Perl_Client_ResetItemCooldown(Client* self, uint32 item_id)
{
self->ResetItemCooldown(item_id);
}
void perl_register_client() void perl_register_client()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -3231,6 +3236,7 @@ void perl_register_client()
package.add("ResetCastbarCooldownBySlot", &Perl_Client_ResetCastbarCooldownBySlot); package.add("ResetCastbarCooldownBySlot", &Perl_Client_ResetCastbarCooldownBySlot);
package.add("ResetCastbarCooldownBySpellID", &Perl_Client_ResetCastbarCooldownBySpellID); package.add("ResetCastbarCooldownBySpellID", &Perl_Client_ResetCastbarCooldownBySpellID);
package.add("ResetDisciplineTimer", &Perl_Client_ResetDisciplineTimer); package.add("ResetDisciplineTimer", &Perl_Client_ResetDisciplineTimer);
package.add("ResetItemCooldown", &Perl_Client_ResetItemCooldown);
package.add("ResetTrade", &Perl_Client_ResetTrade); package.add("ResetTrade", &Perl_Client_ResetTrade);
package.add("Save", &Perl_Client_Save); package.add("Save", &Perl_Client_Save);
package.add("SaveBackup", &Perl_Client_SaveBackup); package.add("SaveBackup", &Perl_Client_SaveBackup);
+40 -5
View File
@@ -6342,9 +6342,9 @@ void Client::SendSpellAnim(uint16 target_id, uint16 spell_id)
entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles));
} }
void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay) void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay, bool in_ignore_casting_requirement)
{ {
if (recast_type == -1) { if (recast_type == RECAST_TYPE_UNLINKED_ITEM) {
return; return;
} }
@@ -6357,6 +6357,7 @@ void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay)
ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer;
ird->recast_delay = recast_delay; ird->recast_delay = recast_delay;
ird->recast_type = static_cast<uint32>(recast_type); ird->recast_type = static_cast<uint32>(recast_type);
ird->ignore_casting_requirement = in_ignore_casting_requirement; //True allows reset of item cast timers
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }
@@ -6369,10 +6370,12 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot)
int recast_delay = 0; int recast_delay = 0;
int recast_type = 0; int recast_type = 0;
bool from_augment = false; bool from_augment = false;
int item_casting = 0;
if (!item) { if (!item) {
return; return;
} }
item_casting = item->GetItem()->ID;
//Check primary item. //Check primary item.
if (item->GetItem()->RecastDelay > 0) { if (item->GetItem()->RecastDelay > 0) {
@@ -6396,6 +6399,7 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot)
recast_delay = aug_i->GetItem()->RecastDelay; recast_delay = aug_i->GetItem()->RecastDelay;
recast_type = aug_i->GetItem()->RecastType; recast_type = aug_i->GetItem()->RecastType;
from_augment = true; from_augment = true;
item_casting = aug_i->GetItem()->ID;
break; break;
} }
} }
@@ -6410,13 +6414,20 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot)
if (recast_delay > 0) { if (recast_delay > 0) {
if (recast_type != RECAST_TYPE_UNLINKED_ITEM) {
GetPTimers().Start((pTimerItemStart + recast_type), static_cast<uint32>(recast_delay)); GetPTimers().Start((pTimerItemStart + recast_type), static_cast<uint32>(recast_delay));
if (recast_type != -1) { database.UpdateItemRecast(
database.UpdateItemRecastTimestamps(
CharacterID(), CharacterID(),
recast_type, recast_type,
GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp()
); );
} else if (recast_type == RECAST_TYPE_UNLINKED_ITEM) {
GetPTimers().Start((pTimerNegativeItemReuse * item_casting), static_cast<uint32>(recast_delay));
database.UpdateItemRecast(
CharacterID(),
item_casting,
GetPTimers().Get(pTimerNegativeItemReuse * item_casting)->GetReadyTimestamp()
);
} }
if (!from_augment) { if (!from_augment) {
@@ -6425,12 +6436,32 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot)
} }
} }
void Client::DeleteItemRecastTimer(uint32 item_id)
{
const auto* d = database.GetItem(item_id);
if (!d) {
return;
}
const auto recast_type = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? d->RecastType : item_id;
const int timer_id = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? (pTimerItemStart + recast_type) : (pTimerNegativeItemReuse * item_id);
database.DeleteItemRecast(CharacterID(), recast_type);
GetPTimers().Clear(&database, timer_id);
if (recast_type != RECAST_TYPE_UNLINKED_ITEM) {
SendItemRecastTimer(recast_type, 1, true);
}
}
bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
{ {
EQ::ItemInstance *item = CastToClient()->GetInv().GetItem(inventory_slot); EQ::ItemInstance *item = CastToClient()->GetInv().GetItem(inventory_slot);
int recast_delay = 0; int recast_delay = 0;
int recast_type = 0; int recast_type = 0;
int item_id = 0;
bool from_augment = false; bool from_augment = false;
if (!item) { if (!item) {
@@ -6445,6 +6476,7 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
if (item->GetItem()->RecastDelay > 0) { if (item->GetItem()->RecastDelay > 0) {
recast_type = item->GetItem()->RecastType; recast_type = item->GetItem()->RecastType;
recast_delay = item->GetItem()->RecastDelay; recast_delay = item->GetItem()->RecastDelay;
item_id = item->GetItem()->ID;
} }
//Check augmenent //Check augmenent
else { else {
@@ -6463,6 +6495,7 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) { if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) {
recast_delay = aug_i->GetItem()->RecastDelay; recast_delay = aug_i->GetItem()->RecastDelay;
recast_type = aug_i->GetItem()->RecastType; recast_type = aug_i->GetItem()->RecastType;
item_id = aug_i->GetItem()->ID;
} }
break; break;
} }
@@ -6473,7 +6506,9 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
return false; return false;
} }
//if time is not expired, then it exists and therefore we have a recast on this item. //if time is not expired, then it exists and therefore we have a recast on this item.
if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { if (recast_type != RECAST_TYPE_UNLINKED_ITEM && !CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) {
return true;
} else if (recast_type == RECAST_TYPE_UNLINKED_ITEM && !CastToClient()->GetPTimers().Expired(&database, (pTimerNegativeItemReuse * item_id), false)) {
return true; return true;
} }
+17 -4
View File
@@ -3333,11 +3333,24 @@ void ZoneDatabase::RemoveTempFactions(Client *client) {
QueryDatabase(query); QueryDatabase(query);
} }
void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp) void ZoneDatabase::UpdateItemRecast(uint32 character_id, uint32 recast_type, uint32 timestamp)
{ {
std::string query = const auto query = fmt::format(
StringFormat("REPLACE INTO character_item_recast (id, recast_type, timestamp) VALUES (%u, %u, %u)", char_id, "REPLACE INTO character_item_recast (id, recast_type, timestamp) VALUES ({}, {}, {})",
recast_type, timestamp); character_id,
recast_type,
timestamp
);
QueryDatabase(query);
}
void ZoneDatabase::DeleteItemRecast(uint32 character_id, uint32 recast_type)
{
const auto query = fmt::format(
"DELETE FROM character_item_recast WHERE id = {} AND recast_type = {}",
character_id,
recast_type
);
QueryDatabase(query); QueryDatabase(query);
} }
+2 -1
View File
@@ -369,7 +369,8 @@ public:
void LoadPetInfo(Client *c); void LoadPetInfo(Client *c);
void SavePetInfo(Client *c); void SavePetInfo(Client *c);
void RemoveTempFactions(Client *c); void RemoveTempFactions(Client *c);
void UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp); void UpdateItemRecast(uint32 char_id, uint32 recast_type, uint32 timestamp);
void DeleteItemRecast(uint32 char_id, uint32 recast_type);
bool DeleteCharacterAAs(uint32 character_id); bool DeleteCharacterAAs(uint32 character_id);
bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);