Merge branch 'master' into Bot_Raid_work

This commit is contained in:
neckkola
2022-02-13 07:26:18 -04:00
committed by GitHub
857 changed files with 92718 additions and 44581 deletions
+577 -268
View File
@@ -1,277 +1,586 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.2)
SET(zone_sources
aa.cpp
aa_ability.cpp
aggro.cpp
aggromanager.cpp
api_service.cpp
attack.cpp
aura.cpp
beacon.cpp
bonuses.cpp
bot.cpp
bot_command.cpp
bot_database.cpp
botspellsai.cpp
client.cpp
client_mods.cpp
client_packet.cpp
client_process.cpp
command.cpp
corpse.cpp
data_bucket.cpp
doors.cpp
dynamic_zone.cpp
effects.cpp
embparser.cpp
embparser_api.cpp
embperl.cpp
embxs.cpp
encounter.cpp
entity.cpp
exp.cpp
expedition.cpp
expedition_database.cpp
expedition_request.cpp
fastmath.cpp
fearpath.cpp
forage.cpp
groups.cpp
guild.cpp
guild_mgr.cpp
hate_list.cpp
heal_rotation.cpp
horse.cpp
inventory.cpp
loottables.cpp
lua_bit.cpp
lua_corpse.cpp
lua_client.cpp
lua_door.cpp
lua_encounter.cpp
lua_entity.cpp
lua_entity_list.cpp
lua_expedition.cpp
lua_general.cpp
lua_group.cpp
lua_hate_list.cpp
lua_inventory.cpp
lua_item.cpp
lua_iteminst.cpp
lua_mob.cpp
lua_mod.cpp
lua_npc.cpp
lua_object.cpp
lua_packet.cpp
lua_parser.cpp
lua_parser_events.cpp
lua_raid.cpp
lua_spawn.cpp
lua_spell.cpp
lua_stat_bonuses.cpp
embperl.cpp
embxs.cpp
entity.cpp
exp.cpp
fearpath.cpp
forage.cpp
global_loot_manager.cpp
groups.cpp
guild.cpp
guild_mgr.cpp
hate_list.cpp
horse.cpp
inventory.cpp
loottables.cpp
main.cpp
map.cpp
merc.cpp
mob.cpp
mob_ai.cpp
mob_appearance.cpp
mob_movement_manager.cpp
mob_info.cpp
mod_functions.cpp
npc.cpp
npc_ai.cpp
npc_scale_manager.cpp
object.cpp
oriented_bounding_box.cpp
pathfinder_interface.cpp
pathfinder_nav_mesh.cpp
pathfinder_null.cpp
pathing.cpp
perl_client.cpp
perl_doors.cpp
perl_entity.cpp
perl_expedition.cpp
perl_groups.cpp
perl_hateentry.cpp
perl_inventory.cpp
perl_mob.cpp
perl_npc.cpp
perl_object.cpp
perl_perlpacket.cpp
perl_player_corpse.cpp
perl_questitem.cpp
perl_raids.cpp
perlpacket.cpp
petitions.cpp
pets.cpp
position.cpp
qglobals.cpp
queryserv.cpp
questmgr.cpp
quest_parser_collection.cpp
raids.cpp
raycast_mesh.cpp
spawn2.cpp
spawn2.h
spawngroup.cpp
special_attacks.cpp
spell_effects.cpp
spells.cpp
task_client_state.cpp
task_goal_list_manager.cpp
task_manager.cpp
task_proximity_manager.cpp
tasks.cpp
titles.cpp
tradeskills.cpp
trading.cpp
trap.cpp
tribute.cpp
tune.cpp
water_map.cpp
water_map_v1.cpp
water_map_v2.cpp
waypoints.cpp
worldserver.cpp
xtargetautohaters.cpp
zone.cpp
zone_config.cpp
zonedb.cpp
zone_reload.cpp
zone_store.cpp
zoning.cpp)
aa.cpp
aa_ability.cpp
aggro.cpp
aggromanager.cpp
api_service.cpp
attack.cpp
aura.cpp
beacon.cpp
bonuses.cpp
bot.cpp
bot_command.cpp
bot_database.cpp
botspellsai.cpp
cheat_manager.cpp
client.cpp
client_mods.cpp
client_packet.cpp
client_process.cpp
command.cpp
corpse.cpp
data_bucket.cpp
doors.cpp
dialogue_window.cpp
dynamic_zone.cpp
effects.cpp
embparser.cpp
embparser_api.cpp
embperl.cpp
embxs.cpp
encounter.cpp
entity.cpp
exp.cpp
expedition.cpp
expedition_database.cpp
expedition_request.cpp
fastmath.cpp
fearpath.cpp
forage.cpp
groups.cpp
guild.cpp
guild_mgr.cpp
hate_list.cpp
heal_rotation.cpp
horse.cpp
inventory.cpp
loottables.cpp
lua_bot.cpp
lua_bit.cpp
lua_corpse.cpp
lua_client.cpp
lua_door.cpp
lua_encounter.cpp
lua_entity.cpp
lua_entity_list.cpp
lua_expedition.cpp
lua_general.cpp
lua_group.cpp
lua_hate_list.cpp
lua_inventory.cpp
lua_item.cpp
lua_iteminst.cpp
lua_mob.cpp
lua_mod.cpp
lua_npc.cpp
lua_object.cpp
lua_packet.cpp
lua_parser.cpp
lua_parser_events.cpp
lua_raid.cpp
lua_spawn.cpp
lua_spell.cpp
lua_stat_bonuses.cpp
embperl.cpp
embxs.cpp
entity.cpp
exp.cpp
fearpath.cpp
forage.cpp
global_loot_manager.cpp
gm_commands/door_manipulation.cpp
groups.cpp
guild.cpp
guild_mgr.cpp
hate_list.cpp
horse.cpp
inventory.cpp
loottables.cpp
main.cpp
map.cpp
merc.cpp
mob.cpp
mob_ai.cpp
mob_appearance.cpp
mob_movement_manager.cpp
mob_info.cpp
mod_functions.cpp
npc.cpp
npc_ai.cpp
npc_scale_manager.cpp
object.cpp
oriented_bounding_box.cpp
pathfinder_interface.cpp
pathfinder_nav_mesh.cpp
pathfinder_null.cpp
pathing.cpp
perl_bot.cpp
perl_client.cpp
perl_doors.cpp
perl_entity.cpp
perl_expedition.cpp
perl_groups.cpp
perl_hateentry.cpp
perl_inventory.cpp
perl_mob.cpp
perl_npc.cpp
perl_object.cpp
perl_perlpacket.cpp
perl_player_corpse.cpp
perl_questitem.cpp
perl_raids.cpp
perl_spell.cpp
perlpacket.cpp
petitions.cpp
pets.cpp
position.cpp
qglobals.cpp
queryserv.cpp
questmgr.cpp
quest_parser_collection.cpp
raids.cpp
raycast_mesh.cpp
shared_task_zone_messaging.cpp
spawn2.cpp
spawn2.h
spawngroup.cpp
special_attacks.cpp
spell_effects.cpp
spells.cpp
task_client_state.cpp
task_goal_list_manager.cpp
task_manager.cpp
task_proximity_manager.cpp
tasks.cpp
titles.cpp
tradeskills.cpp
trading.cpp
trap.cpp
tribute.cpp
tune.cpp
water_map.cpp
water_map_v1.cpp
water_map_v2.cpp
waypoints.cpp
worldserver.cpp
xtargetautohaters.cpp
zone.cpp
zone_config.cpp
zonedb.cpp
zone_event_scheduler.cpp
zone_reload.cpp
zone_store.cpp
zoning.cpp
)
SET(zone_headers
aa.h
aa_ability.h
aggromanager.h
api_service.h
aura.h
basic_functions.h
beacon.h
bot.h
bot_command.h
bot_database.h
bot_structs.h
client.h
client_packet.h
command.h
common.h
corpse.h
data_bucket.h
doors.h
dynamic_zone.h
embparser.h
embperl.h
embxs.h
encounter.h
entity.h
errmsg.h
event_codes.h
expedition.h
expedition_database.h
expedition_request.h
fastmath.h
forage.h
global_loot_manager.h
groups.h
guild_mgr.h
hate_list.h
heal_rotation.h
horse.h
lua_bit.h
lua_client.h
lua_corpse.h
lua_door.h
lua_encounter.h
lua_entity.h
lua_entity_list.h
lua_expedition.h
lua_general.h
lua_group.h
lua_hate_list.h
lua_inventory.h
lua_item.h
lua_iteminst.h
lua_mob.h
lua_mod.h
lua_npc.h
lua_object.h
lua_packet.h
lua_parser.h
lua_parser_events.h
lua_ptr.h
lua_raid.h
lua_spawn.h
lua_spell.h
lua_stat_bonuses.h
map.h
masterentity.h
maxskill.h
message.h
merc.h
mob.h
mob_movement_manager.h
npc.h
npc_ai.h
npc_scale_manager.h
object.h
oriented_bounding_box.h
pathfinder_interface.h
pathfinder_nav_mesh.h
pathfinder_null.h
perlpacket.h
petitions.h
pets.h
position.h
qglobals.h
quest_interface.h
queryserv.h
quest_interface.h
questmgr.h
quest_parser_collection.h
raids.h
raycast_mesh.h
skills.h
spawn2.cpp
spawn2.h
spawngroup.h
string_ids.h
task_client_state.h
task_goal_list_manager.h
task_manager.h
task_proximity_manager.h
tasks.h
titles.h
trap.h
water_map.h
water_map_v1.h
water_map_v2.h
worldserver.h
xtargetautohaters.h
zone.h
zone_config.h
zonedb.h
zonedump.h
zone_reload.h
zone_store.h)
aa.h
aa_ability.h
aggromanager.h
api_service.h
aura.h
basic_functions.h
beacon.h
bot.h
bot_command.h
bot_database.h
bot_structs.h
cheat_manager.h
client.h
client_packet.h
command.h
common.h
corpse.h
data_bucket.h
doors.h
dialogue_window.h
dynamic_zone.h
embparser.h
embperl.h
embxs.h
encounter.h
entity.h
errmsg.h
event_codes.h
expedition.h
expedition_database.h
expedition_request.h
fastmath.h
forage.h
global_loot_manager.h
gm_commands/door_manipulation.h
groups.h
guild_mgr.h
hate_list.h
heal_rotation.h
horse.h
lua_bot.h
lua_bit.h
lua_client.h
lua_corpse.h
lua_door.h
lua_encounter.h
lua_entity.h
lua_entity_list.h
lua_expedition.h
lua_general.h
lua_group.h
lua_hate_list.h
lua_inventory.h
lua_item.h
lua_iteminst.h
lua_mob.h
lua_mod.h
lua_npc.h
lua_object.h
lua_packet.h
lua_parser.h
lua_parser_events.h
lua_ptr.h
lua_raid.h
lua_spawn.h
lua_spell.h
lua_stat_bonuses.h
map.h
masterentity.h
maxskill.h
message.h
merc.h
mob.h
mob_movement_manager.h
npc.h
npc_ai.h
npc_scale_manager.h
object.h
oriented_bounding_box.h
pathfinder_interface.h
pathfinder_nav_mesh.h
pathfinder_null.h
perlpacket.h
petitions.h
pets.h
position.h
qglobals.h
quest_interface.h
queryserv.h
quest_interface.h
questmgr.h
quest_parser_collection.h
raids.h
raycast_mesh.h
skills.h
shared_task_zone_messaging.h
spawn2.cpp
spawn2.h
spawngroup.h
string_ids.h
task_client_state.h
task_goal_list_manager.h
task_manager.h
task_proximity_manager.h
tasks.h
titles.h
trap.h
water_map.h
water_map_v1.h
water_map_v2.h
worldserver.h
xtargetautohaters.h
zone.h
zone_event_scheduler.h
zone_config.h
zonedb.h
zonedump.h
zone_reload.h
zone_store.h
)
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
SET(gm_commands
gm_commands/acceptrules.cpp
gm_commands/advnpcspawn.cpp
gm_commands/aggro.cpp
gm_commands/aggrozone.cpp
gm_commands/ai.cpp
gm_commands/appearance.cpp
gm_commands/appearanceeffects.cpp
gm_commands/attack.cpp
gm_commands/augmentitem.cpp
gm_commands/ban.cpp
gm_commands/beard.cpp
gm_commands/beardcolor.cpp
gm_commands/bestz.cpp
gm_commands/bind.cpp
gm_commands/camerashake.cpp
gm_commands/castspell.cpp
gm_commands/chat.cpp
gm_commands/checklos.cpp
gm_commands/copycharacter.cpp
gm_commands/corpse.cpp
gm_commands/corpsefix.cpp
gm_commands/countitem.cpp
gm_commands/cvs.cpp
gm_commands/damage.cpp
gm_commands/databuckets.cpp
gm_commands/date.cpp
gm_commands/dbspawn2.cpp
gm_commands/delacct.cpp
gm_commands/deletegraveyard.cpp
gm_commands/delpetition.cpp
gm_commands/depop.cpp
gm_commands/depopzone.cpp
gm_commands/details.cpp
gm_commands/devtools.cpp
gm_commands/disablerecipe.cpp
gm_commands/disarmtrap.cpp
gm_commands/distance.cpp
gm_commands/doanim.cpp
gm_commands/door.cpp
gm_commands/dye.cpp
gm_commands/dz.cpp
gm_commands/dzkickplayers.cpp
gm_commands/editmassrespawn.cpp
gm_commands/emote.cpp
gm_commands/emotesearch.cpp
gm_commands/emoteview.cpp
gm_commands/enablerecipe.cpp
gm_commands/endurance.cpp
gm_commands/equipitem.cpp
gm_commands/face.cpp
gm_commands/faction.cpp
gm_commands/findclass.cpp
gm_commands/findfaction.cpp
gm_commands/findnpctype.cpp
gm_commands/findrace.cpp
gm_commands/findskill.cpp
gm_commands/findspell.cpp
gm_commands/findtask.cpp
gm_commands/findzone.cpp
gm_commands/fixmob.cpp
gm_commands/flag.cpp
gm_commands/flagedit.cpp
gm_commands/flags.cpp
gm_commands/flymode.cpp
gm_commands/fov.cpp
gm_commands/freeze.cpp
gm_commands/gassign.cpp
gm_commands/gearup.cpp
gm_commands/gender.cpp
gm_commands/getplayerburiedcorpsecount.cpp
gm_commands/getvariable.cpp
gm_commands/ginfo.cpp
gm_commands/giveitem.cpp
gm_commands/givemoney.cpp
gm_commands/globalview.cpp
gm_commands/gm.cpp
gm_commands/gmspeed.cpp
gm_commands/gmzone.cpp
gm_commands/goto.cpp
gm_commands/grid.cpp
gm_commands/guild.cpp
gm_commands/guildapprove.cpp
gm_commands/guildcreate.cpp
gm_commands/guildlist.cpp
gm_commands/hair.cpp
gm_commands/haircolor.cpp
gm_commands/haste.cpp
gm_commands/hatelist.cpp
gm_commands/heal.cpp
gm_commands/helm.cpp
gm_commands/heritage.cpp
gm_commands/heromodel.cpp
gm_commands/hideme.cpp
gm_commands/hp.cpp
gm_commands/incstat.cpp
gm_commands/instance.cpp
gm_commands/interrogateinv.cpp
gm_commands/interrupt.cpp
gm_commands/invsnapshot.cpp
gm_commands/invul.cpp
gm_commands/ipban.cpp
gm_commands/iplookup.cpp
gm_commands/iteminfo.cpp
gm_commands/itemsearch.cpp
gm_commands/kick.cpp
gm_commands/kill.cpp
gm_commands/killallnpcs.cpp
gm_commands/lastname.cpp
gm_commands/list.cpp
gm_commands/listpetition.cpp
gm_commands/loc.cpp
gm_commands/lock.cpp
gm_commands/logcommand.cpp
gm_commands/logs.cpp
gm_commands/makepet.cpp
gm_commands/mana.cpp
gm_commands/max_all_skills.cpp
gm_commands/memspell.cpp
gm_commands/merchantcloseshop.cpp
gm_commands/merchantopenshop.cpp
gm_commands/modifynpcstat.cpp
gm_commands/motd.cpp
gm_commands/movechar.cpp
gm_commands/movement.cpp
gm_commands/myskills.cpp
gm_commands/mysql.cpp
gm_commands/mystats.cpp
gm_commands/name.cpp
gm_commands/netstats.cpp
gm_commands/network.cpp
gm_commands/npccast.cpp
gm_commands/npcedit.cpp
gm_commands/npceditmass.cpp
gm_commands/npcemote.cpp
gm_commands/npcloot.cpp
gm_commands/npcsay.cpp
gm_commands/npcshout.cpp
gm_commands/npcspawn.cpp
gm_commands/npcspecialattk.cpp
gm_commands/npcstats.cpp
gm_commands/npctype_cache.cpp
gm_commands/npctypespawn.cpp
gm_commands/nudge.cpp
gm_commands/nukebuffs.cpp
gm_commands/nukeitem.cpp
gm_commands/object.cpp
gm_commands/oocmute.cpp
gm_commands/opcode.cpp
gm_commands/path.cpp
gm_commands/peekinv.cpp
gm_commands/peqzone.cpp
gm_commands/permaclass.cpp
gm_commands/permagender.cpp
gm_commands/permarace.cpp
gm_commands/petitems.cpp
gm_commands/petitioninfo.cpp
gm_commands/petname.cpp
gm_commands/pf.cpp
gm_commands/picklock.cpp
gm_commands/profanity.cpp
gm_commands/proximity.cpp
gm_commands/push.cpp
gm_commands/pvp.cpp
gm_commands/qglobal.cpp
gm_commands/questerrors.cpp
gm_commands/race.cpp
gm_commands/raidloot.cpp
gm_commands/randomfeatures.cpp
gm_commands/refreshgroup.cpp
gm_commands/reloadaa.cpp
gm_commands/reloadallrules.cpp
gm_commands/reloadcontentflags.cpp
gm_commands/reloademote.cpp
gm_commands/reloadlevelmods.cpp
gm_commands/reloadmerchants.cpp
gm_commands/reloadperlexportsettings.cpp
gm_commands/reloadqst.cpp
gm_commands/reloadstatic.cpp
gm_commands/reloadtitles.cpp
gm_commands/reloadtraps.cpp
gm_commands/reloadworld.cpp
gm_commands/reloadworldrules.cpp
gm_commands/reloadzps.cpp
gm_commands/removeitem.cpp
gm_commands/repop.cpp
gm_commands/resetaa.cpp
gm_commands/resetaa_timer.cpp
gm_commands/resetdisc_timer.cpp
gm_commands/revoke.cpp
gm_commands/roambox.cpp
gm_commands/rules.cpp
gm_commands/save.cpp
gm_commands/scale.cpp
gm_commands/scribespell.cpp
gm_commands/scribespells.cpp
gm_commands/sendzonespawns.cpp
gm_commands/sensetrap.cpp
gm_commands/serverinfo.cpp
gm_commands/serverrules.cpp
gm_commands/set_adventure_points.cpp
gm_commands/setaapts.cpp
gm_commands/setaaxp.cpp
gm_commands/setaltcurrency.cpp
gm_commands/setanim.cpp
gm_commands/setcrystals.cpp
gm_commands/setendurance.cpp
gm_commands/setfaction.cpp
gm_commands/setgraveyard.cpp
gm_commands/sethp.cpp
gm_commands/setlanguage.cpp
gm_commands/setlsinfo.cpp
gm_commands/setmana.cpp
gm_commands/setpass.cpp
gm_commands/setpvppoints.cpp
gm_commands/setskill.cpp
gm_commands/setskillall.cpp
gm_commands/setstartzone.cpp
gm_commands/setstat.cpp
gm_commands/setxp.cpp
gm_commands/showbonusstats.cpp
gm_commands/showbuffs.cpp
gm_commands/shownpcgloballoot.cpp
gm_commands/shownumhits.cpp
gm_commands/showskills.cpp
gm_commands/showspellslist.cpp
gm_commands/showstats.cpp
gm_commands/showzonegloballoot.cpp
gm_commands/showzonepoints.cpp
gm_commands/shutdown.cpp
gm_commands/size.cpp
gm_commands/spawn.cpp
gm_commands/spawnfix.cpp
gm_commands/spawnstatus.cpp
gm_commands/spellinfo.cpp
gm_commands/stun.cpp
gm_commands/summon.cpp
gm_commands/summonburiedplayercorpse.cpp
gm_commands/summonitem.cpp
gm_commands/suspend.cpp
gm_commands/task.cpp
gm_commands/tattoo.cpp
gm_commands/tempname.cpp
gm_commands/texture.cpp
gm_commands/time.cpp
gm_commands/timers.cpp
gm_commands/timezone.cpp
gm_commands/title.cpp
gm_commands/titlesuffix.cpp
gm_commands/traindisc.cpp
gm_commands/trapinfo.cpp
gm_commands/tune.cpp
gm_commands/ucs.cpp
gm_commands/undye.cpp
gm_commands/undyeme.cpp
gm_commands/unfreeze.cpp
gm_commands/unlock.cpp
gm_commands/unmemspell.cpp
gm_commands/unmemspells.cpp
gm_commands/unscribespell.cpp
gm_commands/unscribespells.cpp
gm_commands/untraindisc.cpp
gm_commands/untraindiscs.cpp
gm_commands/uptime.cpp
gm_commands/version.cpp
gm_commands/viewcurrencies.cpp
gm_commands/viewnpctype.cpp
gm_commands/viewpetition.cpp
gm_commands/viewzoneloot.cpp
gm_commands/wc.cpp
gm_commands/weather.cpp
gm_commands/who.cpp
gm_commands/worldshutdown.cpp
gm_commands/worldwide.cpp
gm_commands/wp.cpp
gm_commands/wpadd.cpp
gm_commands/wpinfo.cpp
gm_commands/xtargets.cpp
gm_commands/zclip.cpp
gm_commands/zcolor.cpp
gm_commands/zheader.cpp
gm_commands/zonebootup.cpp
gm_commands/zonelock.cpp
gm_commands/zoneshutdown.cpp
gm_commands/zonestatus.cpp
gm_commands/zopp.cpp
gm_commands/zsafecoords.cpp
gm_commands/zsave.cpp
gm_commands/zsky.cpp
gm_commands/zstats.cpp
gm_commands/zunderworld.cpp
)
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers} ${gm_commands})
INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
+427 -181
View File
@@ -69,10 +69,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
for (int x = 0; x < MAX_SWARM_PETS; x++)
{
if (spells[spell_id].effectid[x] == SE_TemporaryPets)
if (spells[spell_id].effect_id[x] == SE_TemporaryPets)
{
pet.count = spells[spell_id].base[x];
pet.duration = spells[spell_id].max[x];
pet.count = spells[spell_id].base_value[x];
pet.duration = spells[spell_id].max_value[x];
}
}
@@ -151,10 +151,14 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
//give the pets somebody to "love"
if (targ != nullptr) {
swarm_pet_npc->AddToHateList(targ, 1000, 1000);
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg)
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) {
swarm_pet_npc->GetSwarmInfo()->target = targ->GetID();
else
swarm_pet_npc->SetPetTargetLockID(targ->GetID());
swarm_pet_npc->SetSpecialAbility(IMMUNE_AGGRO, 1);
}
else {
swarm_pet_npc->GetSwarmInfo()->target = 0;
}
}
//we allocated a new NPC type object, give the NPC ownership of that memory
@@ -213,7 +217,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
glm::vec2(5, 5), glm::vec2(-5, 5), glm::vec2(5, -5), glm::vec2(-5, -5),
glm::vec2(10, 10), glm::vec2(-10, 10), glm::vec2(10, -10), glm::vec2(-10, -10),
glm::vec2(8, 8), glm::vec2(-8, 8), glm::vec2(8, -8), glm::vec2(-8, -8)
};;
};
while(summon_count > 0) {
int pet_duration = pet.duration;
@@ -255,10 +259,14 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
if(targ != nullptr){
swarm_pet_npc->AddToHateList(targ, 1000, 1000);
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg)
if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) {
swarm_pet_npc->GetSwarmInfo()->target = targ->GetID();
else
swarm_pet_npc->SetPetTargetLockID(targ->GetID());
swarm_pet_npc->SetSpecialAbility(IMMUNE_AGGRO, 1);
}
else {
swarm_pet_npc->GetSwarmInfo()->target = 0;
}
}
//we allocated a new NPC type object, give the NPC ownership of that memory
@@ -273,63 +281,94 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
delete made_npc;
}
void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
{
Corpse *CorpseToUse = nullptr;
CorpseToUse = entity_list.GetClosestCorpse(this, nullptr);
void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *target, uint32 duration) {
if(!CorpseToUse)
/*
SPA 299 Wake The Dead, 'animateDead' should be temp pet, always spawns 1 pet from corpse, max value is duration
SPA 306 Wake The Dead, 'animateDead#' should be temp pet, base is amount of pets from indivual corpses, max value is duration
Max range for closet corpse is 250 units.
TODO: Should use temp pets
*/
if (!corpse_to_use) {
return;
}
//assuming we have pets in our table; we take the first pet as a base type.
const NPCType *base_type = content_db.LoadNPCTypesData(500);
auto make_npc = new NPCType;
memcpy(make_npc, base_type, sizeof(NPCType));
/* TODO: Does WTD use pet focus?
int act_power = 0;
//combat stats
make_npc->AC = ((GetLevel() * 7) + 550);
make_npc->ATK = GetLevel();
make_npc->max_dmg = (GetLevel() * 4) + 2;
make_npc->min_dmg = 1;
if (IsClient()) {
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
*/
//base stats
make_npc->current_hp = (GetLevel() * 55);
make_npc->max_hp = (GetLevel() * 55);
make_npc->STR = 85 + (GetLevel() * 3);
make_npc->STA = 85 + (GetLevel() * 3);
make_npc->DEX = 85 + (GetLevel() * 3);
make_npc->AGI = 85 + (GetLevel() * 3);
make_npc->INT = 85 + (GetLevel() * 3);
make_npc->WIS = 85 + (GetLevel() * 3);
make_npc->CHA = 85 + (GetLevel() * 3);
make_npc->MR = 25;
make_npc->FR = 25;
make_npc->CR = 25;
make_npc->DR = 25;
make_npc->PR = 25;
SwarmPet_Struct pet;
pet.count = 1;
pet.duration = 1;
//pet.duration += GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000; //TODO: Does WTD use pet focus?
pet.npc_id = WAKE_THE_DEAD_NPCTYPEID;
NPCType *made_npc = nullptr;
const NPCType *npc_type = content_db.LoadNPCTypesData(WAKE_THE_DEAD_NPCTYPEID);
if (npc_type == nullptr) {
//log write
LogError("Unknown npc type for 'Wake the Dead' swarm pet spell id: [{}]", spell_id);
Message(0, "Unable to find pet!");
return;
}
made_npc = new NPCType;
memcpy(made_npc, npc_type, sizeof(NPCType));
//level class and gender
make_npc->level = GetLevel();
make_npc->class_ = CorpseToUse->class_;
make_npc->race = CorpseToUse->race;
make_npc->gender = CorpseToUse->gender;
make_npc->loottable_id = 0;
//name
char NewName[64];
sprintf(NewName, "%s`s Animated Corpse", GetCleanName());
strcpy(make_npc->name, NewName);
strcpy(made_npc->name, NewName);
npc_type = made_npc;
//combat stats
made_npc->AC = ((GetLevel() * 7) + 550);
made_npc->ATK = GetLevel();
made_npc->max_dmg = (GetLevel() * 4) + 2;
made_npc->min_dmg = 1;
//base stats
made_npc->current_hp = (GetLevel() * 55);
made_npc->max_hp = (GetLevel() * 55);
made_npc->STR = 85 + (GetLevel() * 3);
made_npc->STA = 85 + (GetLevel() * 3);
made_npc->DEX = 85 + (GetLevel() * 3);
made_npc->AGI = 85 + (GetLevel() * 3);
made_npc->INT = 85 + (GetLevel() * 3);
made_npc->WIS = 85 + (GetLevel() * 3);
made_npc->CHA = 85 + (GetLevel() * 3);
made_npc->MR = 25;
made_npc->FR = 25;
made_npc->CR = 25;
made_npc->DR = 25;
made_npc->PR = 25;
//level class and gender
made_npc->level = GetLevel();
made_npc->class_ = corpse_to_use->class_;
made_npc->race = corpse_to_use->race;
made_npc->gender = corpse_to_use->gender;
made_npc->loottable_id = 0;
//appearance
make_npc->beard = CorpseToUse->beard;
make_npc->beardcolor = CorpseToUse->beardcolor;
make_npc->eyecolor1 = CorpseToUse->eyecolor1;
make_npc->eyecolor2 = CorpseToUse->eyecolor2;
make_npc->haircolor = CorpseToUse->haircolor;
make_npc->hairstyle = CorpseToUse->hairstyle;
make_npc->helmtexture = CorpseToUse->helmtexture;
make_npc->luclinface = CorpseToUse->luclinface;
make_npc->size = CorpseToUse->size;
make_npc->texture = CorpseToUse->texture;
made_npc->beard = corpse_to_use->beard;
made_npc->beardcolor = corpse_to_use->beardcolor;
made_npc->eyecolor1 = corpse_to_use->eyecolor1;
made_npc->eyecolor2 = corpse_to_use->eyecolor2;
made_npc->haircolor = corpse_to_use->haircolor;
made_npc->hairstyle = corpse_to_use->hairstyle;
made_npc->helmtexture = corpse_to_use->helmtexture;
made_npc->luclinface = corpse_to_use->luclinface;
made_npc->size = corpse_to_use->size;
made_npc->texture = corpse_to_use->texture;
//cast stuff.. based off of PEQ's if you want to change
//it you'll have to mod this code, but most likely
@@ -337,130 +376,144 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
//part of their spell list; can't think of any smooth
//way to do this
//some basic combat mods here too since it's convienent
switch(CorpseToUse->class_)
switch (corpse_to_use->class_)
{
case CLERIC:
make_npc->npc_spells_id = 1;
made_npc->npc_spells_id = 1;
break;
case WIZARD:
make_npc->npc_spells_id = 2;
made_npc->npc_spells_id = 2;
break;
case NECROMANCER:
make_npc->npc_spells_id = 3;
made_npc->npc_spells_id = 3;
break;
case MAGICIAN:
make_npc->npc_spells_id = 4;
made_npc->npc_spells_id = 4;
break;
case ENCHANTER:
make_npc->npc_spells_id = 5;
made_npc->npc_spells_id = 5;
break;
case SHAMAN:
make_npc->npc_spells_id = 6;
made_npc->npc_spells_id = 6;
break;
case DRUID:
make_npc->npc_spells_id = 7;
made_npc->npc_spells_id = 7;
break;
case PALADIN:
//SPECATK_TRIPLE
strcpy(make_npc->special_abilities, "6,1");
make_npc->current_hp = make_npc->current_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 8;
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 150 / 100;
made_npc->max_hp = made_npc->max_hp * 150 / 100;
made_npc->npc_spells_id = 8;
break;
case SHADOWKNIGHT:
strcpy(make_npc->special_abilities, "6,1");
make_npc->current_hp = make_npc->current_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 9;
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 150 / 100;
made_npc->max_hp = made_npc->max_hp * 150 / 100;
made_npc->npc_spells_id = 9;
break;
case RANGER:
strcpy(make_npc->special_abilities, "7,1");
make_npc->current_hp = make_npc->current_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
make_npc->npc_spells_id = 10;
strcpy(made_npc->special_abilities, "7,1");
made_npc->current_hp = made_npc->current_hp * 135 / 100;
made_npc->max_hp = made_npc->max_hp * 135 / 100;
made_npc->npc_spells_id = 10;
break;
case BARD:
strcpy(make_npc->special_abilities, "6,1");
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 11;
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
made_npc->npc_spells_id = 11;
break;
case BEASTLORD:
strcpy(make_npc->special_abilities, "7,1");
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 12;
strcpy(made_npc->special_abilities, "7,1");
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
made_npc->npc_spells_id = 12;
break;
case ROGUE:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
break;
case MONK:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->current_hp = make_npc->current_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 135 / 100;
made_npc->max_hp = made_npc->max_hp * 135 / 100;
break;
case WARRIOR:
case BERSERKER:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->current_hp = make_npc->current_hp * 175 / 100;
make_npc->max_hp = make_npc->max_hp * 175 / 100;
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 175 / 100;
made_npc->max_hp = made_npc->max_hp * 175 / 100;
break;
default:
make_npc->npc_spells_id = 0;
made_npc->npc_spells_id = 0;
break;
}
make_npc->loottable_id = 0;
make_npc->merchanttype = 0;
make_npc->d_melee_texture1 = 0;
make_npc->d_melee_texture2 = 0;
made_npc->loottable_id = 0;
made_npc->merchanttype = 0;
made_npc->d_melee_texture1 = 0;
made_npc->d_melee_texture2 = 0;
auto npca = new NPC(make_npc, 0, GetPosition(), GravityBehavior::Water);
if(!npca->GetSwarmInfo()){
auto nSI = new SwarmPet;
npca->SetSwarmInfo(nSI);
npca->GetSwarmInfo()->duration = new Timer(duration*1000);
}
else{
npca->GetSwarmInfo()->duration->Start(duration*1000);
}
int summon_count = 0;
summon_count = pet.count;
npca->StartSwarmTimer(duration * 1000);
npca->GetSwarmInfo()->owner_id = GetID();
NPC* swarm_pet_npc = nullptr;
//TODO: potenitally add support for multiple pets per corpse
while (summon_count > 0) {
int pet_duration = duration;
//give the pet somebody to "love"
if(target != nullptr){
npca->AddToHateList(target, 100000);
npca->GetSwarmInfo()->target = target->GetID();
}
//gear stuff, need to make sure there's
//no situation where this stuff can be duped
for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++)
{
uint32 sitem = 0;
sitem = CorpseToUse->GetWornItem(x);
if(sitem){
const EQ::ItemData * itm = database.GetItem(sitem);
npca->AddLootDrop(itm, &npca->itemlist, NPC::NewLootDropEntry(), true);
NPCType *npc_dup = nullptr;
if (made_npc != nullptr) {
npc_dup = new NPCType;
memcpy(npc_dup, made_npc, sizeof(NPCType));
}
swarm_pet_npc = new NPC(
(npc_dup != nullptr) ? npc_dup : npc_type,
0, corpse_to_use->GetPosition(),GravityBehavior::Water);
swarm_pet_npc->SetFollowID(GetID());
if (!swarm_pet_npc->GetSwarmInfo()) {
auto nSI = new SwarmPet;
swarm_pet_npc->SetSwarmInfo(nSI);
swarm_pet_npc->GetSwarmInfo()->duration = new Timer(pet_duration * 1000);
}
else {
swarm_pet_npc->GetSwarmInfo()->duration->Start(pet_duration * 1000);
}
swarm_pet_npc->StartSwarmTimer(pet_duration * 1000);
//removing this prevents the pet from attacking
swarm_pet_npc->GetSwarmInfo()->owner_id = GetID();
//give the pets somebody to "love"
if (target != nullptr) {
swarm_pet_npc->AddToHateList(target, 10000, 1000);
swarm_pet_npc->GetSwarmInfo()->target = 0;
}
//we allocated a new NPC type object, give the NPC ownership of that memory
if (npc_dup != nullptr)
swarm_pet_npc->GiveNPCTypeData(npc_dup);
entity_list.AddNPC(swarm_pet_npc, true, true);
summon_count--;
}
//we allocated a new NPC type object, give the NPC ownership of that memory
if(make_npc != nullptr)
npca->GiveNPCTypeData(make_npc);
entity_list.AddNPC(npca, true, true);
//the target of these swarm pets will take offense to being cast on...
if(target != nullptr)
if (target != nullptr)
target->AddToHateList(this, 1, 0);
// The other pointers we make are handled elsewhere.
delete made_npc;
}
void Client::ResetAA() {
@@ -492,15 +545,26 @@ void Client::ResetAA() {
m_pp.raid_leadership_points = 0;
m_pp.group_leadership_exp = 0;
m_pp.raid_leadership_exp = 0;
database.DeleteCharacterAAs(CharacterID());
database.DeleteCharacterLeadershipAAs(CharacterID());
}
void Client::SendClearAA()
{
auto outapp = new EQApplicationPacket(OP_ClearLeadershipAbilities, 0);
SendClearLeadershipAA();
SendClearPlayerAA();
}
void Client::SendClearPlayerAA()
{
auto outapp = new EQApplicationPacket(OP_ClearAA, 0);
FastQueuePacket(&outapp);
outapp = new EQApplicationPacket(OP_ClearAA, 0);
}
void Client::SendClearLeadershipAA()
{
auto outapp = new EQApplicationPacket(OP_ClearLeadershipAbilities, 0);
FastQueuePacket(&outapp);
}
@@ -764,7 +828,7 @@ void Client::InspectBuffs(Client* Inspector, int Rank)
continue;
ib->spell_id[packet_index] = buffs[i].spellid;
if (Rank > 1)
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buff_duration_formula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
packet_index++;
}
@@ -904,8 +968,8 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
outapp->SetWritePosition(sizeof(AARankInfo_Struct));
for(auto &effect : rank->effects) {
outapp->WriteSInt32(effect.effect_id);
outapp->WriteSInt32(effect.base1);
outapp->WriteSInt32(effect.base2);
outapp->WriteSInt32(effect.base_value);
outapp->WriteSInt32(effect.limit_value);
outapp->WriteSInt32(effect.slot);
}
@@ -1162,25 +1226,28 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) {
void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
if(!rank) {
if (!rank) {
return;
}
AA::Ability *ability = rank->base_ability;
if(!ability) {
if (!ability) {
return;
}
if(!IsValidSpell(rank->spell)) {
if (!IsValidSpell(rank->spell)) {
return;
}
if(!CanUseAlternateAdvancementRank(rank)) {
if (!CanUseAlternateAdvancementRank(rank)) {
return;
}
bool use_toggle_passive_hotkey = UseTogglePassiveHotkey(*rank);
//make sure it is not a passive
if(!rank->effects.empty()) {
if (!rank->effects.empty() && !use_toggle_passive_hotkey) {
return;
}
@@ -1188,34 +1255,32 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
// We don't have the AA
if (!GetAA(rank_id, &charges))
return;
//if expendable make sure we have charges
if(ability->charges > 0 && charges < 1)
if (ability->charges > 0 && charges < 1)
return;
//check cooldown
if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) {
if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) {
uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart);
uint32 aaremain_hr = aaremain / (60 * 60);
uint32 aaremain_min = (aaremain / 60) % 60;
uint32 aaremain_sec = aaremain % 60;
if(aaremain_hr >= 1) {
if (aaremain_hr >= 1) {
Message(Chat::Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
aaremain_hr, aaremain_min, aaremain_sec);
aaremain_hr, aaremain_min, aaremain_sec);
}
else {
Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds",
aaremain_min, aaremain_sec);
aaremain_min, aaremain_sec);
}
return;
}
//calculate cooldown
int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
if(cooldown < 0) {
cooldown = 0;
int timer_duration = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
if (timer_duration < 0) {
timer_duration = 0;
}
if (!IsCastWhileInvis(rank->spell))
@@ -1227,11 +1292,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
}
//
// Modern clients don't require pet targeted for AA casts that are ST_Pet
if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet)
target_id = GetPetID();
// extra handling for cast_not_standing spells
if (!spells[rank->spell].cast_not_standing) {
if (!IgnoreCastingRestriction(rank->spell)) {
if (GetAppearance() == eaSitting) // we need to stand!
SetAppearance(eaStanding, false);
@@ -1241,20 +1306,27 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
}
}
// Bards can cast instant cast AAs while they are casting another song
if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
return;
if (use_toggle_passive_hotkey) {
TogglePassiveAlternativeAdvancement(*rank, ability->id);
}
else {
// Bards can cast instant cast AAs while they are casting or channeling item cast.
if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) {
if (!DoCastingChecksOnCaster(rank->spell)) {
return;
}
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1,
rank->spell_type + pTimerAAStart, timer_duration, false, rank->id)) {
return;
}
}
ExpendAlternateAdvancementCharge(ability->id);
} else {
if(!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
return;
else {
if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, timer_duration, nullptr, rank->id)) {
return;
}
}
}
CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown);
SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
@@ -1267,6 +1339,7 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
return 0;
}
int total_reduction = 0;
for(auto &aa : aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first);
auto ability = ability_rank.first;
@@ -1277,26 +1350,26 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
}
for(auto &effect : rank->effects) {
if(effect.effect_id == SE_HastenedAASkill && effect.base2 == ability_in->id) {
return effect.base1;
if(effect.effect_id == SE_HastenedAASkill && effect.limit_value == ability_in->id) {
total_reduction += effect.base_value;
}
}
}
return 0;
return total_reduction;
}
void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
for(auto &iter : aa_ranks) {
for (auto &iter : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);
if(ability && aa_id == ability->id) {
if(iter.second.second > 0) {
if (ability && aa_id == ability->id) {
if (iter.second.second > 0) {
iter.second.second -= 1;
if(iter.second.second == 0) {
if(IsClient()) {
if (iter.second.second == 0) {
if (IsClient()) {
AA::Rank *r = ability->GetRankByPointsSpent(iter.second.first);
if(r) {
if (r) {
CastToClient()->GetEPP().expended_aa += r->cost;
}
}
@@ -1307,7 +1380,7 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
aa_ranks.erase(iter.first);
}
if(IsClient()) {
if (IsClient()) {
Client *c = CastToClient();
c->SaveAA();
c->SendAlternateAdvancementPoints();
@@ -1691,11 +1764,18 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
}
LogInfo("Loaded [{}] Alternate Advancement Abilities", (int)abilities.size());
int expansion = RuleI(Expansion, CurrentExpansion);
bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly);
LogInfo("Loading Alternate Advancement Ability Ranks");
ranks.clear();
query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
if (use_expansion_aa && expansion >= 0) {
query = fmt::format("SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
"next_id, expansion FROM aa_ranks WHERE expansion <= {}", expansion);
} else {
query = "SELECT id, upper_hotkey_sid, lower_hotkey_sid, title_sid, desc_sid, cost, level_req, spell, spell_type, recast_time, "
"next_id, expansion FROM aa_ranks";
}
results = QueryDatabase(query);
if(results.Success()) {
for(auto row = results.begin(); row != results.end(); ++row) {
@@ -1736,8 +1816,8 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
int rank_id = atoi(row[0]);
effect.slot = atoi(row[1]);
effect.effect_id = atoi(row[2]);
effect.base1 = atoi(row[3]);
effect.base2 = atoi(row[4]);
effect.base_value = atoi(row[3]);
effect.limit_value = atoi(row[4]);
if(effect.slot < 1)
continue;
@@ -1796,3 +1876,169 @@ bool Mob::CheckAATimer(int timer)
}
return false;
}
void Client::TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id)
{
/*
Certain AA, like Weapon Stance line use a special toggle Hotkey to enable or disable the AA's passive abilities.
This is occurs by doing the following. Each 'rank' of Weapon Stance is actually 2 actual ranks.
First rank is always the Disabled version which cost X amount of AA. Second rank is the Enabled version which cost 0 AA.
When you buy the first rank, you make a hotkey that on live say 'Weapon Stance Disabled', if you clik that it then BUYS the
next rank of AA (cost 0) which switches the hotkey to 'Enabled Weapon Stance' and you are given the passive buff effects.
If you click the Enabled hotkey, it causes you to lose an AA rank and once again be disabled. Thus, you are switching between
two AA ranks. Thefore when creating an AA using this ability, you need generate both ranks. Follow the same pattern for additional ranks.
IMPORTANT! The toggle system can be used to Enable or Disable ANY passive AA. You just need to follow the instructions on how to create it.
Example: Enable or Disable a buff that gives a large hate modifier. Play may Enable when tanking and Disable when DPS ect.
Note: On live the Enabled rank is shown having a Charge of 1, while Disabled rank has no charges. Our current code doesn't support that. Do not use charges.
Note: Live uses a spell 'Disable Ability' ID 46164 to trigger a script to do the AA rank changes. At present time it is not coded to require that, any spell id works.
Note: Discovered a bug on ROF2, where when you buy first rank of an AA with a hotkey, it will always display the title of the second rank in the database. Be aware. No easy fix.
Dev Note(Kayen 8/1/21): The system as set up is very similar to live, with exception that live gives the Enabled rank 1 Charge. The code here emulates what happens when a
charge would be expended.
Instructions for how to make the AA - assuming a basic level of knowledge of how AA's work.
- aa_abilities table : Create new ability with a hotkey, type 3, zero charges
- aa_ranks table : [Disabled rank] First rank, should have a cost > 0 (this is what you buy), Set hotkeys, MUST SET A SPELL CONTAINING EFFECT SE_Buy_AA_Rank(SPA 472), set a short recast timer.
[Enabled rank] Second rank, should have a cost = 0, Set hotkeys, Set any valid spell ID you want (it has to exist but does nothing), set a short recast timer.
*Recommend if doing custom, just make the hotkey titled 'Toggle <Ability Name>' and use for both.
- aa_rank_effects table : [Disabled rank] No data needed in the aa_ranks_effect table
[Enabled rank] Second rank set effect_id = 457 (weapon stance), slot 1,2,3, base1= spell triggers, base= weapon type (0=2H,1=SH,2=DW), for slot 1,2,3
Example SQL -Disabled
DO NOT ADD any data to the aa_rank_effects for this rank_id
-Enabled
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 1, 476, 145,0);
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 2, 476, 174,1);
INSERT INTO aa_rank_effects (rank_id, slot, effect_id, base1, base2) VALUES (20003, 3, 476, 172,2);
Warning: If you want to design an AA that only uses one weapon type to trigger, like will only apply buff if Shield. Do not include data for other types. Never have a base value=0
in the Enabled rank.
*/
bool enable_next_rank = IsEffectInSpell(rank.spell, SE_Buy_AA_Rank);
if (enable_next_rank) {
//Enable
TogglePurchaseAlternativeAdvancementRank(rank.next_id);
Message(Chat::Spells, "You enable an ability."); //Message live gives you. Should come from spell.
AA::Rank *rank_next = zone->GetAlternateAdvancementRank(rank.next_id);
//Add checks for any special cases for toggle.
if (IsEffectinAlternateAdvancementRankEffects(*rank_next, SE_Weapon_Stance)) {
weaponstance.aabonus_enabled = true;
ApplyWeaponsStance();
}
return;
}
else {
//Disable
ResetAlternateAdvancementRank(ability_id);
TogglePurchaseAlternativeAdvancementRank(rank.prev_id);
Message(Chat::Spells, "You disable an ability."); //Message live gives you. Should come from spell.
//Add checks for any special cases for toggle.
if (IsEffectinAlternateAdvancementRankEffects(rank, SE_Weapon_Stance)) {
weaponstance.aabonus_enabled = false;
BuffFadeBySpellID(weaponstance.aabonus_buff_spell_id);
}
return;
}
}
bool Client::UseTogglePassiveHotkey(const AA::Rank &rank) {
/*
Disabled rank needs a rank spell containing the SE_Buy_AA_Rank effect to return true.
Enabled rank checks to see if the prior rank contains a rank spell with SE_Buy_AA_Rank, if so true.
Note: On live the enabled rank is Expendable with Charge 1.
We have already confirmed the rank spell is valid before this function is called.
*/
if (IsEffectInSpell(rank.spell, SE_Buy_AA_Rank)) {//Checked when is Disabled.
return true;
}
else if (rank.prev_id != -1) {//Check when effect is Enabled.
AA::Rank *rank_prev = zone->GetAlternateAdvancementRank(rank.prev_id);
if (IsEffectInSpell(rank_prev->spell, SE_Buy_AA_Rank)) {
return true;
}
}
return false;
}
bool Client::IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id) {
for (const auto &e : rank.effects) {
if (e.effect_id == effect_id) {
return true;
}
}
return false;
}
void Client::ResetAlternateAdvancementRank(uint32 aa_id) {
/*
Resets your AA to baseline
*/
for(auto &iter : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);
if(ability && aa_id == ability->id) {
RemoveExpendedAA(ability->first_rank_id);
aa_ranks.erase(iter.first);
SaveAA();
SendAlternateAdvancementPoints();
return;
}
}
}
void Client::TogglePurchaseAlternativeAdvancementRank(int rank_id){
/*
Stripped down version of purchasing AA. Will give no messages.
Used with toggle hotkey functions.
*/
AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
if (!rank) {
return;
}
if (!rank->base_ability) {
return;
}
if (!CanPurchaseAlternateAdvancementRank(rank, false, false)) {
return;
}
rank_id = rank->base_ability->first_rank_id;
SetAA(rank_id, rank->current_value, 0);
if (rank->next) {
SendAlternateAdvancementRank(rank->base_ability->id, rank->next->current_value);
}
SaveAA();
SendAlternateAdvancementPoints();
SendAlternateAdvancementStats();
CalcBonuses();
}
+1
View File
@@ -2,6 +2,7 @@
#define AA_H
#define MAX_SWARM_PETS 12 //this can change as long as you make more coords (swarm_pet_x/swarm_pet_y)
#define WAKE_THE_DEAD_NPCTYPEID 500 //We use first pet in pets table as a template
typedef enum {
aaActionNone = 0,
+2 -2
View File
@@ -29,8 +29,8 @@ struct RankEffect
{
int slot;
int effect_id;
int base1;
int base2;
int base_value;
int limit_value;
};
}
+476 -256
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -436,7 +436,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection,
row["cwp"] = mob->GetCWP();
row["cwpp"] = mob->GetCWPP();
row["divine_aura"] = mob->DivineAura();
row["do_casting_checks"] = mob->DoCastingChecks();
row["dont_buff_me_before"] = mob->DontBuffMeBefore();
row["dont_cure_me_before"] = mob->DontCureMeBefore();
row["dont_dot_me_before"] = mob->DontDotMeBefore();
@@ -478,7 +477,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection,
row["has_temp_pets_active"] = mob->HasTempPetsActive();
row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
row["has_virus"] = mob->HasVirus();
row["hate_summon"] = mob->HateSummon();
row["helm_texture"] = mob->GetHelmTexture();
row["hp"] = mob->GetHP();
+826 -325
View File
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -98,7 +98,7 @@ bool Beacon::Process()
{
// NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
// I don't think any other cases that get here matter
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster;
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].target_type != ST_AECaster;
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
}
else
@@ -127,10 +127,10 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj
caster_id = caster->GetID();
spell_id = cast_spell_id;
this->resist_adjust = resist_adjust;
spell_iterations = spells[spell_id].AEDuration / 2500;
spell_iterations = spells[spell_id].aoe_duration / 2500;
spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1
if (spells[spell_id].aemaxtargets)
max_targets = spells[spell_id].aemaxtargets;
if (spells[spell_id].aoe_max_targets)
max_targets = spells[spell_id].aoe_max_targets;
spell_timer.Start(2500);
spell_timer.Trigger();
}
+1876 -1091
View File
File diff suppressed because it is too large Load Diff
+250 -533
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -359,8 +359,6 @@ public:
static uint32 SpawnedBotCount(uint32 botOwnerCharacterID);
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage);
static std::string ClassIdToString(uint16 classId);
static std::string RaceIdToString(uint16 raceId);
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName);
static void ProcessBotGroupInvite(Client* c, std::string botName);
+258 -179
View File
@@ -130,11 +130,11 @@ public:
for (int spell_id = 2; spell_id < SPDAT_RECORDS; ++spell_id) {
if (spells[spell_id].player_1[0] == '\0')
continue;
if (spells[spell_id].targettype != ST_Target && spells[spell_id].CastRestriction != 0) // watch
if (spells[spell_id].target_type != ST_Target && spells[spell_id].cast_restriction != 0) // watch
continue;
auto target_type = BCEnum::TT_None;
switch (spells[spell_id].targettype) {
switch (spells[spell_id].target_type) {
case ST_GroupTeleport:
target_type = BCEnum::TT_GroupV1;
break;
@@ -147,7 +147,7 @@ public:
//target_type = BCEnum::TT_AEBard;
break;
case ST_Target:
switch (spells[spell_id].CastRestriction) {
switch (spells[spell_id].cast_restriction) {
case 0:
target_type = BCEnum::TT_Single;
break;
@@ -213,15 +213,15 @@ public:
STBaseEntry* entry_prototype = nullptr;
while (true) {
switch (spells[spell_id].effectid[EFFECTIDTOINDEX(1)]) {
switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(1)]) {
case SE_BindAffinity:
entry_prototype = new STBaseEntry(BCEnum::SpT_BindAffinity);
break;
case SE_Charm:
if (spells[spell_id].SpellAffectIndex != 12)
if (spells[spell_id].spell_affect_index != 12)
break;
entry_prototype = new STCharmEntry();
if (spells[spell_id].ResistDiff <= -1000)
if (spells[spell_id].resist_difficulty <= -1000)
entry_prototype->SafeCastToCharm()->dire = true;
break;
case SE_Teleport:
@@ -248,11 +248,11 @@ public:
}
break;
case SE_ModelSize:
if (spells[spell_id].base[EFFECTIDTOINDEX(1)] > 100) {
if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 100) {
entry_prototype = new STSizeEntry;
entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Enlarge;
}
else if (spells[spell_id].base[EFFECTIDTOINDEX(1)] > 0 && spells[spell_id].base[EFFECTIDTOINDEX(1)] < 100) {
else if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 0 && spells[spell_id].base_value[EFFECTIDTOINDEX(1)] < 100) {
entry_prototype = new STSizeEntry;
entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Reduce;
}
@@ -261,42 +261,42 @@ public:
entry_prototype = new STBaseEntry(BCEnum::SpT_Identify);
break;
case SE_Invisibility:
if (spells[spell_id].SpellAffectIndex != 9)
if (spells[spell_id].spell_affect_index != 9)
break;
entry_prototype = new STInvisibilityEntry;
entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Living;
break;
case SE_SeeInvis:
if (spells[spell_id].SpellAffectIndex != 5)
if (spells[spell_id].spell_affect_index != 5)
break;
entry_prototype = new STInvisibilityEntry;
entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_See;
break;
case SE_InvisVsUndead:
if (spells[spell_id].SpellAffectIndex != 9)
if (spells[spell_id].spell_affect_index != 9)
break;
entry_prototype = new STInvisibilityEntry;
entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Undead;
break;
case SE_InvisVsAnimals:
if (spells[spell_id].SpellAffectIndex != 9)
if (spells[spell_id].spell_affect_index != 9)
break;
entry_prototype = new STInvisibilityEntry;
entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Animal;
break;
case SE_Mez:
if (spells[spell_id].SpellAffectIndex != 12)
if (spells[spell_id].spell_affect_index != 12)
break;
entry_prototype = new STBaseEntry(BCEnum::SpT_Mesmerize);
break;
case SE_Revive:
if (spells[spell_id].SpellAffectIndex != 1)
if (spells[spell_id].spell_affect_index != 1)
break;
entry_prototype = new STResurrectEntry();
entry_prototype->SafeCastToResurrect()->aoe = BCSpells::IsCasterCentered(target_type);
break;
case SE_Rune:
if (spells[spell_id].SpellAffectIndex != 2)
if (spells[spell_id].spell_affect_index != 2)
break;
entry_prototype = new STBaseEntry(BCEnum::SpT_Rune);
break;
@@ -312,7 +312,7 @@ public:
if (entry_prototype)
break;
switch (spells[spell_id].effectid[EFFECTIDTOINDEX(2)]) {
switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(2)]) {
case SE_Succor:
entry_prototype = new STEscapeEntry;
std::string is_lesser = spells[spell_id].name;
@@ -323,7 +323,7 @@ public:
if (entry_prototype)
break;
switch (spells[spell_id].effectid[EFFECTIDTOINDEX(3)]) {
switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(3)]) {
case SE_Lull:
entry_prototype = new STBaseEntry(BCEnum::SpT_Lull);
break;
@@ -336,8 +336,8 @@ public:
if (entry_prototype)
break;
while (spells[spell_id].typedescnum == 27) {
if (!spells[spell_id].goodEffect)
while (spells[spell_id].type_description_id == 27) {
if (!spells[spell_id].good_effect)
break;
if (spells[spell_id].skill != EQ::skills::SkillOffense && spells[spell_id].skill != EQ::skills::SkillDefense)
break;
@@ -353,38 +353,38 @@ public:
if (entry_prototype)
break;
switch (spells[spell_id].SpellAffectIndex) {
switch (spells[spell_id].spell_affect_index) {
case 1: {
bool valid_spell = false;
entry_prototype = new STCureEntry;
for (int i = EffectIDFirst; i <= EffectIDLast; ++i) {
int effect_index = EFFECTIDTOINDEX(i);
if (spells[spell_id].effectid[effect_index] != SE_Blind && spells[spell_id].base[effect_index] >= 0)
if (spells[spell_id].effect_id[effect_index] != SE_Blind && spells[spell_id].base_value[effect_index] >= 0)
continue;
else if (spells[spell_id].effectid[effect_index] == SE_Blind && !spells[spell_id].goodEffect)
else if (spells[spell_id].effect_id[effect_index] == SE_Blind && !spells[spell_id].good_effect)
continue;
switch (spells[spell_id].effectid[effect_index]) {
switch (spells[spell_id].effect_id[effect_index]) {
case SE_Blind:
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Blindness)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Blindness)] += spells[spell_id].base_value[effect_index];
break;
case SE_DiseaseCounter:
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Disease)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Disease)] += spells[spell_id].base_value[effect_index];
break;
case SE_PoisonCounter:
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Poison)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Poison)] += spells[spell_id].base_value[effect_index];
break;
case SE_CurseCounter:
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Curse)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Curse)] += spells[spell_id].base_value[effect_index];
break;
case SE_CorruptionCounter:
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Corruption)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX(BCEnum::AT_Corruption)] += spells[spell_id].base_value[effect_index];
break;
default:
continue;
}
entry_prototype->SafeCastToCure()->cure_total += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToCure()->cure_total += spells[spell_id].base_value[effect_index];
valid_spell = true;
}
if (!valid_spell) {
@@ -400,32 +400,32 @@ public:
for (int i = EffectIDFirst; i <= EffectIDLast; ++i) {
int effect_index = EFFECTIDTOINDEX(i);
if (spells[spell_id].base[effect_index] <= 0)
if (spells[spell_id].base_value[effect_index] <= 0)
continue;
switch (spells[spell_id].effectid[effect_index]) {
switch (spells[spell_id].effect_id[effect_index]) {
case SE_ResistFire:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Fire)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Fire)] += spells[spell_id].base_value[effect_index];
break;
case SE_ResistCold:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Cold)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Cold)] += spells[spell_id].base_value[effect_index];
break;
case SE_ResistPoison:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Poison)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Poison)] += spells[spell_id].base_value[effect_index];
break;
case SE_ResistDisease:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Disease)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Disease)] += spells[spell_id].base_value[effect_index];
break;
case SE_ResistMagic:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Magic)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Magic)] += spells[spell_id].base_value[effect_index];
break;
case SE_ResistCorruption:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Corruption)] += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(BCEnum::RT_Corruption)] += spells[spell_id].base_value[effect_index];
break;
default:
continue;
}
entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].base[effect_index];
entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].base_value[effect_index];
valid_spell = true;
}
if (!valid_spell) {
@@ -437,7 +437,7 @@ public:
}
case 7:
case 10:
if (spells[spell_id].effectdescnum != 65)
if (spells[spell_id].effect_description_id != 65)
break;
if (IsEffectInSpell(spell_id, SE_NegateIfCombat))
break;
@@ -622,18 +622,18 @@ private:
if (RuleI(Bots, CommandSpellRank) == 1) {
spells_list->sort([](STBaseEntry* l, STBaseEntry* r) {
if (spells[l->spell_id].spellgroup < spells[r->spell_id].spellgroup)
if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group)
return true;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class < r->caster_class)
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class)
return true;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank)
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank)
return true;
return false;
});
spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) {
std::string r_name = spells[r->spell_id].name;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) {
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) {
removed_spells_list->push_back(r);
return true;
}
@@ -673,18 +673,18 @@ private:
// needs rework
if (RuleI(Bots, CommandSpellRank) == 2 || RuleI(Bots, CommandSpellRank) == 3) {
spells_list->sort([](STBaseEntry* l, STBaseEntry* r) {
if (spells[l->spell_id].spellgroup < spells[r->spell_id].spellgroup)
if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group)
return true;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class < r->caster_class)
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class)
return true;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank)
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank)
return true;
return false;
});
spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) {
std::string l_name = spells[l->spell_id].name;
if (spells[l->spell_id].spellgroup == spells[r->spell_id].spellgroup && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) {
if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) {
removed_spells_list->push_back(r);
return true;
}
@@ -786,15 +786,15 @@ private:
continue;
case BCEnum::SpT_Charm:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (LT_SPELLS(l, r, ResistDiff))
if (LT_SPELLS(l, r, resist_difficulty))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type))
if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && LT_STBASE(l, r, spell_level))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -883,11 +883,11 @@ private:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (LT_STBASE(l, r, target_type))
return true;
if (EQ_STBASE(l, r, target_type) && LT_SPELLS(l, r, zonetype))
if (EQ_STBASE(l, r, target_type) && LT_SPELLS(l, r, zone_type))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zonetype) && GT_STBASE(l, r, spell_level))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && GT_STBASE(l, r, spell_level))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zonetype) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -895,15 +895,15 @@ private:
continue;
case BCEnum::SpT_Lull:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (LT_SPELLS(l, r, ResistDiff))
if (LT_SPELLS(l, r, resist_difficulty))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type))
if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 3))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 3))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 3) && LT_STBASE(l, r, spell_level))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 3) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -911,15 +911,15 @@ private:
continue;
case BCEnum::SpT_Mesmerize:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (GT_SPELLS(l, r, ResistDiff))
if (GT_SPELLS(l, r, resist_difficulty))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && LT_STBASE(l, r, target_type))
if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && GT_STBASE(l, r, spell_level))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && GT_STBASE(l, r, spell_level))
return true;
if (EQ_SPELLS(l, r, ResistDiff) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -929,11 +929,11 @@ private:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (LT_STBASE(l, r, target_type))
return true;
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base, 2))
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 2))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 2) && LT_STBASE(l, r, spell_level))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 2) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -951,13 +951,13 @@ private:
continue;
case BCEnum::SpT_Resurrect:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (GT_SPELLS_EFFECT_ID(l, r, base, 1))
if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1))
return true;
if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && LT_STBASE(l, r, target_type))
if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, target_type))
return true;
if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level))
if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -967,11 +967,11 @@ private:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (LT_STBASE(l, r, target_type))
return true;
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max, 1))
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && LT_STBASE(l, r, spell_level))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
return false;
@@ -999,19 +999,19 @@ private:
if (l_size_type < r_size_type)
return true;
if (l_size_type == BCEnum::SzT_Enlarge && r_size_type == BCEnum::SzT_Enlarge) {
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base, 1))
if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 1))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && GT_STBASE(l, r, spell_level))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
}
if (l_size_type == BCEnum::SzT_Reduce && r_size_type == BCEnum::SzT_Reduce) {
if (EQ_STBASE(l, r, target_type) && LT_SPELLS_EFFECT_ID(l, r, base, 1))
if (EQ_STBASE(l, r, target_type) && LT_SPELLS_EFFECT_ID(l, r, base_value, 1))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && GT_STBASE(l, r, spell_level))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level))
return true;
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class))
return true;
}
@@ -1028,11 +1028,11 @@ private:
continue;
case BCEnum::SpT_SummonCorpse:
spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) {
if (GT_SPELLS_EFFECT_ID(l, r, base, 1))
if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1))
return true;
if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && LT_STBASE(l, r, spell_level))
if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, spell_level))
return true;
if (EQ_SPELLS_EFFECT_ID(l, r, base, 1) && EQ_STBASE(l, r, spell_level) && EQ_STBASE(l, r, caster_class))
if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && EQ_STBASE(l, r, caster_class))
return true;
return false;
@@ -1119,7 +1119,7 @@ private:
for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) {
if (levels_iter->second < test_iter->second)
test_iter = levels_iter;
if (strcasecmp(Bot::ClassIdToString(levels_iter->first).c_str(), Bot::ClassIdToString(test_iter->first).c_str()) < 0 && levels_iter->second <= test_iter->second)
if (strcasecmp(GetClassIDName(levels_iter->first), GetClassIDName(test_iter->first)) < 0 && levels_iter->second <= test_iter->second)
test_iter = levels_iter;
}
@@ -1131,8 +1131,8 @@ private:
else
bot_segment = " or %s(%u)";
required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second));
required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second);
required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), GetClassIDName(test_iter->first), test_iter->second));
required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", GetClassIDName(test_iter->first), test_iter->second);
bot_levels.erase(test_iter);
}
}
@@ -1167,18 +1167,18 @@ private:
spell_dump << StringFormat(" /mn:%05u/RD:%06i/zt:%02i/d#:%06i/td#:%05i/ed#:%05i/SAI:%03u",
spells[spell_id].mana,
spells[spell_id].ResistDiff,
spells[spell_id].zonetype,
spells[spell_id].descnum,
spells[spell_id].typedescnum,
spells[spell_id].effectdescnum,
spells[spell_id].SpellAffectIndex
spells[spell_id].resist_difficulty,
spells[spell_id].zone_type,
spells[spell_id].description_id,
spells[spell_id].type_description_id,
spells[spell_id].effect_description_id,
spells[spell_id].spell_affect_index
);
for (int i = EffectIDFirst; i <= 3/*EffectIDLast*/; ++i) {
int effect_index = EFFECTIDTOINDEX(i);
spell_dump << StringFormat(" /e%02i:%04i/b%02i:%06i/m%02i:%06i",
i, spells[spell_id].effectid[effect_index], i, spells[spell_id].base[effect_index], i, spells[spell_id].max[effect_index]);
i, spells[spell_id].effect_id[effect_index], i, spells[spell_id].base_value[effect_index], i, spells[spell_id].max_value[effect_index]);
}
switch (list_entry->BCST()) {
@@ -1428,6 +1428,7 @@ int bot_command_init(void)
bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) ||
bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) ||
bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) ||
bot_command_add("viewcombos", "Views bot race class combinations", 0, bot_command_view_combos) ||
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing)
) {
bot_command_deinit();
@@ -1651,47 +1652,68 @@ int bot_command_real_dispatch(Client *c, const char *message)
void bot_command_log_command(Client *c, const char *message)
{
int admin = c->Admin();
int admin = c->Admin();
bool continueevents = false;
switch (zone->loglevelvar) { //catch failsafe
case 9: // log only LeadGM
if ((admin >= 150) && (admin <200))
switch (zone->loglevelvar){ //catch failsafe
case 9: { // log only LeadGM
if (
admin >= AccountStatus::GMLeadAdmin &&
admin < AccountStatus::GMMgmt
) {
continueevents = true;
}
break;
}
case 8: { // log only GM
if (
admin >= AccountStatus::GMAdmin &&
admin < AccountStatus::GMLeadAdmin
) {
continueevents = true;
}
break;
}
case 1: {
if (admin >= AccountStatus::GMMgmt) {
continueevents = true;
}
break;
}
case 2: {
if (admin >= AccountStatus::GMLeadAdmin) {
continueevents = true;
}
break;
}
case 3: {
if (admin >= AccountStatus::GMAdmin) {
continueevents = true;
}
break;
}
case 4: {
if (admin >= AccountStatus::QuestTroupe) {
continueevents = true;
}
break;
}
case 5: {
if (admin >= AccountStatus::ApprenticeGuide) {
continueevents = true;
}
break;
}
case 6: {
if (admin >= AccountStatus::Steward) {
continueevents = true;
}
break;
}
case 7: {
continueevents = true;
break;
case 8: // log only GM
if ((admin >= 100) && (admin <150))
continueevents = true;
break;
case 1:
if ((admin >= 200))
continueevents = true;
break;
case 2:
if ((admin >= 150))
continueevents = true;
break;
case 3:
if ((admin >= 100))
continueevents = true;
break;
case 4:
if ((admin >= 80))
continueevents = true;
break;
case 5:
if ((admin >= 20))
continueevents = true;
break;
case 6:
if ((admin >= 10))
continueevents = true;
break;
case 7:
continueevents = true;
break;
default:
break;
break;
}
}
if (continueevents)
@@ -2934,7 +2956,7 @@ void bot_command_charm(Client *c, const Seperator *sep)
return;
}
if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob, true);
@@ -3381,7 +3403,7 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep)
return;
#if (EQDEBUG >= 12)
while (c->Admin() >= 250) {
while (c->Admin() >= AccountStatus::GMImpossible) {
if (strcasecmp(sep->arg[1], "shone")) { break; }
Bot* my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot || !(my_bot->IsHealRotationMember())) { break; }
@@ -3789,7 +3811,7 @@ void bot_command_mesmerize(Client *c, const Seperator *sep)
if (!target_mob)
continue;
if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
@@ -4704,7 +4726,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep)
if (!target_mob)
continue;
if (spells[local_entry->spell_id].base[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
if (spells[local_entry->spell_id].base_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel())
continue;
my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob);
@@ -4788,12 +4810,25 @@ void bot_command_taunt(Client *c, const Seperator *sep)
++taunting_count;
}
for (auto bot_iter : sbl) {
if (!bot_iter->HasPet())
continue;
if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt))
continue;
if (toggle_taunt)
bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting());
else
bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state);
if (sbl.size() == 1)
Bot::BotGroupSay(bot_iter, "My Pet is %s taunting", bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer");
++taunting_count;
}
if (taunting_count) {
if (toggle_taunt)
c->Message(m_action, "%i of your bots %s toggled their taunting state", taunting_count, ((taunting_count != 1) ? ("have") : ("has")));
c->Message(m_action, "%i of your bots and their pets %s toggled their taunting state", taunting_count, ((taunting_count != 1) ? ("have") : ("has")));
else
c->Message(m_action, "%i of your bots %s %s taunting", taunting_count, ((taunting_count != 1) ? ("have") : ("has")), ((taunt_state) ? ("started") : ("stopped")));
c->Message(m_action, "%i of your bots and their pets %s %s taunting", taunting_count, ((taunting_count != 1) ? ("have") : ("has")), ((taunt_state) ? ("started") : ("stopped")));
}
else {
c->Message(m_fail, "None of your bots are capable of taunting");
@@ -5107,6 +5142,68 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str());
}
void bot_command_view_combos(Client *c, const Seperator *sep)
{
const std::string class_substrs[17] = { "",
"%u (WAR)", "%u (CLR)", "%u (PAL)", "%u (RNG)",
"%u (SHD)", "%u (DRU)", "%u (MNK)", "%u (BRD)",
"%u (ROG)", "%u (SHM)", "%u (NEC)", "%u (WIZ)",
"%u (MAG)", "%u (ENC)", "%u (BST)", "%u (BER)"
};
const std::string race_substrs[17] = { "",
"%u (HUM)", "%u (BAR)", "%u (ERU)", "%u (ELF)",
"%u (HIE)", "%u (DEF)", "%u (HEF)", "%u (DWF)",
"%u (TRL)", "%u (OGR)", "%u (HFL)", "%u (GNM)",
"%u (IKS)", "%u (VAH)", "%u (FRG)", "%u (DRK)"
};
const uint16 race_values[17] = { 0,
HUMAN, BARBARIAN, ERUDITE, WOOD_ELF,
HIGH_ELF, DARK_ELF, HALF_ELF, DWARF,
TROLL, OGRE, HALFLING, GNOME,
IKSAR, VAHSHIR, FROGLOK, DRAKKIN
};
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
std::string window_title = "Bot Races";
std::string window_text;
std::string message_separator = " ";
c->Message(m_usage, "Usage: %s [bot_race]", sep->arg[0]);
window_text.append("<c \"#FFFFFF\">Races:<c \"#FFFF\">");
for (int race_id = 0; race_id <= 15; ++race_id) {
window_text.append(message_separator);
window_text.append(StringFormat(race_substrs[race_id + 1].c_str(), race_values[race_id + 1]));
message_separator = ", ";
}
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
if (sep->arg[1][0] == '\0' || !sep->IsNumber(1)) {
c->Message(m_fail, "Invalid Race!");
return;
}
uint16 bot_race = atoi(sep->arg[1]);
auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race);
auto race_name = GetRaceIDName(bot_race);
std::string window_title = "Bot Classes";
std::string window_text;
std::string message_separator = " ";
c->Message(m_usage, "%s can be these classes.", race_name);
window_text.append("<c \"#FFFFFF\">Classes:<c \"#FFFF\">");
for (int class_id = 0; class_id <= 15; ++class_id) {
if (classes_bitmask & GetPlayerClassBit(class_id)) {
window_text.append(message_separator);
window_text.append(StringFormat(class_substrs[class_id].c_str(), class_id));
message_separator = ", ";
}
}
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
void bot_subcommand_bot_create(Client *c, const Seperator *sep)
{
const std::string class_substrs[17] = { "",
@@ -5148,10 +5245,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
message_separator = " ";
object_count = 1;
for (int i = 0; i <= 15; ++i) {
if (((1 << i) & RuleI(Bots, AllowedClasses)) == 0)
continue;
window_text.append(const_cast<const std::string&>(message_separator));
window_text.append(message_separator);
if (object_count >= object_max) {
window_text.append("<br>");
object_count = 0;
@@ -5166,10 +5260,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
message_separator = " ";
object_count = 1;
for (int i = 0; i <= 15; ++i) {
if (((1 << i) & RuleI(Bots, AllowedRaces)) == 0)
continue;
window_text.append(const_cast<const std::string&>(message_separator));
window_text.append(message_separator);
if (object_count >= object_max) {
window_text.append("<br>");
object_count = 0;
@@ -5183,12 +5274,8 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
window_text.append("<c \"#FFFFFF\">Genders:<c \"#FFFF\">");
message_separator = " ";
for (int i = 0; i <= 1; ++i) {
if (((1 << i) & RuleI(Bots, AllowedGenders)) == 0)
continue;
window_text.append(const_cast<const std::string&>(message_separator));
window_text.append(message_separator);
window_text.append(StringFormat(gender_substrs[i].c_str(), i));
message_separator = ", ";
}
@@ -5802,9 +5889,9 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s",
((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQ::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)),
bots_iter.Level,
Bot::RaceIdToString(bots_iter.Race).c_str(),
GetRaceIDName(bots_iter.Race),
((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))),
Bot::ClassIdToString(bots_iter.Class).c_str(),
GetClassIDName(bots_iter.Class),
bots_iter.Owner
);
if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; }
@@ -5977,7 +6064,7 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep)
if (!bot_iter)
continue;
std::string report_msg = StringFormat("%s %s reports", Bot::ClassIdToString(bot_iter->GetClass()).c_str(), bot_iter->GetCleanName());
std::string report_msg = StringFormat("%s %s reports", GetClassIDName(bot_iter->GetClass()), bot_iter->GetCleanName());
report_msg.append(StringFormat(": %3.1f%% health", bot_iter->GetHPRatio()));
if (!IsNonSpellFighterClass(bot_iter->GetClass()))
report_msg.append(StringFormat(": %3.1f%% mana", bot_iter->GetManaRatio()));
@@ -8672,33 +8759,25 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
auto class_bit = GetPlayerClassBit(bot_class);
if ((class_bit & RuleI(Bots, AllowedClasses)) == PLAYER_CLASS_UNKNOWN_BIT) {
bot_owner->Message(m_fail, "Class '%s' bots are not allowed on this server", GetPlayerClassName(bot_class));
return bot_id;
}
auto race_bit = GetPlayerRaceBit(bot_race);
if ((race_bit & RuleI(Bots, AllowedRaces)) == PLAYER_RACE_UNKNOWN_BIT) {
bot_owner->Message(m_fail, "Race '%s' bots are not allowed on this server", GetPlayerRaceName(bot_class));
return bot_id;
}
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) {
bot_owner->Message(m_fail, "'%s'(%u):'%s'(%u) is an invalid race-class combination",
Bot::RaceIdToString(bot_race).c_str(), bot_race, Bot::ClassIdToString(bot_class).c_str(), bot_class);
const char* bot_race_name = GetRaceIDName(bot_race);
const char* bot_class_name = GetClassIDName(bot_class);
std::string view_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format("^viewcombos {}", bot_race), false, "view");
bot_owner->Message(
m_fail,
fmt::format(
"{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?",
bot_race_name,
bot_class_name,
view_saylink,
bot_race_name
).c_str()
);
return bot_id;
}
if (bot_gender > FEMALE || (((1 << bot_gender) & RuleI(Bots, AllowedGenders)) == 0)) {
if (RuleI(Bots, AllowedGenders) == 3)
bot_owner->Message(m_fail, "gender: %u(M), %u(F)", MALE, FEMALE);
else if (RuleI(Bots, AllowedGenders) == 2)
bot_owner->Message(m_fail, "gender: %u(F)", FEMALE);
else if (RuleI(Bots, AllowedGenders) == 1)
bot_owner->Message(m_fail, "gender: %u(M)", MALE);
else
bot_owner->Message(m_fail, "gender: ERROR - No valid genders exist");
if (bot_gender > FEMALE) {
bot_owner->Message(m_fail, "gender: %u (M), %u (F)", MALE, FEMALE);
return bot_id;
}
@@ -8710,7 +8789,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
if (bot_count >= max_bot_count) {
bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count);
bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots.", max_bot_count);
return bot_id;
}
@@ -8971,7 +9050,7 @@ bool helper_spell_check_fail(STBaseEntry* local_entry)
{
if (!local_entry)
return true;
if (spells[local_entry->spell_id].zonetype && zone->GetZoneType() && !(spells[local_entry->spell_id].zonetype & zone->GetZoneType()))
if (spells[local_entry->spell_id].zone_type && zone->GetZoneType() && !(spells[local_entry->spell_id].zone_type & zone->GetZoneType()))
return true;
return false;
+1
View File
@@ -593,6 +593,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep);
void bot_command_suspend(Client *c, const Seperator *sep);
void bot_command_taunt(Client *c, const Seperator *sep);
void bot_command_track(Client *c, const Seperator *sep);
void bot_command_view_combos(Client *c, const Seperator *sep);
void bot_command_water_breathing(Client *c, const Seperator *sep);
+17 -3
View File
@@ -766,7 +766,7 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst)
else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0)
bot_buffs[buff_count].counters = atoi(row[7]);
bot_buffs[buff_count].numhits = atoi(row[8]);
bot_buffs[buff_count].hit_number = atoi(row[8]);
bot_buffs[buff_count].melee_rune = atoi(row[9]);
bot_buffs[buff_count].magic_rune = atoi(row[10]);
bot_buffs[buff_count].dot_rune = atoi(row[11]);
@@ -843,13 +843,13 @@ bool BotDatabase::SaveBuffs(Bot* bot_inst)
bot_inst->GetBotID(),
bot_buffs[buff_index].spellid,
bot_buffs[buff_index].casterlevel,
spells[bot_buffs[buff_index].spellid].buffdurationformula,
spells[bot_buffs[buff_index].spellid].buff_duration_formula,
bot_buffs[buff_index].ticsremaining,
((CalculatePoisonCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)),
((CalculateDiseaseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)),
((CalculateCurseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)),
((CalculateCorruptionCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)),
bot_buffs[buff_index].numhits,
bot_buffs[buff_index].hit_number,
bot_buffs[buff_index].melee_rune,
bot_buffs[buff_index].magic_rune,
bot_buffs[buff_index].dot_rune,
@@ -2953,6 +2953,20 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
}
uint16 BotDatabase::GetRaceClassBitmask(uint16 bot_race)
{
std::string query = fmt::format(
"SELECT `classes` FROM `bot_create_combinations` WHERE `race` = {}",
bot_race
);
auto results = database.QueryDatabase(query);
uint16 classes = 0;
if (results.RowCount() == 1) {
auto row = results.begin();
classes = atoi(row[0]);
}
return classes;
}
/* fail::Bot functions */
const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; }
+1
View File
@@ -186,6 +186,7 @@ public:
/* Bot miscellaneous functions */
uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index);
uint16 GetRaceClassBitmask(uint16 bot_race);
class fail {
public:
+36 -36
View File
@@ -239,7 +239,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
break;
// Can we cast this spell on this target?
if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
if(!(spells[botSpell.SpellId].target_type==ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this)
&& !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
break;
@@ -338,14 +338,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
// can not cast buffs for your own pet only on another pet that isn't yours
if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet()))
if((spells[selectedBotSpell.SpellId].target_type == ST_Pet) && (tar != this->GetPet()))
continue;
// Validate target
if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this ||
spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport ||
(botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard))
if(!((spells[selectedBotSpell.SpellId].target_type == ST_Target || spells[selectedBotSpell.SpellId].target_type == ST_Pet || tar == this ||
spells[selectedBotSpell.SpellId].target_type == ST_Group || spells[selectedBotSpell.SpellId].target_type == ST_GroupTeleport ||
(botClass == BARD && spells[selectedBotSpell.SpellId].target_type == ST_AEBard))
&& !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this)
&& (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) {
continue;
@@ -613,7 +613,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
if(CheckSpellRecastTimers(this, itr->SpellIndex))
{
if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buffduration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)))
if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buff_duration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)))
continue;
//short duration buffs or other buffs only to be cast during combat.
@@ -651,14 +651,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
// can not cast buffs for your own pet only on another pet that isn't yours
if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet()))
if((spells[selectedBotSpell.SpellId].target_type == ST_Pet) && (tar != this->GetPet()))
continue;
// Validate target
if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this ||
spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport ||
(botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard))
if(!((spells[selectedBotSpell.SpellId].target_type == ST_Target || spells[selectedBotSpell.SpellId].target_type == ST_Pet || tar == this ||
spells[selectedBotSpell.SpellId].target_type == ST_Group || spells[selectedBotSpell.SpellId].target_type == ST_GroupTeleport ||
(botClass == BARD && spells[selectedBotSpell.SpellId].target_type == ST_AEBard))
&& !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this)
&& (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) {
continue;
@@ -853,9 +853,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
continue;
if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index?
if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index?
continue;
if (spells[iter.SpellId].targettype != ST_Target)
if (spells[iter.SpellId].target_type != ST_Target)
continue;
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
continue;
@@ -875,7 +875,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
case BEASTLORD: {
botSpell = GetBestBotSpellForDiseaseBasedSlow(this);
if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].ResistDiff)))
if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].resist_difficulty)))
botSpell = GetBestBotSpellForMagicBasedSlow(this);
break;
}
@@ -969,9 +969,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
continue;
if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index?
if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index?
continue;
if (spells[iter.SpellId].targettype != ST_Target)
if (spells[iter.SpellId].target_type != ST_Target)
continue;
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
continue;
@@ -996,9 +996,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
continue;
if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index?
if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index?
continue;
switch (spells[iter.SpellId].targettype) {
switch (spells[iter.SpellId].target_type) {
case ST_AEBard:
case ST_AECaster:
case ST_GroupTeleport:
@@ -1028,9 +1028,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
continue;
if (spells[iter.SpellId].zonetype != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zonetype != zone->GetZoneType()) // is this bit or index?
if (spells[iter.SpellId].zone_type != -1 && zone->GetZoneType() != -1 && spells[iter.SpellId].zone_type != zone->GetZoneType()) // is this bit or index?
continue;
switch (spells[iter.SpellId].targettype) {
switch (spells[iter.SpellId].target_type) {
case ST_AEBard:
case ST_AECaster:
case ST_GroupTeleport:
@@ -1095,11 +1095,11 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
} else
dist2 = DistanceSquared(m_Position, tar->GetPosition());
if (((((spells[AIspells[i].spellid].targettype==ST_GroupTeleport && AIspells[i].type==2)
|| spells[AIspells[i].spellid].targettype==ST_AECaster
|| spells[AIspells[i].spellid].targettype==ST_Group
|| spells[AIspells[i].spellid].targettype==ST_AEBard)
&& dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange)
if (((((spells[AIspells[i].spellid].target_type==ST_GroupTeleport && AIspells[i].type==2)
|| spells[AIspells[i].spellid].target_type==ST_AECaster
|| spells[AIspells[i].spellid].target_type==ST_Group
|| spells[AIspells[i].spellid].target_type==ST_AEBard)
&& dist2 <= spells[AIspells[i].spellid].aoe_range*spells[AIspells[i].spellid].aoe_range)
|| dist2 <= GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)*GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana()))
{
result = NPC::AIDoSpellCast(i, tar, mana_cost, oDontDoAgainBefore);
@@ -1126,8 +1126,8 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time;
if(spells[AIspells[i].spellid].EndurTimerIndex > 0) {
SetSpellRecastTimer(spells[AIspells[i].spellid].EndurTimerIndex, spells[AIspells[i].spellid].recast_time);
if(spells[AIspells[i].spellid].timer_id > 0) {
SetSpellRecastTimer(spells[AIspells[i].spellid].timer_id, spells[AIspells[i].spellid].recast_time);
}
}
@@ -1582,7 +1582,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
return false;
// Can we cast this spell on this target?
if (!(spells[botSpell.SpellId].targettype == ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
if (!(spells[botSpell.SpellId].target_type == ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this)
&& !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
return false;
@@ -1637,7 +1637,7 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster,
}
if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
if(spells[botSpellList[i].spellid].targettype == targetType) {
if(spells[botSpellList[i].spellid].target_type == targetType) {
BotSpell botSpell;
botSpell.SpellId = botSpellList[i].spellid;
botSpell.SpellIndex = i;
@@ -2006,7 +2006,7 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if (IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -2031,7 +2031,7 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -2272,26 +2272,26 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ
bool spellSelected = false;
if(CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if(selectLureNuke && (spells[botSpellListItr->SpellId].ResistDiff < lureResisValue)) {
if(selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) {
spellSelected = true;
}
else if(IsPureNukeSpell(botSpellListItr->SpellId)) {
if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC)
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
&& (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue))
{
spellSelected = true;
}
else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD)
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
&& (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue))
{
spellSelected = true;
}
else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE)
&& (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue))
&& (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue))
{
spellSelected = true;
}
else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) {
else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) {
firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId;
firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex;
firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost;
@@ -2534,7 +2534,7 @@ int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) {
bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) {
if(caster) {
if(caster->AIspells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast
if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer
if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].timer_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer
return true; //can cast spell
}
}
+411
View File
@@ -0,0 +1,411 @@
#include "cheat_manager.h"
#include "client.h"
#include "quest_parser_collection.h"
void CheatManager::SetClient(Client *cli)
{
m_target = cli;
}
void CheatManager::SetExemptStatus(ExemptionType type, bool v)
{
if (v) {
MovementCheck();
}
m_exemption[type] = v;
}
bool CheatManager::GetExemptStatus(ExemptionType type)
{
return m_exemption[type];
}
void CheatManager::CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2)
{
switch (type) {
case MQWarp:
if (m_time_since_last_warp_detection.GetRemainingTime() == 0 && RuleB(Cheat, EnableMQWarpDetector) &&
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQWarp (large warp detection) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]",
position1.x,
position1.y,
position1.z,
position2.x,
position2.y,
position2.z,
Distance(position1, position2)
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
std::string export_string = fmt::format(
"{} {} {}",
position1.x,
position1.y,
position1.z
);
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
}
break;
case MQWarpAbsolute:
if (RuleB(Cheat, EnableMQWarpDetector) &&
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQWarp (Absolute) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] to x [{:.2f}] y [{:.2f}] z [{:.2f}] Distance [{:.2f}]",
position1.x,
position1.y,
position1.z,
position2.x,
position2.y,
position2.z,
Distance(position1, position2)
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
std::string export_string = fmt::format(
"{} {} {}",
position1.x,
position1.y,
position1.z
);
parse->EventPlayer(EVENT_WARP, m_target, export_string, 0);
m_time_since_last_warp_detection.Start(2500);
}
break;
case MQWarpShadowStep:
if (RuleB(Cheat, EnableMQWarpDetector) &&
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQWarp (ShadowStep) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was shadow step exempt but we still found this suspicious.",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
case MQWarpKnockBack:
if (RuleB(Cheat, EnableMQWarpDetector) &&
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQWarp (Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] the target was Knock Back exempt but we still found this suspicious.",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
case MQWarpLight:
if (RuleB(Cheat, EnableMQWarpDetector) &&
((m_target->Admin() < RuleI(Cheat, MQWarpExemptStatus) || (RuleI(Cheat, MQWarpExemptStatus)) == -1))) {
if (RuleB(Cheat, MarkMQWarpLT)) {
std::string message = fmt::format(
"/MQWarp(Knockback) with location from x [{:.2f}] y [{:.2f}] z [{:.2f}] running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
}
break;
case MQZone:
if (RuleB(Cheat, EnableMQZoneDetector) &&
((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
case MQZoneUnknownDest:
if (RuleB(Cheat, EnableMQZoneDetector) &&
((m_target->Admin() < RuleI(Cheat, MQZoneExemptStatus) || (RuleI(Cheat, MQZoneExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQZone used at x [{:.2f}] y [{:.2f}] z [{:.2f}] with Unknown Destination",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
case MQGate:
if (RuleB(Cheat, EnableMQGateDetector) &&
((m_target->Admin() < RuleI(Cheat, MQGateExemptStatus) || (RuleI(Cheat, MQGateExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQGate used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
case MQGhost:
// this isn't just for ghost, its also for if a person isn't sending their MovementHistory packet also.
if (RuleB(Cheat, EnableMQGhostDetector) &&
((m_target->Admin() < RuleI(Cheat, MQGhostExemptStatus) ||
(RuleI(Cheat, MQGhostExemptStatus)) == -1))) {
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
"Packet blocking detected.",
zone->GetShortName()
);
LogCheat(
"[MQGhost] [{}] [{}] was caught not sending the proper packets as regularly as they were suppose to.",
m_target->AccountName(),
m_target->GetName()
);
}
break;
case MQFastMem:
if (RuleB(Cheat, EnableMQFastMemDetector) &&
((m_target->Admin() < RuleI(Cheat, MQFastMemExemptStatus) ||
(RuleI(Cheat, MQFastMemExemptStatus)) == -1))) {
std::string message = fmt::format(
"/MQFastMem used at x [{:.2f}] y [{:.2f}] z [{:.2f}]",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
}
break;
default:
std::string message = fmt::format(
"Unhandled HackerDetection flag with location from x [{:.2f}] y [{:.2f}] z [{:.2f}]",
position1.x,
position1.y,
position1.z
);
database.SetMQDetectionFlag(
m_target->AccountName(),
m_target->GetName(),
message.c_str(),
zone->GetShortName()
);
LogCheat(message);
break;
}
}
void CheatManager::MovementCheck(glm::vec3 updated_position)
{
if (m_time_since_last_movement_history.GetRemainingTime() == 0) {
CheatDetected(MQGhost, updated_position);
}
float dist = DistanceNoZ(m_target->GetPosition(), updated_position);
uint32 cur_time = Timer::GetCurrentTime();
if (dist == 0) {
if (m_distance_since_last_position_check > 0.0f) {
MovementCheck(0);
}
else {
m_time_since_last_position_check = cur_time;
m_cheat_detect_moved = false;
}
}
else {
m_distance_since_last_position_check += dist;
m_cheat_detect_moved = true;
if (m_time_since_last_position_check == 0) {
m_time_since_last_position_check = cur_time;
}
else {
MovementCheck(2500);
}
}
}
void CheatManager::MovementCheck(uint32 time_between_checks)
{
uint32 cur_time = Timer::GetCurrentTime();
if ((cur_time - m_time_since_last_position_check) > time_between_checks) {
float estimated_speed =
(m_distance_since_last_position_check * 100) / (float) (cur_time - m_time_since_last_position_check);
// MQWarpDetection shouldn't go below 1.0f so we can't end up dividing by 0.
float run_speed = m_target->GetRunspeed() /
std::min(
RuleR(Cheat, MQWarpDetectionDistanceFactor),
1.0f
);
if (estimated_speed > run_speed) {
bool using_gm_speed = m_target->GetGMSpeed();
bool is_immobile = m_target->GetRunspeed() == 0; // this covers stuns, roots, mez, and pseudorooted.
if (!using_gm_speed && !is_immobile) {
if (GetExemptStatus(ShadowStep)) {
if (m_distance_since_last_position_check > 800) {
CheatDetected(
MQWarpShadowStep,
glm::vec3(
m_target->GetX(),
m_target->GetY(),
m_target->GetZ()
)
);
}
}
else if (GetExemptStatus(KnockBack)) {
if (estimated_speed > 30.0f) {
CheatDetected(MQWarpKnockBack, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
}
}
else if (!GetExemptStatus(Port)) {
if (estimated_speed > (run_speed * 1.5)) {
CheatDetected(MQWarp, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
m_time_since_last_position_check = cur_time;
m_distance_since_last_position_check = 0.0f;
}
else {
CheatDetected(MQWarpLight, glm::vec3(m_target->GetX(), m_target->GetY(), m_target->GetZ()));
}
}
}
}
if (time_between_checks != 1000) {
SetExemptStatus(ShadowStep, false);
SetExemptStatus(KnockBack, false);
SetExemptStatus(Port, false);
}
m_time_since_last_position_check = cur_time;
m_distance_since_last_position_check = 0.0f;
}
}
void CheatManager::CheckMemTimer()
{
if (m_target == nullptr) {
return;
}
if (m_time_since_last_memorization - Timer::GetCurrentTime() <= 1) {
glm::vec3 pos = m_target->GetPosition();
CheatDetected(MQFastMem, pos);
}
m_time_since_last_memorization = Timer::GetCurrentTime();
}
void CheatManager::ProcessMovementHistory(const EQApplicationPacket *app)
{
// if they haven't sent sent the packet within this time... they are probably spoofing...
// linux users reported that they don't send this packet at all but i can't prove they don't so i'm not sure if thats a fake or not.
m_time_since_last_movement_history.Start(70000);
if (GetExemptStatus(Port)) {
return;
}
auto *m_MovementHistory = (UpdateMovementEntry *) app->pBuffer;
if (app->size < sizeof(UpdateMovementEntry)) {
LogDebug(
"Size mismatch in OP_MovementHistoryList, expected {}, got [{}]",
sizeof(UpdateMovementEntry),
app->size
);
DumpPacket(app);
return;
}
for (int index = 0; index < (app->size) / sizeof(UpdateMovementEntry); index++) {
glm::vec3 to = glm::vec3(m_MovementHistory[index].X, m_MovementHistory[index].Y, m_MovementHistory[index].Z);
switch (m_MovementHistory[index].type) {
case UpdateMovementType::ZoneLine:
SetExemptStatus(Port, true);
break;
case UpdateMovementType::TeleportA:
if (index != 0) {
glm::vec3 from = glm::vec3(
m_MovementHistory[index - 1].X,
m_MovementHistory[index - 1].Y,
m_MovementHistory[index - 1].Z
);
CheatDetected(MQWarpAbsolute, from, to);
}
SetExemptStatus(Port, false);
break;
}
}
}
void CheatManager::ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter)
{
if (type == AT_Anim && parameter == ANIM_SIT) {
m_time_since_last_memorization = Timer::GetCurrentTime();
}
else if (spawn_id == 0 && type == AT_AntiCheat) {
m_time_since_last_action = parameter;
}
}
void CheatManager::ProcessItemVerifyRequest(int32 slot_id, uint32 target_id)
{
if (slot_id == -1 && m_warp_counter != target_id) {
m_warp_counter = target_id;
}
}
void CheatManager::ClientProcess()
{
if (!m_cheat_detect_moved) {
m_time_since_last_position_check = Timer::GetCurrentTime();
}
}
+89
View File
@@ -0,0 +1,89 @@
#ifndef ANTICHEAT_H
#define ANTICHEAT_H
class CheatManager;
class Client;
#include "../common/timer.h"
#include "../common/rulesys.h"
#include <glm/ext/vector_float3.hpp>
#include "../common/eq_packet_structs.h"
#include "../common/eq_packet.h"
typedef enum {
Collision = 1,
TeleportB,
TeleportA,
ZoneLine,
Unknown0x5,
Unknown0x6,
SpellA, // Titanium - UF
Unknown0x8,
SpellB // Used in RoF+
} UpdateMovementType;
typedef enum {
ShadowStep,
KnockBack,
Port,
Assist,
Sense,
MAX_EXEMPTIONS
} ExemptionType;
typedef enum {
MQWarp,
MQWarpShadowStep,
MQWarpKnockBack,
MQWarpLight,
MQZone,
MQZoneUnknownDest,
MQGate,
MQGhost,
MQFastMem,
MQWarpAbsolute
} CheatTypes;
class CheatManager {
public:
CheatManager()
{
SetExemptStatus(ShadowStep, false);
SetExemptStatus(KnockBack, false);
SetExemptStatus(Port, false);
SetExemptStatus(Assist, false);
SetExemptStatus(Sense, false);
m_distance_since_last_position_check = 0.0f;
m_cheat_detect_moved = false;
m_target = nullptr;
m_time_since_last_memorization = 0;
m_time_since_last_position_check = 0;
m_time_since_last_warp_detection.Start();
m_time_since_last_movement_history.Start(70000);
m_warp_counter = 0;
}
void SetClient(Client *cli);
void SetExemptStatus(ExemptionType type, bool v);
bool GetExemptStatus(ExemptionType type);
void CheatDetected(CheatTypes type, glm::vec3 position1, glm::vec3 position2 = glm::vec3(0, 0, 0));
void MovementCheck(glm::vec3 updated_position);
void MovementCheck(uint32 time_between_checks = 1000);
void CheckMemTimer();
void ProcessMovementHistory(const EQApplicationPacket *app);
void ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter);
void ProcessItemVerifyRequest(int32 slot_id, uint32 target_id);
void ClientProcess();
private:
bool m_exemption[ExemptionType::MAX_EXEMPTIONS]{};
float m_distance_since_last_position_check;
bool m_cheat_detect_moved;
Client *m_target;
uint32 m_time_since_last_position_check;
uint32 m_time_since_last_memorization;
uint32 m_time_since_last_action{};
Timer m_time_since_last_warp_detection;
Timer m_time_since_last_movement_history;
uint32 m_warp_counter;
};
#endif //ANTICHEAT_H
+1232 -494
View File
File diff suppressed because it is too large Load Diff
+134 -61
View File
@@ -66,6 +66,7 @@ namespace EQ
#include "zone_store.h"
#include "task_manager.h"
#include "task_client_state.h"
#include "cheat_manager.h"
#ifdef _WINDOWS
// since windows defines these within windef.h (which windows.h include)
@@ -79,6 +80,7 @@ namespace EQ
#include <algorithm>
#include <memory>
#include <deque>
#include <ctime>
#define CLIENT_TIMEOUT 90000
@@ -120,17 +122,6 @@ typedef enum {
EvacToSafeCoords
} ZoneMode;
typedef enum {
MQWarp,
MQWarpShadowStep,
MQWarpKnockBack,
MQWarpLight,
MQZone,
MQZoneUnknownDest,
MQGate,
MQGhost
} CheatTypes;
enum {
HideCorpseNone = 0,
HideCorpseAll = 1,
@@ -212,7 +203,11 @@ enum eInnateSkill {
InnateDisabled = 255
};
const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000;
const std::string DIAWIND_RESPONSE_ONE_KEY = "diawind_npc_response_one";
const std::string DIAWIND_RESPONSE_TWO_KEY = "diawind_npc_response_two";
const uint32 POPUPID_DIAWIND_ONE = 99999;
const uint32 POPUPID_DIAWIND_TWO = 100000;
const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000;
struct ClientReward
{
@@ -306,8 +301,8 @@ public:
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
uint32 FindTraderItemSerialNumber(int32 ItemID);
EQ::ItemInstance* FindTraderItemBySerialNumber(int32 SerialNumber);
void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot);
void NukeTraderItem(uint16 slot, int16 charges, uint16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
void FindAndNukeTraderItem(int32 item_id,int16 quantity,Client* customer,uint16 traderslot);
void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);
void TradeRequestFailed(const EQApplicationPacket* app);
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
@@ -345,6 +340,9 @@ public:
bool GetRevoked() const { return revoked; }
void SetRevoked(bool rev) { revoked = rev; }
inline uint32 GetIP() const { return ip; }
std::string GetIPString();
int GetIPExemption();
void SetIPExemption(int exemption_amount);
inline bool GetHideMe() const { return gm_hide_me; }
void SetHideMe(bool hm);
inline uint16 GetPort() const { return port; }
@@ -394,6 +392,7 @@ public:
void Duck();
void Stand();
void Sit();
virtual void SetMaxHP();
int32 LevelRegen();
@@ -426,7 +425,7 @@ public:
inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; }
inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; }
inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; }
inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; }
inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zone_id; }
inline uint32 GetBindInstanceID(uint32 index = 0) const { return m_pp.binds[index].instance_id; }
int32 CalcMaxMana();
int32 CalcBaseMana();
@@ -550,7 +549,6 @@ public:
inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; }
int32 GetActSpellCost(uint16 spell_id, int32);
int32 GetActSpellCasttime(uint16 spell_id, int32);
virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual int GetCurrentBuffSlots() const;
@@ -600,7 +598,12 @@ public:
inline uint32 GetEXP() const { return m_pp.exp; }
bool UpdateLDoNPoints(int32 points, uint32 theme);
inline double GetAAEXPModifier(uint32 zone_id) const { return database.GetAAEXPModifier(CharacterID(), zone_id); };
inline double GetEXPModifier(uint32 zone_id) const { return database.GetEXPModifier(CharacterID(), zone_id); };
inline void SetAAEXPModifier(uint32 zone_id, double aa_modifier) { database.SetAAEXPModifier(CharacterID(), zone_id, aa_modifier); };
inline void SetEXPModifier(uint32 zone_id, double exp_modifier) { database.SetEXPModifier(CharacterID(), zone_id, exp_modifier); };
bool UpdateLDoNPoints(uint32 theme_id, int points);
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; }
void AddPVPPoints(uint32 Points);
@@ -644,7 +647,8 @@ public:
void GoToSafeCoords(uint16 zone_id, uint16 instance_id);
void Gate(uint8 bindnum = 0);
void SetBindPoint(int bind_num = 0, int to_zone = -1, int to_instance = 0, const glm::vec3& location = glm::vec3());
void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f);
void SetBindPoint2(int bind_num = 0, int to_zone = -1, int to_instance = 0, const glm::vec4& location = glm::vec4());
void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f, float heading = 0.0f);
uint32 GetStartZone(void);
void MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
@@ -657,6 +661,7 @@ public:
void MoveZoneInstanceGroup(uint16 instance_id);
void MoveZoneInstanceRaid(uint16 instance_id);
void SendToGuildHall();
void SendToInstance(std::string instance_type, std::string zone_short_name, uint32 instance_version, float x, float y, float z, float heading, std::string instance_identifier, uint32 duration);
void AssignToInstance(uint16 instance_id);
void RemoveFromInstance(uint16 instance_id);
void WhoAll();
@@ -726,6 +731,7 @@ public:
void Stun(int duration);
void UnStun();
void ReadBook(BookRequest_Struct *book);
void ReadBookByName(std::string book_name, uint8 book_type);
void QuestReadBook(const char* text, uint8 type);
void SendClientMoneyUpdate(uint8 type,uint32 amount);
void SendMoneyUpdate();
@@ -777,27 +783,44 @@ public:
void GMKill();
inline bool IsMedding() const {return medding;}
inline uint16 GetDuelTarget() const { return duel_target; }
inline uint32 GetDuelTarget() const { return duel_target; }
inline bool IsDueling() const { return duelaccepted; }
inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; }
inline void SetDuelTarget(uint32 set_id) { duel_target = set_id; }
inline void SetDueling(bool duel) { duelaccepted = duel; }
// use this one instead
void MemSpell(uint16 spell_id, int slot, bool update_client = true);
void UnmemSpell(int slot, bool update_client = true);
void UnmemSpellBySpellID(int32 spell_id);
void UnmemSpellAll(bool update_client = true);
int FindEmptyMemSlot();
uint16 FindMemmedSpellBySlot(int slot);
int FindMemmedSpellBySpellID(uint16 spell_id);
int MemmedCount();
std::vector<int> GetLearnableDisciplines(uint8 min_level = 1, uint8 max_level = 0);
std::vector<int> GetLearnedDisciplines();
std::vector<int> GetMemmedSpells();
std::vector<int> GetScribeableSpells(uint8 min_level = 1, uint8 max_level = 0);
std::vector<int> GetScribedSpells();
void ScribeSpell(uint16 spell_id, int slot, bool update_client = true);
void UnscribeSpell(int slot, bool update_client = true);
// defer save used when bulk saving
void ScribeSpell(uint16 spell_id, int slot, bool update_client = true, bool defer_save = false);
void SaveSpells();
void SaveDisciplines();
// Bulk Scribe/Learn
uint16 ScribeSpells(uint8 min_level, uint8 max_level);
uint16 LearnDisciplines(uint8 min_level, uint8 max_level);
// Configurable Tracking Skill
uint16 GetClassTrackingDistanceMultiplier(uint16 class_);
bool CanThisClassTrack();
// defer save used when bulk saving
void UnscribeSpell(int slot, bool update_client = true, bool defer_save = false);
void UnscribeSpellAll(bool update_client = true);
void UntrainDisc(int slot, bool update_client = true);
void UntrainDisc(int slot, bool update_client = true, bool defer_save = false);
void UntrainDiscAll(bool update_client = true);
void UntrainDiscBySpellID(uint16 spell_id, bool update_client = true);
bool SpellGlobalCheck(uint16 spell_id, uint32 char_id);
bool SpellBucketCheck(uint16 spell_id, uint32 char_id);
uint32 GetCharMaxLevelFromQGlobal();
@@ -812,9 +835,6 @@ public:
inline uint8 GetBecomeNPCLevel() const { return npclevel; }
inline void SetBecomeNPC(bool flag) { npcflag = flag; }
inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; }
void SetFeigned(bool in_feigned);
/// this cures timing issues cuz dead animation isn't done but server side feigning is?
inline bool GetFeigned() const { return(feigned); }
EQStreamInterface* Connection() { return eqs; }
#ifdef PACKET_PROFILER
void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); }
@@ -831,6 +851,7 @@ public:
void SummonHorse(uint16 spell_id);
void SetHorseId(uint16 horseid_in);
inline void SetControlledMobId(uint16 mob_id_in) { controlled_mob_id = mob_id_in; }
uint16 GetControlledMobId() const{ return controlled_mob_id; }
uint16 GetHorseId() const { return horseId; }
bool CanMedOnHorse();
@@ -881,12 +902,14 @@ public:
void ResetAA();
void RefundAA();
void SendClearAA();
void SendClearLeadershipAA();
void SendClearPlayerAA();
inline uint32 GetAAXP() const { return m_pp.expAA; }
inline uint32 GetAAPercent() const { return m_epp.perAA; }
int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id);
int32 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id);
void SetAATitle(const char *Title);
void SetTitleSuffix(const char *txt);
void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing);
void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0);
// Item methods
void EVENT_ITEM_ScriptStopReturn();
@@ -901,13 +924,16 @@ public:
bool PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, bool client_update = false);
bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false);
void SendCursorBuffer();
void DeleteItemInInventory(int16 slot_id, int8 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);
void RemoveItem(uint32 item_id, uint32 quantity = 1);
bool SwapItem(MoveItem_Struct* move_in);
void SwapItemResync(MoveItem_Struct* move_slots);
void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false);
void PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, ServerLootItem_Struct** bag_item_data = 0);
bool AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQ::invslot::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0);
void SummonBaggedItems(uint32 bag_item_id, const std::vector<ServerLootItem_Struct>& bag_items);
void SetStats(uint8 type,int16 set_val);
void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true);
@@ -952,9 +978,9 @@ public:
//remove charges/multiple objects from inventory:
//bool DecreaseByType(uint32 type, uint8 amt);
bool DecreaseByID(uint32 type, uint8 amt);
bool DecreaseByID(uint32 type, int16 quantity);
uint8 SlotConvert2(uint8 slot); //Maybe not needed.
void Escape(); //AA Escape
void Escape(); //keep or quest function
void DisenchantSummonedBags(bool client_update = true);
void RemoveNoRent(bool client_update = true);
void RemoveDuplicateLore(bool client_update = true);
@@ -977,8 +1003,10 @@ public:
void ResetTrade();
void DropInst(const EQ::ItemInstance* inst);
bool TrainDiscipline(uint32 itemid);
bool MemorizeSpellFromItem(uint32 item_id);
void TrainDiscBySpellID(int32 spell_id);
uint32 GetDisciplineTimer(uint32 timer_id);
void ResetAllDisciplineTimers();
int GetDiscSlotBySpellID(int32 spellid);
void ResetDisciplineTimer(uint32 timer_id);
void SendDisciplineUpdate();
@@ -988,6 +1016,10 @@ public:
void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration);
bool IsLinkedSpellReuseTimerReady(uint32 timer_id);
void ResetCastbarCooldownBySlot(int slot);
void ResetAllCastbarCooldowns();
void ResetCastbarCooldownBySpellID(uint32 spell_id);
bool CheckTitle(int titleset);
void EnableTitle(int titleset);
@@ -1009,8 +1041,10 @@ public:
int FindSpellBookSlotBySpellID(uint16 spellid);
uint32 GetSpellIDByBookSlot(int book_slot);
int GetNextAvailableSpellBookSlot(int starting_slot = 0);
int GetNextAvailableDisciplineSlot(int starting_slot = 0);
inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; }
inline bool HasSpellScribed(int spellid) { return FindSpellBookSlotBySpellID(spellid) != -1; }
uint32 GetHighestScribedSpellinSpellGroup(uint32 spell_group);
uint16 GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill);
void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0);
void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0);
@@ -1028,7 +1062,11 @@ public:
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
void SendTaskFailed(int task_id, int task_index, TaskType task_type);
void SendTaskComplete(int task_index);
bool HasTaskRequestCooldownTimer();
void SendTaskRequestCooldownTimerMessage();
void StartTaskRequestCooldownTimer();
inline ClientTaskState *GetTaskState() const { return task_state; }
inline bool HasTaskState() { if (task_state) { return true; } return false; }
inline void CancelTask(int task_index, TaskType task_type)
{
if (task_state) {
@@ -1083,17 +1121,8 @@ public:
);
}
}
inline void UpdateTasksOnKill(int npc_type_id)
{
if (task_state) {
task_state->UpdateTasksOnKill(
this,
npc_type_id
);
}
}
inline void UpdateTasksForItem(
ActivityType activity_type,
TaskActivityType activity_type,
int item_id,
int count = 1
)
@@ -1200,7 +1229,7 @@ public:
bool enforce_level_requirement = false
) {
if (task_state) {
task_state->AcceptNewTask(this, task_id, npc_id, enforce_level_requirement);
task_state->AcceptNewTask(this, task_id, npc_id, std::time(nullptr), enforce_level_requirement);
}
}
inline int ActiveSpeakTask(int npc_type_id)
@@ -1260,6 +1289,18 @@ public:
{
return (task_state ? task_state->CompletedTasksInSet(task_set_id) : 0);
}
void PurgeTaskTimers();
// shared task shims / middleware
// these variables are used as a shim to intercept normal localized task functionality
// and pipe it into zone -> world and back to world -> zone
// world is authoritative
bool m_requesting_shared_task = false;
bool m_shared_task_update = false;
bool m_requested_shared_task_removal = false;
std::vector<Client*> GetPartyMembers();
void HandleUpdateTasksOnKill(uint32 npc_type_id);
inline const EQ::versions::ClientVersion ClientVersion() const { return m_ClientVersion; }
inline const uint32 ClientVersionBit() const { return m_ClientVersionBit; }
@@ -1303,8 +1344,7 @@ public:
uint32 GetLDoNWinsTheme(uint32 t);
uint32 GetLDoNLossesTheme(uint32 t);
uint32 GetLDoNPointsTheme(uint32 t);
void UpdateLDoNWins(uint32 t, int32 n);
void UpdateLDoNLosses(uint32 t, int32 n);
void UpdateLDoNWinLoss(uint32 theme_id, bool win = false, bool remove = false);
void CheckLDoNHail(Mob *target);
void CheckEmoteHail(Mob *target, const char* message);
@@ -1328,9 +1368,9 @@ public:
const std::string& event_Name, int seconds, const std::string& uuid = {}, bool update_db = false);
void AddNewExpeditionLockout(const std::string& expedition_name,
const std::string& event_name, uint32_t duration, std::string uuid = {});
Expedition* CreateExpedition(DynamicZone& dz_instance, ExpeditionRequest& request);
Expedition* CreateExpedition(
const std::string& zone_name, uint32 version, uint32 duration, const std::string& expedition_name,
Expedition* CreateExpedition(DynamicZone& dz, bool disable_messages = false);
Expedition* CreateExpedition(const std::string& zone_name,
uint32 version, uint32 duration, const std::string& expedition_name,
uint32 min_players, uint32 max_players, bool disable_messages = false);
Expedition* GetExpedition() const;
uint32 GetExpeditionID() const { return m_expedition_id; }
@@ -1348,7 +1388,6 @@ public:
void SendExpeditionLockoutTimers();
void SetExpeditionID(uint32 expedition_id) { m_expedition_id = expedition_id; };
void SetPendingExpeditionInvite(ExpeditionInvite&& invite) { m_pending_expedition_invite = invite; }
void UpdateExpeditionInfoAndLockouts();
void DzListTimers();
void SetDzRemovalTimer(bool enable_timer);
void SendDzCompassUpdate();
@@ -1358,6 +1397,11 @@ public:
std::vector<DynamicZone*> GetDynamicZones(uint32_t zone_id = 0, int zone_version = -1);
std::unique_ptr<EQApplicationPacket> CreateDzSwitchListPacket(const std::vector<DynamicZone*>& dzs);
std::unique_ptr<EQApplicationPacket> CreateCompassPacket(const std::vector<DynamicZoneCompassEntry_Struct>& entries);
void AddDynamicZoneID(uint32_t dz_id);
void RemoveDynamicZoneID(uint32_t dz_id);
void SendDynamicZoneUpdates();
void SetDynamicZoneMemberStatus(DynamicZoneMemberStatus status);
void CreateTaskDynamicZone(int task_id, DynamicZone& dz_request);
void CalcItemScale();
bool CalcItemScale(uint32 slot_x, uint32 slot_y); // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1
@@ -1371,7 +1415,7 @@ public:
uint32 GetCorpseCount() { return database.GetCharacterCorpseCount(CharacterID()); }
uint32 GetCorpseID(int corpse) { return database.GetCharacterCorpseID(CharacterID(), corpse); }
uint32 GetCorpseItemAt(int corpse_id, int slot_id) { return database.GetCharacterCorpseItemAt(corpse_id, slot_id); }
void SuspendMinion();
void SuspendMinion(int value);
void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration);
void NotifyNewTitlesAvailable();
void Signal(uint32 data);
@@ -1448,6 +1492,10 @@ public:
bool GroupFollow(Client* inviter);
inline bool GetRunMode() const { return runmode; }
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0);
void SetItemRecastTimer(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 void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); }
@@ -1486,6 +1534,7 @@ public:
void UpdateMercLevel();
void CheckMercSuspendTimer();
Timer* GetMercTimer() { return &merc_timer; };
Timer* GetPickLockTimer() { return &pick_lock_timer; };
const char* GetRacePlural(Client* client);
const char* GetClassPlural(Client* client);
@@ -1512,6 +1561,7 @@ public:
void LoadAccountFlags();
void SetAccountFlag(std::string flag, std::string val);
std::string GetAccountFlag(std::string flag);
void SetGMStatus(int newStatus);
float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing);
void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume);
void PlayMP3(const char* fname);
@@ -1519,7 +1569,7 @@ public:
int mod_client_damage(int damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemInstance* weapon, Mob* other);
bool mod_client_message(char* message, uint8 chan_num);
bool mod_can_increase_skill(EQ::skills::SkillType skillid, Mob* against_who);
int16 mod_increase_skill_chance(int16 chance, Mob* against_who);
double mod_increase_skill_chance(double chance, Mob* against_who);
int mod_bindwound_percent(int max_percent, Mob* bindmob);
int mod_bindwound_hp(int bindhps, Mob* bindmob);
int mod_client_haste(int h);
@@ -1541,6 +1591,13 @@ public:
void ShowNumHits(); // work around function for numhits not showing on buffs
void ApplyWeaponsStance();
void TogglePassiveAlternativeAdvancement(const AA::Rank &rank, uint32 ability_id);
bool UseTogglePassiveHotkey(const AA::Rank &rank);
void TogglePurchaseAlternativeAdvancementRank(int rank_id);
void ResetAlternateAdvancementRank(uint32 aa_id);
bool IsEffectinAlternateAdvancementRankEffects(const AA::Rank &rank, int effect_id);
void TripInterrogateInvState() { interrogateinv_flag = true; }
bool GetInterrogateInvState() { return interrogateinv_flag; }
@@ -1553,10 +1610,6 @@ public:
uint32 GetLastInvSnapshotTime() { return m_epp.last_invsnapshot_time; }
uint32 GetNextInvSnapshotTime() { return m_epp.next_invsnapshot_time; }
//Command #Tune functions
virtual int32 Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false);
void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false);
void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing
@@ -1578,6 +1631,10 @@ public:
Raid *p_raid_instance;
void ShowDevToolsMenu();
CheatManager cheat_manager;
// rate limit
Timer m_list_task_timers_rate_limit = {};
protected:
friend class Mob;
@@ -1590,7 +1647,7 @@ protected:
void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true);
bool client_data_loaded;
int16 GetFocusEffect(focusType type, uint16 spell_id);
int32 GetFocusEffect(focusType type, uint16 spell_id, Mob *caster = nullptr);
uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id);
void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost);
@@ -1731,9 +1788,17 @@ private:
int Haste; //precalced value
uint32 tmSitting; // time stamp started sitting, used for HP regen bonus added on MAY 5, 2004
// dev tools
bool display_mob_info_window;
bool dev_tools_enabled;
uint16 m_door_tool_entity_id;
public:
uint16 GetDoorToolEntityId() const;
void SetDoorToolEntityId(uint16 door_tool_entity_id);
private:
int32 max_end;
int32 current_endurance;
@@ -1765,7 +1830,7 @@ private:
void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm);
void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
glm::vec3 m_ZoneSummonLocation;
glm::vec4 m_ZoneSummonLocation;
uint16 zonesummon_id;
uint8 zonesummon_ignorerestrictions;
ZoneMode zone_mode;
@@ -1784,10 +1849,8 @@ private:
Timer linkdead_timer;
Timer dead_timer;
Timer global_channel_timer;
Timer shield_timer;
Timer fishing_timer;
Timer endupkeep_timer;
Timer forget_timer; // our 2 min everybody forgets you timer
Timer autosave_timer;
Timer client_scan_npc_aggro_timer;
Timer client_zone_wide_full_position_update_timer;
@@ -1808,12 +1871,14 @@ private:
Timer helm_toggle_timer;
Timer aggro_meter_timer;
Timer mob_close_scan_timer;
Timer hp_self_update_throttle_timer; /* This is to prevent excessive packet sending under trains/fast combat */
Timer hp_other_update_throttle_timer; /* This is to keep clients from DOSing the server with macros that change client targets constantly */
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
Timer consent_throttle_timer;
Timer dynamiczone_removal_timer;
Timer task_request_timer;
Timer pick_lock_timer;
Timer heroforge_wearchange_timer;
glm::vec3 m_Proximity;
glm::vec4 last_position_before_bulk_update;
@@ -1825,7 +1890,6 @@ private:
bool npcflag;
uint8 npclevel;
bool feigned;
bool bZoning;
bool tgb;
bool instalog;
@@ -1847,6 +1911,14 @@ private:
ClientTaskState *task_state;
int TotalSecondsPlayed;
// we use this very sparingly at the zone level
// used for keeping clients in donecount sync before world sends absolute confirmations of state
int64 m_shared_task_id = 0;
public:
void SetSharedTaskId(int64 shared_task_id);
int64 GetSharedTaskId() const;
private:
//Anti Spam Stuff
Timer *KarmaUpdateTimer;
uint32 TotalKarma;
@@ -1919,6 +1991,7 @@ private:
std::vector<ExpeditionLockoutTimer> m_expedition_lockouts;
glm::vec3 m_quest_compass;
bool m_has_quest_compass = false;
std::vector<uint32_t> m_dynamic_zone_ids;
#ifdef BOTS
+87 -28
View File
@@ -328,18 +328,15 @@ int32 Client::CalcMaxHP()
if (current_hp > max_hp) {
current_hp = max_hp;
}
int hp_perc_cap = spellbonuses.HPPercCap[0];
int hp_perc_cap = spellbonuses.HPPercCap[SBIndex::RESOURCE_PERCENT_CAP];
if (hp_perc_cap) {
int curHP_cap = (max_hp * hp_perc_cap) / 100;
if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1])) {
if (current_hp > curHP_cap || (spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_hp > spellbonuses.HPPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) {
current_hp = curHP_cap;
}
}
// hack fix for client health not reflecting server value
last_max_hp = 0;
return max_hp;
}
@@ -591,10 +588,10 @@ int32 Client::CalcMaxMana()
if (current_mana > max_mana) {
current_mana = max_mana;
}
int mana_perc_cap = spellbonuses.ManaPercCap[0];
int mana_perc_cap = spellbonuses.ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP];
if (mana_perc_cap) {
int curMana_cap = (max_mana * mana_perc_cap) / 100;
if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[1] && current_mana > spellbonuses.ManaPercCap[1])) {
if (current_mana > curMana_cap || (spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_mana > spellbonuses.ManaPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) {
current_mana = curMana_cap;
}
}
@@ -781,7 +778,7 @@ int32 Client::CalcManaRegen(bool bCombat)
int32 Client::CalcManaRegenCap()
{
int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap;
int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap + itembonuses.ItemManaRegenCap + spellbonuses.ItemManaRegenCap;
return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
}
@@ -1054,7 +1051,12 @@ int Client::CalcHaste()
}
// 51+ 25 (despite there being higher spells...), 1-50 10
if (level > 50) { // 51+
h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
cap = RuleI(Character, Hastev3Cap);
if (spellbonuses.hastetype3 > cap) {
h += cap;
} else {
h += spellbonuses.hastetype3;
}
}
else { // 1-50
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
@@ -1506,22 +1508,30 @@ int32 Client::CalcATK()
return (ATK);
}
uint32 Mob::GetInstrumentMod(uint16 spell_id) const
uint32 Mob::GetInstrumentMod(uint16 spell_id)
{
if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod
if (GetClass() != BARD) {
//Other classes can get a base effects mod using SPA 413
if (HasBaseEffectFocus()) {
return (10 + (GetFocusEffect(focusFcBaseEffects, spell_id) / 10));//TODO: change action->instrument mod to float to support < 10% focus values
}
return 10;
}
//AA's click effects that use instrument/singing skills don't apply modifiers (Confirmed on live 11/24/21 ~Kayen)
if (casting_spell_aa_id) {
return 10;
}
uint32 effectmod = 10;
int effectmodcap = 0;
bool nocap = false;
if (RuleB(Character, UseSpellFileSongCap)) {
effectmodcap = spells[spell_id].songcap / 10;
// this looks a bit weird, but easiest way I could think to keep both systems working
if (effectmodcap == 0)
nocap = true;
else
effectmodcap += 10;
} else {
effectmodcap = spells[spell_id].song_cap / 10;
if (effectmodcap) {
effectmodcap += 10; //Actual calculated cap is 100 greater than songcap value.
}
}
else {
effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
}
// this should never use spell modifiers...
@@ -1530,6 +1540,39 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const
// item mods are in 10ths of percent increases
// clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason
// but clickies that are songs (selo's on Composers Greaves) do get AA mod as well
/*Mechanics: updated 10/19/21 ~Kayen
Bard Spell Effects
Mod uses the highest bonus from either of these for each instrument
SPA 179 SE_AllInstrumentMod is used for instrument spellbonus.______Mod. This applies to ALL instrument mods (Puretones Discipline)
SPA 260 SE_AddSingingMod is used for instrument spellbonus.______Mod. This applies to indiviual instrument mods. (Instrument mastery AA)
-Example usage: From AA a value of 4 = 40%
SPA 118 SE_Amplification is a stackable singing mod, on live it exists as both spell and AA bonus (stackable)
- Live Behavior: Amplifcation can be modified by singing mods and amplification itself, thus on the second cast of Amplification you will recieve
the mod from the first cast, this continues until you reach the song mod cap.
SPA 261 SE_SongModCap raises song focus cap (No longer used on live)
SPA 270 SE_BardSongRange increase range of beneficial bard songs (Sionachie's Crescendo)
SPA 413 SE_FcBaseEffects focus effect that replaced item instrument mods
Issues 10-15-21:
Bonuses are not applied, unless song is stopped and restarted due to pulse keeping it continues. -> Need to recode songs to recast when duration ends.
Formula Live Bards:
mod = (10 + (aabonus.____Mod [SPA 260 AA Instrument Mastery]) + (SE_FcBaseEffect[SPA 413])/10 + (spellbonus.______Mod [SPA 179 Puretone Disc]) + (Amplication [SPA 118])/10
TODO: Spell Table Fields that need to be implemented
Field 225 //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE
Field 226 //float base_effects_focus_offset; // -- BASE_EFFECTS_FOCUS_OFFSET (35161 Ruaabri's Reckless Renewal -120)
Based on description possibly works as a way to quickly balance instrument mods to a song.
Using a standard slope formula: y = mx + b
modified_base_value = (base_effects_focus_slope x effectmod)(base_value) + (base_effects_focus_offset)
Will need to confirm on live before implementing.
*/
switch (spells[spell_id].skill) {
case EQ::skills::SkillPercussionInstruments:
if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0)
@@ -1587,18 +1630,34 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const
else
effectmod = spellbonuses.singingMod;
if (IsBardSong(spell_id))
effectmod += aabonuses.singingMod + spellbonuses.Amplification;
effectmod += aabonuses.singingMod + (spellbonuses.Amplification + itembonuses.Amplification + aabonuses.Amplification); //SPA 118 SE_Amplification
break;
default:
effectmod = 10;
return effectmod;
}
if (!RuleB(Character, UseSpellFileSongCap))
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10)
if (HasBaseEffectFocus()) {
effectmod += (GetFocusEffect(focusFcBaseEffects, spell_id) / 10);
}
if (effectmod < 10) {
effectmod = 10;
if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap.
effectmod = effectmodcap;
}
if (effectmodcap) {
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap; //SPA 261 SE_SongModCap (not used on live)
//Incase a negative modifier is used.
if (effectmodcap <= 0) {
effectmodcap = 10;
}
if (effectmod > effectmodcap) { // if the cap is calculated to be 0 using new rules, no cap.
effectmod = effectmodcap;
}
}
LogSpells("[{}]::GetInstrumentMod() spell=[{}] mod=[{}] modcap=[{}]\n", GetName(), spell_id, effectmod, effectmodcap);
@@ -1614,10 +1673,10 @@ void Client::CalcMaxEndurance()
if (current_endurance > max_end) {
current_endurance = max_end;
}
int end_perc_cap = spellbonuses.EndPercCap[0];
int end_perc_cap = spellbonuses.EndPercCap[SBIndex::RESOURCE_PERCENT_CAP];
if (end_perc_cap) {
int curEnd_cap = (max_end * end_perc_cap) / 100;
if (current_endurance > curEnd_cap || (spellbonuses.EndPercCap[1] && current_endurance > spellbonuses.EndPercCap[1])) {
if (current_endurance > curEnd_cap || (spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP] && current_endurance > spellbonuses.EndPercCap[SBIndex::RESOURCE_AMOUNT_CAP])) {
current_endurance = curEnd_cap;
}
}
@@ -1751,7 +1810,7 @@ int32 Client::CalcEnduranceRegen(bool bCombat)
int32 Client::CalcEnduranceRegenCap()
{
int cap = RuleI(Character, ItemEnduranceRegenCap);
int cap = RuleI(Character, ItemEnduranceRegenCap) + aabonuses.ItemEnduranceRegenCap + itembonuses.ItemEnduranceRegenCap + spellbonuses.ItemEnduranceRegenCap;
return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100);
}
+1371 -970
View File
File diff suppressed because it is too large Load Diff
+14 -2
View File
@@ -97,8 +97,8 @@
void Handle_OP_Disarm(const EQApplicationPacket *app);
void Handle_OP_DisarmTraps(const EQApplicationPacket *app);
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
void Handle_OP_DuelResponse(const EQApplicationPacket *app);
void Handle_OP_DuelResponse2(const EQApplicationPacket *app);
void Handle_OP_DuelDecline(const EQApplicationPacket *app);
void Handle_OP_DuelAccept(const EQApplicationPacket *app);
void Handle_OP_DumpName(const EQApplicationPacket *app);
void Handle_OP_Dye(const EQApplicationPacket *app);
void Handle_OP_DzAddPlayer(const EQApplicationPacket *app);
@@ -282,6 +282,7 @@
void Handle_OP_TargetCommand(const EQApplicationPacket *app);
void Handle_OP_TargetMouse(const EQApplicationPacket *app);
void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app);
void Handle_OP_TaskTimers(const EQApplicationPacket *app);
void Handle_OP_Taunt(const EQApplicationPacket *app);
void Handle_OP_TestBuff(const EQApplicationPacket *app);
void Handle_OP_TGB(const EQApplicationPacket *app);
@@ -313,3 +314,14 @@
void Handle_OP_YellForHelp(const EQApplicationPacket *app);
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
void Handle_OP_ResetAA(const EQApplicationPacket *app);
void Handle_OP_MovementHistoryList(const EQApplicationPacket* app);
void Handle_OP_UnderWorld(const EQApplicationPacket* app);
// shared tasks
void Handle_OP_SharedTaskRemovePlayer(const EQApplicationPacket *app);
void Handle_OP_SharedTaskAddPlayer(const EQApplicationPacket *app);
void Handle_OP_SharedTaskMakeLeader(const EQApplicationPacket *app);
void Handle_OP_SharedTaskInviteResponse(const EQApplicationPacket *app);
void Handle_OP_SharedTaskAccept(const EQApplicationPacket *app);
void Handle_OP_SharedTaskQuit(const EQApplicationPacket *app);
void Handle_OP_SharedTaskPlayerList(const EQApplicationPacket *app);
+87 -80
View File
@@ -119,8 +119,9 @@ bool Client::Process() {
// SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check
// since the function will anyways
if (hpupdate_timer.Check(false))
if (hpupdate_timer.Check(false)) {
SendHPUpdate();
}
/* I haven't naturally updated my position in 10 seconds, updating manually */
if (!is_client_moving && position_update_timer.Check()) {
@@ -131,9 +132,9 @@ bool Client::Process() {
CheckManaEndUpdate();
if (dead && dead_timer.Check()) {
database.MoveCharacterToZone(GetName(), m_pp.binds[0].zoneId);
database.MoveCharacterToZone(GetName(), m_pp.binds[0].zone_id);
m_pp.zone_id = m_pp.binds[0].zoneId;
m_pp.zone_id = m_pp.binds[0].zone_id;
m_pp.zoneInstance = m_pp.binds[0].instance_id;
m_pp.x = m_pp.binds[0].x;
m_pp.y = m_pp.binds[0].y;
@@ -180,11 +181,9 @@ bool Client::Process() {
myraid->MemberZoned(this);
}
Expedition* expedition = GetExpedition();
if (expedition)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline);
}
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
return false; //delete client
}
@@ -200,9 +199,30 @@ bool Client::Process() {
instalog = true;
}
if (heroforge_wearchange_timer.Check()) {
/*
This addresses bug where on zone in heroforge models would not be sent to other clients when this was
in Client::CompleteConnect(). Sending after a small 250 ms delay after that function resolves the issue.
Unclear the underlying reason for this, if a better solution can be found then can move this back.
*/
if (queue_wearchange_slot >= 0) { //Resend slot from Client::SwapItem if heroforge item is swapped.
SendWearChange(static_cast<uint8>(queue_wearchange_slot));
}
else { //Send from Client::CompleteConnect()
SendWearChangeAndLighting(EQ::textures::LastTexture);
Mob *pet = GetPet();
if (pet) {
pet->SendWearChangeAndLighting(EQ::textures::LastTexture);
}
}
heroforge_wearchange_timer.Disable();
}
if (IsStunned() && stunned_timer.Check())
Mob::UnStun();
cheat_manager.ClientProcess();
if (bardsong_timer.Check() && bardsong != 0) {
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
//doing the next pulse on them...
@@ -218,9 +238,9 @@ bool Client::Process() {
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
}
else {
if (!ApplyNextBardPulse(bardsong, song_target, bardsong_slot))
if (!ApplyBardPulse(bardsong, song_target, bardsong_slot)) {
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
//SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana);
}
}
}
@@ -301,6 +321,10 @@ bool Client::Process() {
}
if (AutoFireEnabled()) {
if (GetTarget() == this) {
MessageString(Chat::TooFarAway, TRY_ATTACKING_SOMEONE);
auto_fire = false;
}
EQ::ItemInstance *ranged = GetInv().GetItem(EQ::invslot::slotRange);
if (ranged)
{
@@ -390,12 +414,17 @@ bool Client::Process() {
else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP
{
EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotPrimary);
TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotPrimary);
TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotPrimary);
TriggerDefensiveProcs(auto_attack_target, EQ::invslot::slotPrimary, false);
DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary);
if (TryDoubleMeleeRoundEffect()) {
DoAttackRounds(auto_attack_target, EQ::invslot::slotPrimary);
}
if (CheckAATimer(aaTimerRampage)) {
entity_list.AEAttack(this, 30);
entity_list.AEAttack(this, 40);
}
}
}
@@ -431,26 +460,15 @@ bool Client::Process() {
CheckIncreaseSkill(EQ::skills::SkillDualWield, auto_attack_target, -10);
if (CheckDualWield()) {
EQ::ItemInstance *wpn = GetInv().GetItem(EQ::invslot::slotSecondary);
TryWeaponProc(wpn, auto_attack_target, EQ::invslot::slotSecondary);
TryCombatProcs(wpn, auto_attack_target, EQ::invslot::slotSecondary);
DoAttackRounds(auto_attack_target, EQ::invslot::slotSecondary);
}
}
}
if (HasVirus()) {
if (viral_timer.Check()) {
viral_timer_counter++;
for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i += 2) {
if (viral_spells[i]) {
if (viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) {
SpreadVirus(viral_spells[i], viral_spells[i + 1]);
}
}
}
}
if (viral_timer_counter > 999)
viral_timer_counter = 0;
if (viral_timer.Check() && !dead) {
VirusEffectProcess();
}
ProjectileAttack();
@@ -460,32 +478,8 @@ bool Client::Process() {
DoGravityEffect();
}
if (shield_timer.Check())
{
if (shield_target)
{
if (!CombatRange(shield_target))
{
entity_list.MessageCloseString(
this, false, 100, 0,
END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
for (int y = 0; y < 2; y++)
{
if (shield_target->shielder[y].shielder_id == GetID())
{
shield_target->shielder[y].shielder_id = 0;
shield_target->shielder[y].shielder_bonus = 0;
}
}
shield_target = 0;
shield_timer.Disable();
}
}
else
{
shield_target = 0;
shield_timer.Disable();
}
if (shield_timer.Check()) {
ShieldAbilityFinish();
}
SpellProcess();
@@ -562,7 +556,7 @@ bool Client::Process() {
OnDisconnect(true);
LogInfo("Client linkdead: {}", name);
if (Admin() > 100) {
if (Admin() > AccountStatus::GMAdmin) {
if (GetMerc()) {
GetMerc()->Save();
GetMerc()->Depop();
@@ -575,11 +569,7 @@ bool Client::Process() {
AI_Start(CLIENT_LD_TIMEOUT);
SendAppearancePacket(AT_Linkdead, 1);
Expedition* expedition = GetExpedition();
if (expedition)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::LinkDead);
}
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::LinkDead);
}
}
@@ -711,10 +701,9 @@ void Client::OnDisconnect(bool hard_disconnect) {
}
}
Expedition* expedition = GetExpedition();
if (expedition && !bZoning)
if (!bZoning)
{
expedition->SetMemberStatus(this, ExpeditionMemberStatus::Offline);
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::Offline);
}
RemoveAllAuras();
@@ -1012,31 +1001,38 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
// corpse is in has shutdown since the rez spell was cast.
database.MarkCorpseAsRezzed(PendingRezzDBID);
LogSpells("Player [{}] got a [{}] Rezz, spellid [{}] in zone[{}], instance id [{}]",
this->name, (uint16)spells[SpellID].base[0],
this->name, (uint16)spells[SpellID].base_value[0],
SpellID, ZoneID, InstanceID);
this->BuffFadeNonPersistDeath();
BuffFadeNonPersistDeath();
int SpellEffectDescNum = GetSpellEffectDescNum(SpellID);
// Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client)
if((SpellEffectDescNum == 82) || (SpellEffectDescNum == 39067)) {
if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) {
SetHP(GetMaxHP() / 5);
SetMana(0);
SetHP(GetMaxHP()/5);
int rez_eff = 756;
if (RuleB(Character, UseOldRaceRezEffects) &&
(GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE))
rez_eff = 757;
SpellOnTarget(rez_eff, this); // Rezz effects
int resurrection_sickness_spell_id = (
RuleB(Character, UseOldRaceRezEffects) &&
(
GetRace() == BARBARIAN ||
GetRace() == DWARF ||
GetRace() == TROLL ||
GetRace() == OGRE
) ?
RuleI(Character, OldResurrectionSicknessSpellID) :
RuleI(Character, ResurrectionSicknessSpellID)
);
SpellOnTarget(resurrection_sickness_spell_id, this); // Rezz effects
}
else {
SetMana(GetMaxMana());
SetHP(GetMaxHP());
SetMana(GetMaxMana());
}
if(spells[SpellID].base[0] < 100 && spells[SpellID].base[0] > 0 && PendingRezzXP > 0)
if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0)
{
SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base[0])))),
SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base_value[0])))),
GetAAXP(),true);
}
else if (spells[SpellID].base[0] == 100 && PendingRezzXP > 0) {
else if (spells[SpellID].base_value[0] == 100 && PendingRezzXP > 0) {
SetEXP((GetEXP() + PendingRezzXP), GetAAXP(), true);
}
@@ -1053,6 +1049,11 @@ void Client::OPTGB(const EQApplicationPacket *app)
{
if(!app) return;
if(!app->pBuffer) return;
if(!RuleB(Character, EnableTGB))
{
return;
}
uint32 tgb_flag = *(uint32 *)app->pBuffer;
if(tgb_flag == 2)
@@ -1450,7 +1451,12 @@ void Client::OPMoveCoin(const EQApplicationPacket* app)
}
else{
if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){
this->Message(Chat::Red, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE");
this->SendPopupToClient(
"Shared Bank Warning",
"<c \"#F62217\">::: WARNING! :::<br>"
"SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE!</c>"
);
this->Message(Chat::Red, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE!");
}
}
}
@@ -1746,7 +1752,7 @@ void Client::OPGMSummon(const EQApplicationPacket *app)
}
else
{
if(admin < 80)
if(admin < AccountStatus::QuestTroupe)
{
return;
}
@@ -1861,7 +1867,7 @@ void Client::DoEnduranceUpkeep() {
uint32 buff_count = GetMaxTotalSlots();
for (buffs_i = 0; buffs_i < buff_count; buffs_i++) {
if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep;
int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep;
if(upkeep > 0) {
has_effect = true;
if(cost_redux > 0) {
@@ -1881,7 +1887,7 @@ void Client::DoEnduranceUpkeep() {
if(upkeep_sum != 0){
SetEndurance(GetEndurance() - upkeep_sum);
TryTriggerOnValueAmount(false, false, true);
TryTriggerOnCastRequirement();
}
if (!has_effect)
@@ -1996,7 +2002,7 @@ void Client::HandleRespawnFromHover(uint32 Option)
BindStruct* b = &m_pp.binds[0];
default_to_bind = new RespawnOption;
default_to_bind->name = "Bind Location";
default_to_bind->zone_id = b->zoneId;
default_to_bind->zone_id = b->zone_id;
default_to_bind->instance_id = b->instance_id;
default_to_bind->x = b->x;
default_to_bind->y = b->y;
@@ -2089,7 +2095,8 @@ void Client::HandleRespawnFromHover(uint32 Option)
}
//After they've respawned into the same zone, trigger EVENT_RESPAWN
parse->EventPlayer(EVENT_RESPAWN, this, static_cast<std::string>(itoa(Option)), is_rez ? 1 : 0);
std::string export_string = fmt::format("{}", Option);
parse->EventPlayer(EVENT_RESPAWN, this, export_string, is_rez ? 1 : 0);
//Pop Rez option from the respawn options list;
//easiest way to make sure it stays at the end and
+353 -13332
View File
File diff suppressed because it is too large Load Diff
+44 -68
View File
@@ -1,22 +1,3 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef COMMAND_H
#define COMMAND_H
@@ -25,18 +6,18 @@ class Seperator;
#include "../common/types.h"
#define COMMAND_CHAR '#'
#define COMMAND_CHAR '#'
typedef void (*CmdFuncPtr)(Client *,const Seperator *);
typedef void (*CmdFuncPtr)(Client *, const Seperator *);
typedef struct {
int access;
const char *desc; // description of command
CmdFuncPtr function; // null means perl function
} CommandRecord;
int access;
const char *desc; // description of command
CmdFuncPtr function; // null means perl function
} CommandRecord;
extern int (*command_dispatch)(Client *,char const*);
extern int commandcount; // number of commands loaded
extern int (*command_dispatch)(Client *, char const *);
extern int commandcount; // number of commands loaded
// the command system:
int command_init(void);
@@ -53,30 +34,23 @@ void command_aggro(Client *c, const Seperator *sep);
void command_aggrozone(Client *c, const Seperator *sep);
void command_ai(Client *c, const Seperator *sep);
void command_appearance(Client *c, const Seperator *sep);
void command_appearanceeffects(Client *c, const Seperator *sep);
void command_apply_shared_memory(Client *c, const Seperator *sep);
void command_attack(Client *c, const Seperator *sep);
void command_augmentitem(Client *c, const Seperator *sep);
void command_ban(Client *c, const Seperator *sep);
void command_beard(Client *c, const Seperator *sep);
void command_beardcolor(Client *c, const Seperator *sep);
void command_bind(Client* c, const Seperator *sep);
#ifdef BUGTRACK
void command_bug(Client *c, const Seperator *sep);
#endif
void command_bind(Client *c, const Seperator *sep);
void command_camerashake(Client *c, const Seperator *sep);
void command_castspell(Client *c, const Seperator *sep);
void command_chat(Client *c, const Seperator *sep);
void command_checklos(Client *c, const Seperator *sep);
void command_clearinvsnapshots(Client *c, const Seperator *sep);
void command_connectworldserver(Client *c, const Seperator *sep);
void command_copycharacter(Client *c, const Seperator *sep);
void command_corpse(Client *c, const Seperator *sep);
void command_corpsefix(Client *c, const Seperator *sep);
void command_crashtest(Client *c, const Seperator *sep);
void command_countitem(Client *c, const Seperator *sep);
void command_cvs(Client *c, const Seperator *sep);
void command_d1(Client *c, const Seperator *sep);
void command_damage(Client *c, const Seperator *sep);
void command_databuckets(Client *c, const Seperator *sep);
void command_date(Client *c, const Seperator *sep);
@@ -90,23 +64,30 @@ void command_devtools(Client *c, const Seperator *sep);
void command_details(Client *c, const Seperator *sep);
void command_disablerecipe(Client *c, const Seperator *sep);
void command_disarmtrap(Client *c, const Seperator *sep);
void command_door(Client *c, const Seperator *sep);
void command_distance(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_dye(Client *c, const Seperator *sep);
void command_dz(Client *c, const Seperator *sep);
void command_dzkickplayers(Client *c, const Seperator *sep);
void command_editmassrespawn(Client* c, const Seperator* sep);
void command_editmassrespawn(Client *c, const Seperator *sep);
void command_emote(Client *c, const Seperator *sep);
void command_emotesearch(Client* c, const Seperator *sep);
void command_emoteview(Client* c, const Seperator *sep);
void command_emotesearch(Client *c, const Seperator *sep);
void command_emoteview(Client *c, const Seperator *sep);
void command_emptyinventory(Client *c, const Seperator *sep);
void command_enablerecipe(Client *c, const Seperator *sep);
void command_endurance(Client *c, const Seperator *sep);
void command_equipitem(Client *c, const Seperator *sep);
void command_face(Client *c, const Seperator *sep);
void command_faction(Client *c, const Seperator *sep);
void command_findaliases(Client *c, const Seperator *sep);
void command_findclass(Client *c, const Seperator *sep);
void command_findfaction(Client *c, const Seperator *sep);
void command_findnpctype(Client *c, const Seperator *sep);
void command_findrace(Client *c, const Seperator *sep);
void command_findskill(Client *c, const Seperator *sep);
void command_findspell(Client *c, const Seperator *sep);
void command_findtask(Client *c, const Seperator *sep);
void command_findzone(Client *c, const Seperator *sep);
void command_fixmob(Client *c, const Seperator *sep);
void command_flag(Client *c, const Seperator *sep);
@@ -123,14 +104,14 @@ void command_getvariable(Client *c, const Seperator *sep);
void command_ginfo(Client *c, const Seperator *sep);
void command_giveitem(Client *c, const Seperator *sep);
void command_givemoney(Client *c, const Seperator *sep);
void command_globalview(Client* c, const Seperator *sep);
void command_globalview(Client *c, const Seperator *sep);
void command_gm(Client *c, const Seperator *sep);
void command_gmspeed(Client *c, const Seperator *sep);
void command_gmzone(Client *c, const Seperator *sep);
void command_goto(Client *c, const Seperator *sep);
void command_grid(Client *c, const Seperator *sep);
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);
bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char *what, const char *value);
void command_guildapprove(Client *c, const Seperator *sep);
void command_guildcreate(Client *c, const Seperator *sep);
void command_guildlist(Client *c, const Seperator *sep);
@@ -153,7 +134,6 @@ void command_interrupt(Client *c, const Seperator *sep);
void command_invsnapshot(Client *c, const Seperator *sep);
void command_invul(Client *c, const Seperator *sep);
void command_ipban(Client *c, const Seperator *sep);
void command_ipc(Client *c, const Seperator *sep);
void command_iplookup(Client *c, const Seperator *sep);
void command_iteminfo(Client *c, const Seperator *sep);
void command_itemsearch(Client *c, const Seperator *sep);
@@ -162,17 +142,14 @@ void command_killallnpcs(Client *c, const Seperator *sep);
void command_kill(Client *c, const Seperator *sep);
void command_lastname(Client *c, const Seperator *sep);
void command_level(Client *c, const Seperator *sep);
void command_listnpcs(Client *c, const Seperator *sep);
void command_list(Client *c, const Seperator *sep);
void command_listpetition(Client *c, const Seperator *sep);
void command_load_shared_memory(Client *c, const Seperator *sep);
void command_loc(Client *c, const Seperator *sep);
void command_lock(Client *c, const Seperator *sep);
void command_logs(Client *c, const Seperator *sep);
void command_logtest(Client *c, const Seperator *sep);
void command_makepet(Client *c, const Seperator *sep);
void command_mana(Client *c, const Seperator *sep);
void command_manastat(Client *c, const Seperator *sep);
void command_max_all_skills(Client *c, const Seperator *sep);
void command_memspell(Client *c, const Seperator *sep);
void command_merchantcloseshop(Client *c, const Seperator *sep);
@@ -183,7 +160,6 @@ void command_movechar(Client *c, const Seperator *sep);
void command_movement(Client *c, const Seperator *sep);
void command_myskills(Client *c, const Seperator *sep);
void command_mysql(Client *c, const Seperator *sep);
void command_mysqltest(Client *c, const Seperator *sep);
void command_mystats(Client *c, const Seperator *sep);
void command_name(Client *c, const Seperator *sep);
void command_netstats(Client *c, const Seperator *sep);
@@ -200,14 +176,14 @@ void command_npcspecialattk(Client *c, const Seperator *sep);
void command_npcstats(Client *c, const Seperator *sep);
void command_npctype_cache(Client *c, const Seperator *sep);
void command_npctypespawn(Client *c, const Seperator *sep);
void command_nudge(Client* c, const Seperator* sep);
void command_nudge(Client *c, const Seperator *sep);
void command_nukebuffs(Client *c, const Seperator *sep);
void command_nukeitem(Client *c, const Seperator *sep);
void command_numauths(Client *c, const Seperator *sep);
void command_object(Client* c, const Seperator *sep);
void command_object(Client *c, const Seperator *sep);
void command_oocmute(Client *c, const Seperator *sep);
void command_opcode(Client *c, const Seperator *sep);
void command_optest(Client *c, const Seperator *sep);
void command_bestz(Client *c, const Seperator *message);
void command_pf(Client *c, const Seperator *message);
#ifdef PACKET_PROFILER
void command_packetprofile(Client *c, const Seperator *sep);
@@ -219,6 +195,7 @@ void command_peqzone(Client *c, const Seperator *sep);
void command_permaclass(Client *c, const Seperator *sep);
void command_permagender(Client *c, const Seperator *sep);
void command_permarace(Client *c, const Seperator *sep);
void command_petitems(Client *c, const Seperator *sep);
void command_petitioninfo(Client *c, const Seperator *sep);
void command_picklock(Client *c, const Seperator *sep);
void command_profanity(Client *c, const Seperator *sep);
@@ -232,29 +209,30 @@ void command_proximity(Client *c, const Seperator *sep);
void command_push(Client *c, const Seperator *sep);
void command_pvp(Client *c, const Seperator *sep);
void command_qglobal(Client *c, const Seperator *sep);
void command_qtest(Client *c, const Seperator *sep);
void command_questerrors(Client *c, const Seperator *sep);
void command_race(Client *c, const Seperator *sep);
void command_raidloot(Client* c, const Seperator *sep);
void command_raidloot(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
void command_refreshgroup(Client *c, const Seperator *sep);
void command_refundaa(Client *c, const Seperator *sep);
void command_reloadaa(Client *c, const Seperator *sep);
void command_reloadallrules(Client *c, const Seperator *sep);
void command_reloademote(Client* c, const Seperator *sep);
void command_reloadcontentflags(Client *c, const Seperator *sep);
void command_reloademote(Client *c, const Seperator *sep);
void command_reloadlevelmods(Client *c, const Seperator *sep);
void command_reloadmerchants(Client *c, const Seperator *sep);
void command_reloadperlexportsettings(Client *c, const Seperator *sep);
void command_reloadqst(Client *c, const Seperator *sep);
void command_reloadstatic(Client *c, const Seperator *sep);
void command_reloadtitles(Client *c, const Seperator *sep);
void command_reloadtraps(Client* c, const Seperator *sep);
void command_reloadtraps(Client *c, const Seperator *sep);
void command_reloadworld(Client *c, const Seperator *sep);
void command_reloadworldrules(Client *c, const Seperator *sep);
void command_reloadzps(Client *c, const Seperator *sep);
void command_removeitem(Client *c, const Seperator *sep);
void command_repop(Client *c, const Seperator *sep);
void command_resetaa(Client* c,const Seperator *sep);
void command_resetaa(Client *c, const Seperator *sep);
void command_resetaa_timer(Client *c, const Seperator *sep);
void command_resetdisc_timer(Client *c, const Seperator *sep);
void command_revoke(Client *c, const Seperator *sep);
void command_roambox(Client *c, const Seperator *sep);
void command_rules(Client *c, const Seperator *sep);
@@ -262,21 +240,23 @@ void command_save(Client *c, const Seperator *sep);
void command_scale(Client *c, const Seperator *sep);
void command_scribespell(Client *c, const Seperator *sep);
void command_scribespells(Client *c, const Seperator *sep);
void command_sendop(Client *c, const Seperator *sep);
void command_sendzonespawns(Client *c, const Seperator *sep);
void command_sensetrap(Client *c, const Seperator *sep);
void command_serverinfo(Client *c, const Seperator *sep);
void command_serverrules(Client *c, const Seperator *sep);
void command_serversidename(Client *c, const Seperator *sep);
void command_set_adventure_points(Client *c, const Seperator *sep);
void command_setaapts(Client *c, const Seperator *sep);
void command_setaaxp(Client *c, const Seperator *sep);
void command_setaltcurrency(Client *c, const Seperator *sep);
void command_setanim(Client *c, const Seperator *sep);
void command_setcrystals(Client *c, const Seperator *sep);
void command_setendurance(Client *c, const Seperator *sep);
void command_setfaction(Client *c, const Seperator *sep);
void command_setgraveyard(Client *c, const Seperator *sep);
void command_sethp(Client *c, const Seperator *sep);
void command_setlanguage(Client *c, const Seperator *sep);
void command_setlsinfo(Client *c, const Seperator *sep);
void command_setmana(Client *c, const Seperator *sep);
void command_setpass(Client *c, const Seperator *sep);
void command_setpvppoints(Client *c, const Seperator *sep);
void command_setskill(Client *c, const Seperator *sep);
@@ -301,21 +281,15 @@ void command_spawneditmass(Client *c, const Seperator *sep);
void command_spawnfix(Client *c, const Seperator *sep);
void command_spawnstatus(Client *c, const Seperator *sep);
void command_spellinfo(Client *c, const Seperator *sep);
void command_spoff(Client *c, const Seperator *sep);
void command_spon(Client *c, const Seperator *sep);
void command_stun(Client *c, const Seperator *sep);
void command_summon(Client *c, const Seperator *sep);
void command_summonburiedplayercorpse(Client *c, const Seperator *sep);
void command_summonitem(Client *c, const Seperator *sep);
void command_suspend(Client *c, const Seperator *sep);
void command_synctod(Client *c, const Seperator *sep);
void command_task(Client *c, const Seperator *sep);
void command_tattoo(Client *c, const Seperator *sep);
void command_tempname(Client *c, const Seperator *sep);
void command_petname(Client *c, const Seperator *sep);
void command_test(Client *c, const Seperator *sep);
void command_testspawn(Client *c, const Seperator *sep);
void command_testspawnkill(Client *c, const Seperator *sep);
void command_texture(Client *c, const Seperator *sep);
void command_time(Client *c, const Seperator *sep);
void command_timers(Client *c, const Seperator *sep);
@@ -323,21 +297,25 @@ void command_timezone(Client *c, const Seperator *sep);
void command_title(Client *c, const Seperator *sep);
void command_titlesuffix(Client *c, const Seperator *sep);
void command_traindisc(Client *c, const Seperator *sep);
void command_trapinfo(Client* c, const Seperator *sep);
void command_trapinfo(Client *c, const Seperator *sep);
void command_tune(Client *c, const Seperator *sep);
void command_ucs(Client *c, const Seperator *sep);
void command_undye(Client *c, const Seperator *sep);
void command_undyeme(Client *c, const Seperator *sep);
void command_unfreeze(Client *c, const Seperator *sep);
void command_unlock(Client *c, const Seperator *sep);
void command_unmemspell(Client *c, const Seperator *sep);
void command_unmemspells(Client *c, const Seperator *sep);
void command_unscribespell(Client *c, const Seperator *sep);
void command_unscribespells(Client *c, const Seperator *sep);
void command_untraindisc(Client *c, const Seperator *sep);
void command_untraindiscs(Client *c, const Seperator *sep);
void command_uptime(Client *c, const Seperator *sep);
void command_version(Client *c, const Seperator *sep);
void command_viewcurrencies(Client *c, const Seperator *sep);
void command_viewnpctype(Client *c, const Seperator *sep);
void command_viewpetition(Client *c, const Seperator *sep);
void command_viewzoneloot(Client *c, const Seperator *sep);
void command_wc(Client *c, const Seperator *sep);
void command_weather(Client *c, const Seperator *sep);
void command_who(Client *c, const Seperator *sep);
@@ -355,7 +333,6 @@ void command_zone_instance(Client *c, const Seperator *sep);
void command_zonebootup(Client *c, const Seperator *sep);
void command_zonelock(Client *c, const Seperator *sep);
void command_zoneshutdown(Client *c, const Seperator *sep);
void command_zonespawn(Client *c, const Seperator *sep);
void command_zonestatus(Client *c, const Seperator *sep);
void command_zopp(Client *c, const Seperator *sep);
void command_zsafecoords(Client *c, const Seperator *sep);
@@ -363,7 +340,6 @@ void command_zsave(Client *c, const Seperator *sep);
void command_zsky(Client *c, const Seperator *sep);
void command_zstats(Client *c, const Seperator *sep);
void command_zunderworld(Client *c, const Seperator *sep);
void command_zuwcoords(Client *c, const Seperator *sep);
#ifdef BOTS
#include "bot.h"
+176 -59
View File
@@ -22,8 +22,6 @@
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
#define CHECK_LOS_STEP 1.0f
#define MAX_SHIELDERS 2 //I dont know if this is based on a client limit
#define ARCHETYPE_HYBRID 1
#define ARCHETYPE_CASTER 2
#define ARCHETYPE_MELEE 3
@@ -106,45 +104,56 @@
#define PET_BUTTON_SPELLHOLD 9
#define AURA_HARDCAP 2
#define WEAPON_STANCE_TYPE_MAX 2
#define SHIELD_ABILITY_RECAST_TIME 180
typedef enum { //focus types
focusSpellHaste = 1,
focusSpellDuration,
focusRange,
focusReagentCost,
focusManaCost,
focusImprovedHeal,
focusImprovedDamage,
focusImprovedDamage2,
focusImprovedDOT, //i dont know about this...
focusFcDamagePctCrit,
focusImprovedUndeadDamage,
focusPetPower,
focusResistRate,
focusSpellHateMod,
focusTriggerOnCast,
focusSpellVulnerability,
focusTwincast,
focusSympatheticProc,
focusFcDamageAmt,
focusFcDamageAmt2,
focusFcDamageAmtCrit,
focusSpellDurByTic,
focusSwarmPetDuration,
focusReduceRecastTime,
focusBlockNextSpell,
focusFcHealPctIncoming,
focusFcDamageAmtIncoming,
focusFcHealAmtIncoming,
focusFcBaseEffects,
focusIncreaseNumHits,
focusFcLimitUse,
focusFcMute,
focusFcTimerRefresh,
focusFcStunTimeMod,
focusFcHealPctCritIncoming,
focusFcHealAmt,
focusFcHealAmtCrit,
focusSpellHaste = 1, //@Fc, SPA: 127, SE_IncreaseSpellHaste, On Caster, cast time mod pct, base: pct
focusSpellDuration, //@Fc, SPA: 128, SE_IncreaseSpellDuration, On Caster, spell duration mod pct, base: pct
focusRange, //@Fc, SPA: 129, SE_IncreaseRange, On Caster, spell range mod pct, base: pct
focusReagentCost, //@Fc, SPA: 131, SE_ReduceReagentCost, On Caster, do not consume reagent pct chance, base: min pct, limit: max pct
focusManaCost, //@Fc, SPA: 132, SE_ReduceManaCost, On Caster, reduce mana cost by pct, base: min pct, limt: max pct
focusImprovedHeal, //@Fc, SPA: 125, SE_ImprovedHeal, On Caster, spell healing mod pct, base: min pct, limit: max pct
focusImprovedDamage, //@Fc, SPA: 124, SE_ImprovedDamage, On Caster, spell damage mod pct, base: min pct, limit: max pct
focusImprovedDamage2, //@Fc, SPA: 461, SE_ImprovedDamage2, On Caster, spell damage mod pct, base: min pct, limit: max pct
focusFcDamagePctCrit, //@Fc, SPA: 302, SE_FcDamagePctCrit, On Caster, spell damage mod pct, base: min pct, limit: max pct
focusPetPower, //@Fc, SPA: 167, SE_PetPowerIncrease, On Caster, pet power mod, base: value
focusResistRate, //@Fc, SPA: 126, SE_SpellResistReduction, On Caster, casted spell resist mod pct, base: min pct, limit: max pct
focusSpellHateMod, //@Fc, SPA: 130, SE_SpellHateMod, On Caster, spell hate mod pct, base: min pct, limit: max pct
focusTriggerOnCast, //@Fc, SPA: 339, SE_TriggerOnCast, On Caster, cast on spell use, base: chance pct limit: spellid
focusSpellVulnerability, //@Fc, SPA: 296, SE_FcSpellVulnerability, On Target, spell damage taken mod pct, base: min pct, limit: max pct
focusFcSpellDamagePctIncomingPC, //@Fc, SPA: 483, SE_Fc_Spell_Damage_Pct_IncomingPC, On Target, spell damage taken mod pct, base: min pct, limit: max pct
focusTwincast, //@Fc, SPA: 399, SE_FcTwincast, On Caster, chance cast spell twice, base: chance pct
focusSympatheticProc, //@Fc, SPA: 383, SE_SympatheticProc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid
focusFcDamageAmt, //@Fc, SPA: 286, SE_FcDamageAmt, On Caster, spell damage mod flat amt, base: amt
focusFcDamageAmt2, //@Fc, SPA: 462, SE_FcDamageAmt2, On Caster, spell damage mod flat amt, base: amt
focusFcDamageAmtCrit, //@Fc, SPA: 303, SE_FFcDamageAmtCrit, On Caster, spell damage mod flat amt, base: amt
focusSpellDurByTic, //@Fc, SPA: 287, SE_SpellDurationIncByTic, On Caster, spell buff duration mod, base: tics
focusSwarmPetDuration, //@Fc, SPA: 398, SE_SwarmPetDuration, On Caster, swarm pet duration mod, base: milliseconds
focusReduceRecastTime, //@Fc, SPA: 310, SE_ReduceReuseTimer, On Caster, disc reuse time mod, base: milliseconds
focusBlockNextSpell, //@Fc, SPA: 335, SE_BlockNextSpellFocus, On Caster, chance to block next spell, base: chance
focusFcHealPctIncoming, //@Fc, SPA: 393, SE_FcHealPctIncoming, On Target, heal received mod pct, base: pct
focusFcDamageAmtIncoming, //@Fc, SPA: 297, SE_FcDamageAmtIncoming, On Target, damage taken flat amt, base: amt
focusFcSpellDamageAmtIncomingPC, //@Fc, SPA: 484, SE_Fc_Spell_Damage_Amt_IncomingPC, On Target, damage taken flat amt, base: amt
focusFcCastSpellOnLand, //@Fc, SPA: 481, SE_Fc_Cast_Spell_On_Land, On Target, cast spell if hit by spell, base: chance pct, limit: spellid
focusFcHealAmtIncoming, //@Fc, SPA: 394, SE_FcHealAmtIncoming, On Target, heal received mod flat amt, base: amt
focusFcBaseEffects, //@Fc, SPA: 413, SE_FcBaseEffects, On Caster, base spell effectiveness mod pct, base: pct
focusIncreaseNumHits, //@Fc, SPA: 421, SE_FcIncreaseNumHits, On Caster, numhits mod flat amt, base: amt
focusFcLimitUse, //@Fc, SPA: 420, SE_FcLimitUse, On Caster, numhits mod pct, base: pct
focusFcMute, //@Fc, SPA: 357, SE_FcMute, On Caster, prevents spell casting, base: chance pct
focusFcTimerRefresh, //@Fc, SPA: 389, SE_FcTimerRefresh, On Caster, reset spell recast timer, base: 1
focusFcTimerLockout, //@Fc, SPA: 390, SE_FcTimerLockout, On Caster, set a spell to be on recast timer, base: recast duration milliseconds
focusFcStunTimeMod, //@Fc, SPA: 133, SE_FcStunTimeMod, On Caster, stun time mod pct, base: chance pct
focusFcResistIncoming, //@Fc, SPA: 510, SE_Fc_Resist_Incoming, On Target, resist modifier, base: amt
focusFcAmplifyMod, //@Fc, SPA: 507, SE_Fc_Amplify_Mod, On Caster, damage-heal-dot mod pct, base: pct
focusFcAmplifyAmt, //@Fc, SPA: 508, SE_Fc_Amplify_Amt, On Caster, damage-heal-dot mod flat amt, base: amt
focusFcCastTimeMod2, //@Fc, SPA: 500, SE_Fc_CastTimeMod2, On Caster, cast time mod pct, base: pct
focusFcCastTimeAmt, //@Fc, SPA: 501, SE_Fc_CastTimeAmt, On Caster, cast time mod flat amt, base: milliseconds
focusFcHealPctCritIncoming, //@Fc, SPA: 395, SE_FcHealPctCritIncoming, On Target, spell healing mod pct, base: pct
focusFcHealAmt, //@Fc, SPA: 392, SE_FcHealAmt, On Caster, spell healing mod flat amt, base: amt
focusFcHealAmtCrit, //@Fc, SPA: 396, SE_FcHealAmtCrit, On Caster, spell healing mod flat amt, base: amt
} focusType; //Any new FocusType needs to be added to the Mob::IsFocus function
#define HIGHEST_FOCUS focusFcHealAmtCrit //Should always be last focusType in enum
@@ -192,14 +201,16 @@ enum {
ALLOW_TO_TANK = 41,
IGNORE_ROOT_AGGRO_RULES = 42,
CASTING_RESIST_DIFF = 43,
COUNTER_AVOID_DAMAGE = 44,
COUNTER_AVOID_DAMAGE = 44, //Modify by percent NPC's opponents chance to riposte, block, parry or dodge individually, or for all skills
PROX_AGGRO = 45,
IMMUNE_RANGED_ATTACKS = 46,
IMMUNE_DAMAGE_CLIENT = 47,
IMMUNE_DAMAGE_NPC = 48,
IMMUNE_AGGRO_CLIENT = 49,
IMMUNE_AGGRO_NPC = 50,
MAX_SPECIAL_ATTACK = 51
MODIFY_AVOID_DAMAGE = 51, //Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills
IMMUNE_FADING_MEMORIES = 52,
MAX_SPECIAL_ATTACK = 53
};
typedef enum { //fear states
@@ -309,7 +320,7 @@ struct Buffs_Struct {
char caster_name[64];
int32 ticsremaining;
uint32 counters;
uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects
uint32 hit_number; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects
uint32 melee_rune;
uint32 magic_rune;
uint32 dot_rune;
@@ -319,6 +330,7 @@ struct Buffs_Struct {
int32 ExtraDIChance;
int16 RootBreakChance; //Not saved to dbase
uint32 instrument_mod;
int32 virus_spread_time; //time till next attempted viral spread
bool persistant_buff;
bool client; //True if the caster is a client
bool UpdateClient;
@@ -393,7 +405,7 @@ struct StatBonuses {
int32 skillmodmax[EQ::skills::HIGHEST_SKILL + 1];
int effective_casting_level;
int adjusted_casting_skill; // SPA 112 for fizzles
int reflect_chance; // chance to reflect incoming spell
int reflect[3]; // chance to reflect incoming spell [0]=Chance [1]=Resist Mod [2]= % of Base Dmg
uint32 singingMod;
uint32 Amplification; // stacks with singingMod
uint32 brassMod;
@@ -437,10 +449,14 @@ struct StatBonuses {
int32 HitChanceEffect[EQ::skills::HIGHEST_SKILL + 2]; //Spell effect Chance to Hit, straight percent increase
int32 DamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i
int32 DamageModifier2[EQ::skills::HIGHEST_SKILL + 2]; //i
int32 DamageModifier3[EQ::skills::HIGHEST_SKILL + 2]; //i
int32 MinDamageModifier[EQ::skills::HIGHEST_SKILL + 2]; //i
int32 ProcChance; // ProcChance/10 == % increase i = CombatEffects
int32 ProcChanceSPA; // ProcChance from spell effects
int32 ExtraAttackChance;
int32 ExtraAttackChance[2]; // base chance(w/ 2H weapon)=0, amt of extra attacks=1
int32 ExtraAttackChancePrimary[2]; // base chance=0, , amt of extra attacks=1
int32 ExtraAttackChanceSecondary[2]; // base chance=0, , amt of extra attacks=1
int32 DoubleMeleeRound[2]; // base chance=0, damage mod=1
int32 DoTShielding;
int32 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger)
uint32 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt
@@ -461,6 +477,7 @@ struct StatBonuses {
uint32 SpellOnKill[MAX_SPELL_TRIGGER*3]; // Chance to proc after killing a mob
uint32 SpellOnDeath[MAX_SPELL_TRIGGER*2]; // Chance to have effect cast when you die
int32 CritDmgMod[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 CritDmgModNoStack[EQ::skills::HIGHEST_SKILL + 2];// Critical melee damage modifier by percent, does not stack.
int32 SkillReuseTime[EQ::skills::HIGHEST_SKILL + 1]; // Reduces skill timers
int32 SkillDamageAmount[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon
@@ -474,15 +491,13 @@ struct StatBonuses {
int HPPercCap[2]; //Spell effect that limits you to being healed/regening beyond a % of your max
int ManaPercCap[2]; // ^^ 0 = % Cap 1 = Flat Amount Cap
int EndPercCap[2]; // ^^
bool BlockNextSpell; // Indicates whether the client can block a spell or not
//uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used
bool ImmuneToFlee; // Bypass the fleeing flag
uint32 VoiceGraft; // Stores the ID of the mob with which to talk through
int32 SpellProcChance; // chance to proc from sympathetic spell effects
int32 CharmBreakChance; // chance to break charm
int32 SongRange; // increases range of beneficial bard songs
uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion
uint8 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
int32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot
bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses)
int32 SkillDamageAmount2[EQ::skills::HIGHEST_SKILL + 2]; // Adds skill specific damage
@@ -494,7 +509,8 @@ struct StatBonuses {
uint32 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt
bool TriggerMeleeThreshold; // Has Melee Threshhold
bool TriggerSpellThreshold; // Has Spell Threshhold
uint32 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot
uint32 ManaAbsorbPercentDamage; // 0 = Mitigation value
int32 EnduranceAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Percent Endurance drain per HP lost
int32 ShieldBlock; // Chance to Shield Block
int32 BlockBehind; // Chance to Block Behind (with our without shield)
bool CriticalRegenDecay; // increase critical regen chance, decays based on spell level cast
@@ -502,7 +518,7 @@ struct StatBonuses {
bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast
bool DivineAura; // invulnerability
bool DistanceRemoval; // Check if Cancle if Moved effect is present
int32 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
int32 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buff slot
int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot
int32 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana.
uint32 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot
@@ -518,20 +534,47 @@ struct StatBonuses {
int32 Metabolism; // Food/drink consumption rates.
bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others.
int32 FactionModPct; // Modifies amount of faction gained.
bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 2]; // Determines if we need to search for a skill proc.
bool LimitToSkill[EQ::skills::HIGHEST_SKILL + 3]; // Determines if we need to search for a skill proc.
uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs.
uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success.
int32 SpellProc[MAX_AA_PROCS]; // Max number of spells containing melee spell procs.
int32 RangedProc[MAX_AA_PROCS]; // Max number of spells containing ranged spell procs.
int32 DefensiveProc[MAX_AA_PROCS]; // Max number of spells containing defensive spell procs.
bool Proc_Timer_Modifier; // Used to check if this exists, to avoid any further unnncessary checks.
uint32 PC_Pet_Rampage[2]; // 0= % chance to rampage, 1=damage modifier
uint32 PC_Pet_AE_Rampage[2]; // 0= % chance to AE rampage, 1=damage modifier
uint32 PC_Pet_Flurry; // Percent chance flurry from double attack
int32 Attack_Accuracy_Max_Percent; // Increase ATK accuracy by percent.
int32 AC_Mitigation_Max_Percent; // Increase AC mitigation by percent
int32 AC_Avoidance_Max_Percent; // Increase AC avoidance by percent
int32 Damage_Taken_Position_Mod[2]; // base = percent melee damage reduction base2 0=back 1=front. [0]Back[1]Front
int32 Melee_Damage_Position_Mod[2]; // base = percent melee damage increase base2 0=back 1=front. [0]Back[1]Front
int32 Damage_Taken_Position_Amt[2]; // base = flat amt melee damage reduction base2 0=back 1=front. [0]Back[1]Front
int32 Melee_Damage_Position_Amt[2]; // base = flat amt melee damage increase base2 0=back 1=front. [0]Back[1]Front
int32 Double_Backstab_Front; // base = percent chance to double back stab front
int32 DS_Mitigation_Amount; // base = flat amt DS mitigation. Negative value to reduce
int32 DS_Mitigation_Percentage; // base = percent amt of DS mitigation. Negative value to reduce
int32 Pet_Crit_Melee_Damage_Pct_Owner; // base = percent mod for pet critcal damage from owner
int32 Pet_Add_Atk; // base = Pet ATK bonus from owner
int32 ItemEnduranceRegenCap; // modify endurance regen cap
int32 WeaponStance[WEAPON_STANCE_TYPE_MAX +1];// base = trigger spell id, base2 = 0 is 2h, 1 is shield, 2 is dual wield, [0]spid 2h, [1]spid shield, [2]spid DW
bool ZoneSuspendMinion; // base 1 allows suspended minions to zone
bool CompleteHealBuffBlocker; // Use in SPA 101 to prevent recast of complete heal from this effect till blocker buff is removed.
int32 Illusion; // illusion spell id
// AAs
int8 Packrat; //weight reduction for items, 1 point = 10%
int32 TrapCircumvention; // reduce chance to trigger a trap.
uint16 SecondaryForte; // allow a second skill to be specialized with a cap of this value.
int32 ShieldDuration; // extends duration of /shield ability
int32 ExtendedShielding; // extends range of /shield ability
int8 Packrat; // weight reduction for items, 1 point = 10%
uint8 BuffSlotIncrease; // Increases number of available buff slots
uint32 DelayDeath; // how far below 0 hp you can go
int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses.
uint8 IncreaseRunSpeedCap; // Increase max run speed above cap.
int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x)
int32 SkillAttackProc[3]; // [0] chance to proc [2] spell on [1] skill usage
bool HasSkillAttackProc[EQ::skills::HIGHEST_SKILL + 1]; //check if any skill proc is present before assessing for all skill procs
uint8 FrontalStunResist; // Chance to resist a frontal stun
int32 BindWound; // Increase amount of HP by percent.
int32 MaxBindWound; // Increase max amount of HP you can bind wound.
@@ -566,9 +609,9 @@ struct StatBonuses {
int32 OffhandRiposteFail; // chance for opponent to fail riposte with offhand attack.
int32 ItemATKCap; // Raise item attack cap
int32 FinishingBlow[2]; // Chance to do a finishing blow for specified damage amount.
uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???)
uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= hit point ratio)
int32 ShieldEquipDmgMod; // Increases weapon's base damage by base1 % when shield is equipped (indirectly increasing hate)
bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect.
bool TriggerOnCastRequirement; // Triggers off various different conditions defined as emum SpellRestrictions
int8 StunBashChance; // chance to stun with bash.
int8 IncreaseChanceMemwipe; // increases chance to memory wipe
int8 CriticalMend; // chance critical monk mend
@@ -578,7 +621,7 @@ struct StatBonuses {
uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg
uint8 AssassinateLevel[2]; // Max Level Assassinate will be effective at.
int32 PetMeleeMitigation; // Add AC to owner's pet.
bool IllusionPersistence; // Causes illusions not to fade.
int IllusionPersistence; // 1=Causes illusions not to fade when zoning 2=Allow to persist after death.
uint16 extra_xtargets; // extra xtarget entries
bool ShroudofStealth; // rogue improved invisiblity
uint16 ReduceFallDamage; // reduce fall damage by percent
@@ -594,19 +637,92 @@ struct StatBonuses {
bool hunger; // Song of Sustenance -- min caps to 3500
};
// StatBonus Indexes
namespace SBIndex {
constexpr uint16 BUFFSTACKER_EXISTS = 0; // SPA 446-449
constexpr uint16 BUFFSTACKER_VALUE = 1; // SPA 446-449
constexpr uint16 EXTRA_ATTACK_CHANCE = 0; // SPA 266,498,499
constexpr uint16 EXTRA_ATTACK_NUM_ATKS = 1; // SPA 266,498,499
constexpr uint16 DIVINE_SAVE_CHANCE = 0; // SPA 232
constexpr uint16 DIVINE_SAVE_SPELL_TRIGGER_ID = 1; // SPA 232
constexpr uint16 DEATH_SAVE_TYPE = 0; // SPA 150
constexpr uint16 DEATH_SAVE_BUFFSLOT = 1; // SPA 150
constexpr uint16 DEATH_SAVE_MIN_LEVEL_FOR_HEAL = 2; // SPA 150
constexpr uint16 DEATH_SAVE_HEAL_AMT = 3; // SPA 150
constexpr uint16 RESOURCE_PERCENT_CAP = 0; // SPA 408-410
constexpr uint16 RESOURCE_AMOUNT_CAP = 1; // SPA 408-419
constexpr uint16 NEGATE_ATK_EXISTS = 0; // SPA 163
constexpr uint16 NEGATE_ATK_BUFFSLOT = 1; // SPA 163
constexpr uint16 NEGATE_ATK_MAX_DMG_ABSORB_PER_HIT = 2; // SPA 163
constexpr uint16 MITIGATION_RUNE_PERCENT = 0; // SPA 161,162,450
constexpr uint16 MITIGATION_RUNE_BUFFSLOT = 1; // SPA 161,162,450
constexpr uint16 MITIGATION_RUNE_MAX_DMG_ABSORB_PER_HIT = 2; // SPA 161,162,450
constexpr uint16 MITIGATION_RUNE_MAX_HP_AMT = 3; // SPA 161,162,450
constexpr uint16 THRESHOLDGUARD_MITIGATION_PERCENT = 0; // SPA 451,452
constexpr uint16 THRESHOLDGUARD_BUFFSLOT = 1; // SPA 451,452
constexpr uint16 THRESHOLDGUARD_MIN_DMG_TO_TRIGGER = 2; // SPA 451,452
constexpr uint16 ENDURANCE_ABSORD_MITIGIATION = 0; // SPA 521
constexpr uint16 ENDURANCE_ABSORD_DRAIN_PER_HP = 1; // SPA 521
constexpr uint16 IMPROVED_TAUNT_MAX_LVL = 0; // SPA 444
constexpr uint16 IMPROVED_TAUNT_AGGRO_MOD = 1; // SPA 444
constexpr uint16 IMPROVED_TAUNT_BUFFSLOT = 2; // SPA 444
constexpr uint16 ROOT_EXISTS = 0; // SPA 99
constexpr uint16 ROOT_BUFFSLOT = 1; // SPA 99
constexpr uint16 RUNE_AMOUNT = 0; // SPA 55
constexpr uint16 RUNE_BUFFSLOT = 1; // SPA 78
constexpr uint16 POSITION_BACK = 0; // SPA 503-506
constexpr uint16 POSITION_FRONT = 1; // SPA 503-506
constexpr uint16 PET_RAMPAGE_CHANCE = 0; // SPA 464,465
constexpr uint16 PET_RAMPAGE_DMG_MOD = 1; // SPA 465,465
constexpr uint16 SKILLATK_PROC_SPELL_ID = 0; // SPA 288
constexpr uint16 SKILLATK_PROC_CHANCE = 1; // SPA 288
constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288
constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219
constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219
constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223
constexpr uint16 DOUBLE_RIPOSTE_SKILL_ATK_CHANCE = 1; // SPA 223
constexpr uint16 DOUBLE_RIPOSTE_SKILL = 2; // SPA 223
constexpr uint16 FINISHING_EFFECT_PROC_CHANCE = 0; // SPA 278, 439, 217
constexpr uint16 FINISHING_EFFECT_DMG = 1; // SPA 278, 439, 217
constexpr uint16 FINISHING_EFFECT_LEVEL_MAX = 0; // SPA 440, 345, 346
constexpr uint16 FINISHING_EFFECT_LEVEL_CHANCE_BONUS = 1; // SPA 345, 346
constexpr uint16 FINISHING_BLOW_LEVEL_HP_RATIO = 1; // SPA 440
constexpr uint16 DOUBLE_MELEE_ROUND_CHANCE = 0; // SPA 471
constexpr uint16 DOUBLE_MELEE_ROUND_DMG_BONUS = 1; // SPA 471
constexpr uint16 REFLECT_CHANCE = 0; // SPA 158
constexpr uint16 REFLECT_RESISTANCE_MOD = 1; // SPA 158
constexpr uint16 REFLECT_DMG_EFFECTIVENESS = 2; // SPA 158
constexpr uint16 COMBAT_PROC_ORIGIN_ID = 0; // SPA
constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA
constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA
constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA
};
typedef struct
{
uint16 spellID;
int32 spellID;
uint16 chance;
uint16 base_spellID;
int32 base_spellID;
int level_override;
uint32 proc_reuse_time;
} tProc;
struct Shielders_Struct {
uint32 shielder_id;
uint16 shielder_bonus;
struct WeaponStance_Struct {
bool enabled;
bool spellbonus_enabled;
bool itembonus_enabled;
bool aabonus_enabled;
int spellbonus_buff_spell_id;
int itembonus_buff_spell_id;
int aabonus_buff_spell_id;
};
constexpr uint16 WEAPON_STANCE_TYPE_2H = 0;
constexpr uint16 WEAPON_STANCE_TYPE_SHIELD = 1;
constexpr uint16 WEAPON_STANCE_TYPE_DUAL_WIELD = 2;
typedef struct
{
uint16 increment;
@@ -623,6 +739,7 @@ typedef struct
int ammo_slot;
uint8 skill;
float speed_mod;
bool disable_procs;
} tProjatk;
//eventually turn this into a typedef and
+230 -70
View File
@@ -791,6 +791,45 @@ void Corpse::RemoveItem(ServerLootItem_Struct* item_data)
}
}
void Corpse::RemoveItemByID(uint32 item_id, int quantity) {
if (!database.GetItem(item_id)) {
return;
}
if (!HasItem(item_id)) {
return;
}
int removed_count = 0;
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* sitem = *current_item;
if (removed_count == quantity) {
break;
}
if (sitem && sitem->item_id == item_id) {
int stack_size = sitem->charges > 1 ? sitem->charges : 1;
if ((removed_count + stack_size) <= quantity) {
removed_count += stack_size;
is_corpse_changed = true;
itemlist.erase(current_item);
} else {
int amount_left = (quantity - removed_count);
if (amount_left > 0) {
if (stack_size > amount_left) {
removed_count += amount_left;
sitem->charges -= amount_left;
is_corpse_changed = true;
} else if (stack_size == amount_left) {
removed_count += amount_left;
itemlist.erase(current_item);
}
}
}
}
}
}
void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) {
this->copper = in_copper;
this->silver = in_silver;
@@ -914,7 +953,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
// return;
}
if(is_locked && client->Admin() < 100) {
if(is_locked && client->Admin() < AccountStatus::GMAdmin) {
SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
client->Message(Chat::Red, "Error: Corpse locked by GM.");
return;
@@ -938,7 +977,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
// loot_request_type is scoped to class Corpse and reset on a per-loot session basis
if (client->GetGM()) {
if (client->Admin() >= 100)
if (client->Admin() >= AccountStatus::GMAdmin)
loot_request_type = LootRequestType::GMAllowed;
else
loot_request_type = LootRequestType::GMPeek;
@@ -1164,14 +1203,14 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
}
if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc &&
(char_id != client->CharacterID() && client->Admin() < 150)) {
(char_id != client->CharacterID() && client->Admin() < AccountStatus::GMLeadAdmin)) {
client->Message(Chat::Red, "Error: This is a player corpse and you dont own it.");
client->QueuePacket(app);
SendEndLootErrorPacket(client);
return;
}
if (is_locked && client->Admin() < 100) {
if (is_locked && client->Admin() < AccountStatus::GMAdmin) {
client->QueuePacket(app);
SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
client->Message(Chat::Red, "Error: Corpse locked by GM.");
@@ -1244,40 +1283,54 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
}
}
char buf[88];
char q_corpse_name[64];
strcpy(q_corpse_name, corpse_name);
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(),
EntityList::RemoveNumbers(q_corpse_name));
buf[87] = '\0';
std::string export_string = fmt::format(
"{} {} {} {}",
inst->GetItem()->ID,
inst->GetCharges(),
EntityList::RemoveNumbers(corpse_name),
GetID()
);
std::vector<EQ::Any> args;
args.push_back(inst);
args.push_back(this);
if (parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args) != 0) {
lootitem->auto_loot = -1;
client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name);
client->QueuePacket(app);
delete inst;
return;
bool prevent_loot = false;
if (RuleB(Zone, UseZoneController)) {
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
if (controller){
if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, export_string, 0, &args) != 0) {
prevent_loot = true;
}
}
}
if (parse->EventPlayer(EVENT_LOOT, client, export_string, 0, &args) != 0) {
prevent_loot = true;
}
if (zone && zone->GetInstanceID() != 0)
if (!IsPlayerCorpse())
{
// expeditions may prevent looting based on client's lockouts
auto expedition = Expedition::FindCachedExpeditionByZoneInstance(zone->GetZoneID(), zone->GetInstanceID());
if (expedition && !expedition->CanClientLootCorpse(client, GetNPCTypeID(), GetID()))
// dynamic zones may prevent looting by non-members or based on lockouts
auto dz = zone->GetDynamicZone();
if (dz && !dz->CanClientLootCorpse(client, GetNPCTypeID(), GetID()))
{
client->MessageString(Chat::Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name);
client->QueuePacket(app);
SendEndLootErrorPacket(client);
ResetLooter();
delete inst;
return;
prevent_loot = true;
// note on live this message is only sent once on the first loot attempt of an open corpse
client->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name);
}
}
// do we want this to have a fail option too?
parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0);
// do we want this to have a fail option too? Sure?
if (parse->EventItem(EVENT_LOOT, client, inst, this, export_string, 0) != 0) {
prevent_loot = true;
}
if (prevent_loot) {
lootitem->auto_loot = -1;
client->QueuePacket(app);
safe_delete(inst);
return;
}
// safe to ACK now
client->QueuePacket(app);
@@ -1307,7 +1360,7 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
/* Update any tasks that have an activity to loot this item */
if (RuleB(TaskSystem, EnableTaskSystem))
client->UpdateTasksForItem(ActivityLoot, item->ID);
client->UpdateTasksForItem(TaskActivityType::Loot, item->ID);
/* Remove it from Corpse */
if (item_data) {
@@ -1403,62 +1456,151 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
}
void Corpse::QueryLoot(Client* to) {
int x = 0, y = 0; // x = visible items, y = total items
to->Message(Chat::White, "Coin: %ip, %ig, %is, %ic", platinum, gold, silver, copper);
if (itemlist.size() > 0) {
int player_corpse_limit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse;
to->Message(
Chat::White,
fmt::format(
"Loot | Name: {} ID: {}",
GetName(),
GetNPCTypeID()
).c_str()
);
ItemList::iterator cur,end;
cur = itemlist.begin();
end = itemlist.end();
int item_count = 0;
for (auto current_item : itemlist) {
int item_number = (item_count + 1);
if (!current_item) {
LogError("Corpse::QueryLoot() - ItemList error, null item.");
continue;
}
int corpselootlimit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse;
if (!current_item->item_id || !database.GetItem(current_item->item_id)) {
LogError("Corpse::QueryLoot() - Database error, invalid item.");
continue;
}
for(; cur != end; ++cur) {
ServerLootItem_Struct* sitem = *cur;
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkLootItem);
linker.SetLootData(current_item);
if (IsPlayerCorpse()) {
if (sitem->equip_slot >= EQ::invbag::GENERAL_BAGS_BEGIN && sitem->equip_slot <= EQ::invbag::CURSOR_BAG_END)
sitem->lootslot = 0xFFFF;
else
x < corpselootlimit ? sitem->lootslot = x : sitem->lootslot = 0xFFFF;
const EQ::ItemData* item = database.GetItem(sitem->item_id);
if (item)
to->Message((sitem->lootslot == 0xFFFF), "LootSlot: %i (EquipSlot: %i) Item: %s (%d), Count: %i", static_cast<int16>(sitem->lootslot), sitem->equip_slot, item->Name, item->ID, sitem->charges);
else
to->Message((sitem->lootslot == 0xFFFF), "Error: 0x%04x", sitem->item_id);
if (sitem->lootslot != 0xFFFF)
x++;
y++;
}
else {
sitem->lootslot=y;
const EQ::ItemData* item = database.GetItem(sitem->item_id);
if (item)
to->Message(Chat::White, "LootSlot: %i Item: %s (%d), Count: %i", sitem->lootslot, item->Name, item->ID, sitem->charges);
else
to->Message(Chat::White, "Error: 0x%04x", sitem->item_id);
y++;
to->Message(
Chat::White,
fmt::format(
"Item {} | Name: {} ({}){}",
item_number,
linker.GenerateLink().c_str(),
current_item->item_id,
(
current_item->charges > 1 ?
fmt::format(
" Amount: {}",
current_item->charges
) :
""
)
).c_str()
);
item_count++;
}
}
if (IsPlayerCorpse()) {
to->Message(Chat::White, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetCorpseDBID());
bool has_money = (
platinum > 0 ||
gold > 0 ||
silver > 0 ||
copper > 0
);
if (has_money) {
to->Message(
Chat::White,
fmt::format(
"Money | {}",
ConvertMoneyToString(
platinum,
gold,
silver,
copper
)
).c_str()
);
}
else {
to->Message(Chat::White, "%i %s on %s.", y, y==1?"item":"items", this->GetName());
}
bool Corpse::HasItem(uint32 item_id) {
if (!database.GetItem(item_id)) {
return false;
}
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* loot_item = *current_item;
if (!loot_item) {
LogError("Corpse::HasItem() - ItemList error, null item");
continue;
}
if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) {
LogError("Corpse::HasItem() - Database error, invalid item");
continue;
}
if (loot_item->item_id == item_id) {
return true;
}
}
return false;
}
uint16 Corpse::CountItem(uint32 item_id) {
uint16 item_count = 0;
if (!database.GetItem(item_id)) {
return item_count;
}
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* loot_item = *current_item;
if (!loot_item) {
LogError("Corpse::CountItem() - ItemList error, null item");
continue;
}
if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) {
LogError("Corpse::CountItem() - Database error, invalid item");
continue;
}
if (loot_item->item_id == item_id) {
item_count += loot_item->charges > 0 ? loot_item->charges : 1;
}
}
return item_count;
}
uint32 Corpse::GetItemIDBySlot(uint16 loot_slot) {
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* loot_item = *current_item;
if (loot_item->lootslot == loot_slot) {
return loot_item->item_id;
}
}
return 0;
}
uint16 Corpse::GetFirstSlotByItemID(uint32 item_id) {
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* loot_item = *current_item;
if (loot_item->item_id == item_id) {
return loot_item->lootslot;
}
}
return 0;
}
bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) {
uint32 dist2 = 10000; // pow(100, 2);
if (!spell) {
if (this->GetCharID() == client->CharacterID()) {
if (IsLocked() && client->Admin() < 100) {
if (IsLocked() && client->Admin() < AccountStatus::GMAdmin) {
client->Message(Chat::Red, "That corpse is locked by a GM.");
return false;
}
@@ -1678,3 +1820,21 @@ bool Corpse::MovePlayerCorpseToNonInstance()
return false;
}
std::vector<int> Corpse::GetLootList() {
std::vector<int> corpse_items;
for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) {
ServerLootItem_Struct* loot_item = *current_item;
if (!loot_item) {
LogError("Corpse::GetLootList() - ItemList error, null item");
continue;
}
if (std::find(corpse_items.begin(), corpse_items.end(), loot_item->item_id) != corpse_items.end()) {
continue;
}
corpse_items.push_back(loot_item->item_id);
}
return corpse_items;
}
+6
View File
@@ -95,6 +95,7 @@ class Corpse : public Mob {
int32 GetPlayerKillItem() { return player_kill_item; }
void RemoveItem(uint16 lootslot);
void RemoveItem(ServerLootItem_Struct* item_data);
void RemoveItemByID(uint32 item_id, int quantity = 1);
void AddItem(uint32 itemnum, uint16 charges, int16 slot = 0, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, uint8 attuned = 0);
/* Corpse: Coin */
@@ -113,6 +114,11 @@ class Corpse : public Mob {
/* Corpse: Loot */
void QueryLoot(Client* to);
bool HasItem(uint32 item_id);
uint16 CountItem(uint32 item_id);
uint32 GetItemIDBySlot(uint16 loot_slot);
uint16 GetFirstSlotByItemID(uint32 item_id);
std::vector<int> GetLootList();
void LootItem(Client* client, const EQApplicationPacket* app);
void EndLoot(Client* client, const EQApplicationPacket* app);
void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app);
+505
View File
@@ -0,0 +1,505 @@
#include "dialogue_window.h"
void DialogueWindow::Render(Client *c, std::string markdown)
{
std::string output = markdown;
if (!c->ClientDataLoaded()) {
return;
}
// this is the NPC that the client is interacting with if there is dialogue going on
Mob *target = c->GetTarget() ? c->GetTarget() : c;
// zero this out
c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), "");
c->SetEntityVariable(DIAWIND_RESPONSE_TWO_KEY.c_str(), "");
// simple find and replace for the markdown
find_replace(output, "~", "</c>");
find_replace(output, "{y}", "<c \"#CCFF33\">");
find_replace(output, "{lb}", "<c \"#00FFFF\">");
find_replace(output, "{r}", "<c \"#FF0000\">");
find_replace(output, "{g}", "<c \"#00FF00\">");
find_replace(output, "{gold}", "<c \"#FFFF66\">");
find_replace(output, "{orange}", "<c \"#FFA500\">");
find_replace(output, "{gray}", "<c \"#808080\">");
find_replace(output, "{tan}", "<c \"#daa520\">");
find_replace(output, "{bullet}", "");
find_replace(output, "{name}", fmt::format("{}", c->GetCleanName()));
find_replace(output, "{linebreak}", "--------------------------------------------------------------------");
find_replace(output, "{rowpad}", R"(<tr><td>{tdpad}<"td><td>{tdpad}<"td><"tr>)");
find_replace(output, "{tdpad}", "----------------------");
find_replace(output, "{in}", "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
// mysterious voice
bool render_mysterious_voice = false;
if (markdown.find("{mysterious}") != std::string::npos) {
render_mysterious_voice = true;
LogDiaWind("Client [{}] Rendering mysterious voice", c->GetCleanName());
find_replace(output, "{mysterious}", "");
}
// noquotes
bool render_noquotes = false;
if (markdown.find("noquotes") != std::string::npos) {
render_noquotes = true;
LogDiaWind("Client [{}] Rendering noquotes", c->GetCleanName());
find_replace(output, "noquotes", "");
}
// nobracket
bool render_nobracket = false;
if (markdown.find("nobracket") != std::string::npos) {
render_nobracket = true;
LogDiaWind("Client [{}] Rendering nobracket", c->GetCleanName());
find_replace(output, "nobracket", "");
}
bool render_hiddenresponse = false;
if (markdown.find("hiddenresponse") != std::string::npos) {
render_hiddenresponse = true;
LogDiaWind("Client [{}] Rendering hiddenresponse", c->GetCleanName());
find_replace(output, "hiddenresponse", "");
}
// animations
std::string animation = get_between(output, "+", "+");
if (!animation.empty()) {
LogDiaWind("Client [{}] Animation is not empty, contents are [{}]", c->GetCleanName(), animation);
find_replace(output, fmt::format("+{}+", animation), "");
// we treat the animation field differently if it is a number
if (StringIsNumber(animation)) {
LogDiaWindDetail("Client [{}] Animation is a number, firing animation [{}]", c->GetCleanName(), animation);
target->DoAnim(std::stoi(animation));
}
else {
for (auto &a: animations) {
if (a.first.find(str_tolower(animation)) != std::string::npos) {
LogDiaWindDetail(
"Client [{}] Animation is a string, firing animation [{}] [{}]",
c->GetCleanName(),
a.second,
a.first
);
target->DoAnim(a.second);
}
}
}
}
if (animation.empty() && RuleB(Chat, DialogueWindowAnimatesNPCsIfNoneSet)) {
std::vector<int> greet_animations = {
29, // wave
48, // nodyes
64, // point
67, // salute
69, // tapfoot
70, // bowto
};
int random_animation = rand() % (greet_animations.size() - 1) + 0;
target->DoAnim(greet_animations[random_animation]);
}
// window expire time
std::string expire_time = get_between(output, "=", "=");
uint32 window_expire_seconds = 0;
if (!expire_time.empty()) {
LogDiaWind("Client [{}] Window expire time is not empty, contents are [{}]", c->GetCleanName(), expire_time);
find_replace(output, fmt::format("={}=", expire_time), "");
// we treat the animation field differently if it is a number
if (StringIsNumber(expire_time)) {
LogDiaWindDetail(
"Client [{}] Window expire time is a number, setting expiration to [{}]",
c->GetCleanName(),
expire_time
);
window_expire_seconds = std::stoi(expire_time);
}
}
uint32 popup_id = POPUPID_DIAWIND_ONE;
uint32 negative_id = POPUPID_DIAWIND_TWO;
std::string button_one_name;
std::string button_two_name;
uint32 sound_controls = 0;
// window type
std::string wintype;
if (markdown.find("wintype:") != std::string::npos) {
LogDiaWind("Client [{}] Rendering wintype option", c->GetCleanName());
auto first_split = split_string(output, "wintype:");
if (!first_split.empty()) {
// assumed that there is more after the wintype declaration
// wintype:0 +animation+ etc.
auto second_split = split_string(first_split[1], " ");
if (!second_split.empty()) {
wintype = second_split[0];
LogDiaWindDetail("Client [{}] Rendering wintype option wintype [{}]", c->GetCleanName(), wintype);
}
// if we're dealing with a string that is at the end
// example wintype:0");
if (first_split[1].length() == 1) {
wintype = first_split[1];
LogDiaWindDetail(
"Client [{}] Rendering wintype (end) option wintype [{}]",
c->GetCleanName(),
wintype
);
}
find_replace(output, fmt::format("wintype:{}", wintype), "");
}
}
// popupid
std::string popupid;
if (markdown.find("popupid:") != std::string::npos) {
LogDiaWind("Client [{}] Rendering popupid option", c->GetCleanName());
auto first_split = split_string(output, "popupid:");
if (!first_split.empty()) {
// assumed that there is more after the popupid declaration
// popupid:0 +animation+ etc.
auto second_split = split_string(first_split[1], " ");
if (!second_split.empty()) {
popupid = second_split[0];
LogDiaWindDetail("Client [{}] Rendering popupid option popupid [{}]", c->GetCleanName(), popupid);
}
// if we're dealing with a string that is at the end
// example popupid:0");
if (first_split[1].length() == 1) {
popupid = first_split[1];
LogDiaWindDetail(
"Client [{}] Rendering popupid (end) option popupid [{}]",
c->GetCleanName(),
popupid
);
}
find_replace(output, fmt::format("popupid:{}", popupid), "");
// set the popup id
if (!popupid.empty()) {
popup_id = (StringIsNumber(popupid) ? std::atoi(popupid.c_str()) : 0);
}
}
}
// secondresponseid
std::string secondresponseid;
if (markdown.find("secondresponseid:") != std::string::npos) {
LogDiaWind("Client [{}] Rendering secondresponseid option", c->GetCleanName());
auto first_split = split_string(output, "secondresponseid:");
if (!first_split.empty()) {
auto second_split = split_string(first_split[1], " ");
if (!second_split.empty()) {
secondresponseid = second_split[0];
LogDiaWindDetail("Client [{}] Rendering secondresponseid option secondresponseid [{}]",
c->GetCleanName(),
secondresponseid);
}
if (first_split[1].length() == 1) {
secondresponseid = first_split[1];
LogDiaWindDetail(
"Client [{}] Rendering secondresponseid (end) option secondresponseid [{}]",
c->GetCleanName(),
secondresponseid
);
}
find_replace(output, fmt::format("secondresponseid:{}", secondresponseid), "");
if (!secondresponseid.empty()) {
negative_id = (StringIsNumber(secondresponseid) ? std::atoi(secondresponseid.c_str()) : 0);
}
}
}
// Buttons Text
std::string button_one;
std::string button_two;
if (
markdown.find("{button_one:") != std::string::npos &&
markdown.find("{button_two:") != std::string::npos
) {
LogDiaWind("Client [{}] Rendering button_one option.", c->GetCleanName());
button_one = get_between(output, "{button_one:", "}");
LogDiaWind("Client [{}] button_one [{}]", c->GetCleanName(), button_one);
if (!button_one.empty()) {
find_replace(output, fmt::format("{{button_one:{}}}", button_one), "");
button_one_name = trim(button_one);
}
button_two = get_between(output, "{button_two:", "}");
LogDiaWind("Client [{}] button_two [{}]", c->GetCleanName(), button_two);
if (!button_two.empty()) {
find_replace(output, fmt::format("{{button_two:{}}}", button_two), "");
button_two_name = trim(button_two);
}
LogDiaWind(
"Client [{}] Rendering buttons button_one [{}] button_two [{}]",
c->GetCleanName(),
button_one,
button_two
);
}
// bracket responses
std::vector<std::string> responses;
std::vector<std::string> bracket_responses;
if (markdown.find('[') != std::string::npos && markdown.find(']') != std::string::npos) {
// record any saylinks that may be in saylink form
std::string strip_saylinks = output;
std::map<std::string, std::string> replacements = {};
while (strip_saylinks.find('[') != std::string::npos && strip_saylinks.find(']') != std::string::npos) {
std::string bracket_message = get_between(strip_saylinks, "[", "]");
// strip saylinks and normalize to [regular message]
size_t link_open = bracket_message.find('\x12');
size_t link_close = bracket_message.find_last_of('\x12');
if (link_open != link_close && (bracket_message.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) {
replacements.insert(
std::pair<std::string, std::string>(
bracket_message,
bracket_message.substr(EQ::constants::SAY_LINK_BODY_SIZE + 1)
)
);
}
find_replace(strip_saylinks, fmt::format("[{}]", bracket_message), "");
}
// write replacement strips
for (auto &replacement: replacements) {
find_replace(output, replacement.first, replacement.second.substr(0, replacement.second.size() - 1));
}
// copy
std::string content = output;
// while brackets still exist
int response_index = 0;
while (content.find('[') != std::string::npos && content.find(']') != std::string::npos) {
std::string bracket_message = get_between(content, "[", "]");
LogDiaWindDetail(
"Client [{}] Rendering responses ({}) [{}]",
c->GetCleanName(),
response_index,
bracket_message
);
// pop message onto responses
responses.emplace_back(bracket_message);
// pop the response off of the message
find_replace(content, fmt::format("[{}]", bracket_message), "");
// too many iterations / safety net
if (response_index > 100) {
break;
}
response_index++;
}
}
// build saylinks
if (responses.size() > 1) {
for (auto &r: responses) {
bracket_responses.emplace_back(
fmt::format("[{}]", EQ::SayLinkEngine::GenerateQuestSaylink(r, false, r))
);
}
}
// Placed here to allow silent message or other message to override default for custom values.
if (!button_one_name.empty() && !button_two_name.empty()) {
c->SetEntityVariable(
DIAWIND_RESPONSE_ONE_KEY.c_str(),
button_one_name.c_str()
);
c->SetEntityVariable(
DIAWIND_RESPONSE_TWO_KEY.c_str(),
button_two_name.c_str()
);
}
// handle silent prompts from the [> silent syntax
std::string silent_message;
if (responses.empty() && markdown.find('[') != std::string::npos && markdown.find('>') != std::string::npos) {
silent_message = get_between(output, "[", ">");
// temporary and used during the response
c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), silent_message.c_str());
// pop the silent message off
find_replace(output, fmt::format("[{}>", silent_message), "");
}
else if (!responses.empty()) {
// handle silent prompts from the single respond bracket syntax []
silent_message = responses[0];
// temporary and used during the response
c->SetEntityVariable(DIAWIND_RESPONSE_ONE_KEY.c_str(), silent_message.c_str());
// pop the silent message off
find_replace(output, fmt::format("[{}]", silent_message), "");
}
// strip brackets
if (render_nobracket) {
find_replace(output, "[", "");
find_replace(output, "]", "");
}
// render title
std::string title;
std::string speaking;
if (target) {
speaking = fmt::format("{} says", target->GetCleanName());
}
if (render_mysterious_voice) {
speaking = "A Mysterious Voice says";
}
// title
std::string popup_title;
if (markdown.find("{title:") != std::string::npos) {
popup_title = get_between(output, "{title:", "}");
LogDiaWind("Client [{}] Rendering title option title [{}]", c->GetCleanName(), popup_title);
if (!popup_title.empty()) {
find_replace(output, fmt::format("{{title:{}}}", popup_title), "");
title = trim(popup_title);
}
}
if (title.empty()) {
title = fmt::format("Dialogue [{}]", speaking);
}
// render quotes
std::string quote_string = "'";
if (render_noquotes) {
quote_string = "";
}
// click response
// window type response
uint32 window_type = (StringIsNumber(wintype) ? std::atoi(wintype.c_str()) : 0);
std::string click_response_button = (window_type == 1 ? "Yes" : "OK");
std::string click_response = fmt::format(
"<c \"#F07F00\">Click [{}] to continue...</c>",
click_response_button
);
// different response when a timer is set
if (window_expire_seconds > 0) {
click_response = fmt::format(
"<c \"#F07F00\">This message will disappear in {} second(s)...</c>",
window_expire_seconds
);
}
// respond with silent message
if (!silent_message.empty()) {
click_response = fmt::format(
"<c \"#F07F00\">Click [{}] to respond with [{}]...</c>",
click_response_button,
silent_message
);
}
if (!button_one_name.empty() && !button_two_name.empty()) {
click_response = fmt::format(
"<c \"#F07F00\">Click [{}] to respond with [{}]...<br>"
"Click [{}] to respond with [{}]...</c>",
button_one_name,
button_one_name,
button_two_name,
button_two_name
);
}
// post processing of color markdowns
// {spring_green_1} = <c "#5EFB6E">
if (markdown.find('{') != std::string::npos && markdown.find('}') != std::string::npos) {
// while brackets still exist
int tag_index = 0;
while (output.find('{') != std::string::npos && output.find('}') != std::string::npos) {
std::string color_tag = get_between(output, "{", "}");
LogDiaWindDetail(
"Client [{}] Rendering color tags ({}) [{}]",
c->GetCleanName(),
tag_index,
color_tag
);
std::string html_tag;
for (const auto &color : html_colors) {
if (color_tag.find(color.first) != std::string::npos) {
// build html tag
html_tag = fmt::format("<c \"{}\">", color.second);
// pop the response off of the message
find_replace(output, fmt::format("{{{}}}", color.first), html_tag);
}
}
// too many iterations / safety net
if (tag_index > 100) {
break;
}
tag_index++;
}
}
// build the final output string
std::string final_output;
final_output = fmt::format("{}{}{} <br><br> {}", quote_string, trim(output), quote_string, click_response);
if (render_hiddenresponse) {
final_output = fmt::format("{}{}{}", quote_string, trim(output), quote_string);
}
// send popup
c->SendFullPopup(
title.c_str(),
final_output.c_str(),
popup_id,
negative_id,
window_type,
window_expire_seconds,
button_one_name.c_str(),
button_two_name.c_str(),
sound_controls
);
// if multiple brackets are presented, send message
if (!bracket_responses.empty()) {
c->Message(Chat::White, " --- Select Response from Options --- ");
c->Message(Chat::White, implode(" ", bracket_responses).c_str());
}
}
+394
View File
@@ -0,0 +1,394 @@
#ifndef EQEMU_DIALOGUE_WINDOW_H
#define EQEMU_DIALOGUE_WINDOW_H
#include <string>
#include "client.h"
static const std::map<std::string, std::string> html_colors = {
{"black", "#000000"},
{"brown", "#804000"},
{"burgundy", "#800000"},
{"cadet_blue", "#77BFC7"},
{"cadet_blue_1", "#4C787E"},
{"chartreuse", "#8AFB17"},
{"chartreuse_1", "#7FE817"},
{"chartreuse_2", "#6CC417"},
{"chartreuse_3", "#437C17"},
{"chocolate", "#C85A17"},
{"coral", "#F76541"},
{"coral_1", "#E55B3C"},
{"coral_2", "#C34A2C"},
{"cornflower_blue", "#151B8D"},
{"cyan", "#00FFFF"},
{"cyan_1", "#57FEFF"},
{"cyan_2", "#50EBEC"},
{"cyan_3", "#46C7C7"},
{"cyan_4", "#307D7E"},
{"dark_blue", "#0000A0"},
{"dark_goldenrod", "#AF7817"},
{"dark_goldenrod_1", "#FBB117"},
{"dark_goldenrod_2", "#E8A317"},
{"dark_goldenrod_3", "#C58917"},
{"dark_goldenrod_4", "#7F5217"},
{"dark_green", "#254117"},
{"dark_grey", "#808080"},
{"dark_olive_green", "#CCFB5D"},
{"dark_olive_green_2", "#BCE954"},
{"dark_olive_green_3", "#A0C544"},
{"dark_olive_green_4", "#667C26"},
{"dark_orange", "#F88017"},
{"dark_orange_1", "#F87217"},
{"dark_orange_2", "#E56717"},
{"dark_orange_3", "#7E3117"},
{"dark_orange_3", "#C35617"},
{"dark_orchid", "#7D1B7E"},
{"dark_orchid_1", "#B041FF"},
{"dark_orchid_2", "#A23BEC"},
{"dark_orchid_3", "#8B31C7"},
{"dark_orchid_4", "#571B7e"},
{"dark_purple", "#800080"},
{"dark_salmon", "#E18B6B"},
{"dark_sea_green", "#8BB381"},
{"dark_sea_green_1", "#C3FDB8"},
{"dark_sea_green_2", "#B5EAAA"},
{"dark_sea_green_3", "#99C68E"},
{"dark_sea_green_4", "#617C58"},
{"dark_slate_blue", "#2B3856"},
{"dark_slate_gray", "#25383C"},
{"dark_slate_gray_1", "#9AFEFF"},
{"dark_slate_gray_2", "#8EEBEC"},
{"dark_slate_gray_3", "#78c7c7"},
{"dark_slate_gray_4", "#4C7D7E"},
{"dark_turquoise", "#3B9C9C"},
{"dark_violet", "#842DCE"},
{"deep_pink", "#F52887"},
{"deep_pink_1", "#E4287C"},
{"deep_pink_2", "#C12267"},
{"deep_pink_3", "#7D053F"},
{"deep_sky_blue", "#3BB9FF"},
{"deep_sky_blue_1", "#38ACEC"},
{"deep_sky_blue_2", "#3090C7"},
{"deep_sky_blue_3", "#25587E"},
{"dim_gray", "#463E41"},
{"dodger_blue", "#1589FF"},
{"dodger_blue_1", "#157DEC"},
{"dodger_blue_2", "#1569C7"},
{"dodger_blue_3", "#153E7E"},
{"firebrick", "#800517"},
{"firebrick_1", "#F62817"},
{"firebrick_2", "#E42217"},
{"firebrick_3", "#C11B17"},
{"forest_green", "#4E9258"},
{"forest_green_1", "#808000"},
{"gold", "#D4A017"},
{"gold_1", "#FDD017"},
{"gold_2", "#EAC117"},
{"gold_3", "#C7A317"},
{"gold_4", "#806517"},
{"goldenrod", "#EDDA74"},
{"goldenrod_1", "#FBB917"},
{"goldenrod_2", "#E9AB17"},
{"goldenrod_3", "#C68E17"},
{"goldenrod_4", "#805817"},
{"grass_green", "#408080"},
{"gray", "#736F6E"},
{"gray_1", "#150517"},
{"gray_2", "#250517"},
{"gray_3", "#2B1B17"},
{"gray_4", "#302217"},
{"gray_5", "#302226"},
{"gray_6", "#342826"},
{"gray_7", "#34282C"},
{"gray_8", "#382D2C"},
{"gray_9", "#3b3131"},
{"gray_10", "#3E3535"},
{"gray_11", "#413839"},
{"gray_12", "#41383C"},
{"gray_13", "#463E3F"},
{"gray_14", "#4A4344"},
{"gray_15", "#4C4646"},
{"gray_16", "#4E4848"},
{"gray_17", "#504A4B"},
{"gray_18", "#544E4F"},
{"gray_19", "#565051"},
{"gray_19", "#595454"},
{"gray_20", "#5C5858"},
{"gray_21", "#5F5A59"},
{"gray_22", "#625D5D"},
{"gray_23", "#646060"},
{"gray_24", "#666362"},
{"gray_25", "#696565"},
{"gray_26", "#6D6968"},
{"gray_27", "#6E6A6B"},
{"gray_28", "#726E6D"},
{"gray_29", "#747170"},
{"green", "#00FF00"},
{"green_1", "#5FFB17"},
{"green_2", "#59E817"},
{"green_3", "#4CC417"},
{"green_4", "#347C17"},
{"green_yellow", "#B1FB17"},
{"hot_pink", "#F660AB"},
{"hot_pink_1", "#F665AB"},
{"hot_pink_2", "#E45E9D"},
{"hot_pink_3", "#C25283"},
{"hot_pink_4", "#7D2252"},
{"indian_red", "#F75D59"},
{"indian_red_2", "#E55451"},
{"indian_red_3", "#C24641"},
{"indian_red_4", "#7E2217"},
{"khaki", "#ADA96E"},
{"khaki_1", "#FFF380"},
{"khaki_2", "#EDE275"},
{"khaki_3", "#C9BE62"},
{"khaki_4", "#827839"},
{"lavender", "#E3E4FA"},
{"lavender_blush", "#FDEEF4"},
{"lavender_blush_1", "#EBDDE2"},
{"lavender_blush_2", "#C8BBBE"},
{"lavender_blush_3", "#817679"},
{"lawn_green", "#87F717"},
{"lemon_chiffon", "#FFF8C6"},
{"lemon_chiffon_1", "#ECE5B6"},
{"lemon_chiffon_2", "#C9C299"},
{"lemon_chiffon_3", "#827B60"},
{"light_blue", "#0000FF"},
{"light_blue_1", "#ADDFFF"},
{"light_blue_2", "#BDEDFF"},
{"light_blue_3", "#AFDCEC"},
{"light_blue_4", "#95B9C7"},
{"light_blue_5", "#5E767E"},
{"light_coral", "#E77471"},
{"light_cyan", "#E0FFFF"},
{"light_cyan_1", "#CFECEC"},
{"light_cyan_2", "#AFC7C7"},
{"light_cyan_3", "#717D7D"},
{"light_golden", "#ECD672"},
{"light_goldenrod", "#ECD872"},
{"light_goldenrod_1", "#FFE87C"},
{"light_goldenrod_2", "#C8B560"},
{"light_goldenrod_3", "#817339"},
{"light_goldenrod_yellow", "#FAF8CC"},
{"light_grey", "#C0C0C0"},
{"light_pink", "#FAAFBA"},
{"light_pink_1", "#F9A7B0"},
{"light_pink_2", "#E799A3"},
{"light_pink_3", "#C48189"},
{"light_pink_4", "#7F4E52"},
{"light_purple", "#FF0080"},
{"light_salmon", "#F9966B"},
{"light_salmon_1", "#E78A61"},
{"light_salmon_2", "#C47451"},
{"light_salmon_3", "#7F462C"},
{"light_sea_green", "#3EA99F"},
{"light_sky_blue", "#82CAFA"},
{"light_sky_blue_1", "#A0CFEC"},
{"light_sky_blue_2", "#87AFC7"},
{"light_sky_blue_3", "#566D7E"},
{"light_slate_blue", "#736AFF"},
{"light_slate_gray", "#6D7B8D"},
{"light_steel_blue", "#728FCE"},
{"light_steel_blue_1", "#C6DEFF"},
{"light_steel_blue_2", "#B7CEEC"},
{"light_steel_blue_3", "#646D7E"},
{"lime_green", "#41A317"},
{"magenta", "#FF00FF"},
{"magenta_1", "#F433FF"},
{"magenta_2", "#E238EC"},
{"magenta_3", "#C031C7"},
{"maroon", "#810541"},
{"maroon_1", "#F535AA"},
{"maroon_2", "#E3319D"},
{"maroon_3", "#C12283"},
{"maroon_4", "#7D0552"},
{"medium_aquamarine", "#348781"},
{"medium_forest_green", "#347235"},
{"medium_orchid", "#B048B5"},
{"medium_orchid_1", "#D462FF"},
{"medium_orchid_2", "#C45AEC"},
{"medium_orchid_3", "#A74AC7"},
{"medium_orchid_4", "#6A287E"},
{"medium_purple", "#8467D7"},
{"medium_purple_1", "#9E7BFF"},
{"medium_purple_2", "#9172EC"},
{"medium_purple_3", "#7A5DC7"},
{"medium_purple_4", "#4E387E"},
{"medium_sea_green", "#306754"},
{"medium_slate_blue", "#5E5A80"},
{"medium_spring_green", "#348017"},
{"medium_turquoise", "#48CCCD"},
{"medium_violet_red", "#CA226B"},
{"midnight_blue", "#151B54"},
{"orange", "#FF8040"},
{"pale_turquoise", "#92C7C7"},
{"pale_turquoise_1", "#5E7D7E"},
{"pale_violet_red", "#D16587"},
{"pale_violet_red_1", "#F778A1"},
{"pale_violet_red_2", "#E56E94"},
{"pale_violet_red_3", "#C25A7C"},
{"pale_violet_red_4", "#7E354D"},
{"pastel_green", "#00FF00"},
{"pink", "#FAAFBE"},
{"pink_1", "#FF00FF"},
{"pink_2", "#E7A1B0"},
{"pink_3", "#C48793"},
{"pink_4", "#7F525D"},
{"plum", "#B93B8F"},
{"plum_1", "#F9B7FF"},
{"plum_2", "#E6A9EC"},
{"plum_3", "#C38EC7"},
{"plum_4", "#7E587E"},
{"purple", "#8E35EF"},
{"purple_1", "#893BFF"},
{"purple_2", "#7F38EC"},
{"purple_3", "#6C2DC7"},
{"purple_4", "#461B7E"},
{"red", "#FF0000"},
{"red_1", "#F62217"},
{"red_2", "#E41B17"},
{"rosy_brown", "#B38481"},
{"rosy_brown_1", "#FBBBB9"},
{"rosy_brown_2", "#E8ADAA"},
{"rosy_brown_3", "#C5908E"},
{"rosy_brown_4", "#7F5A58"},
{"royal_blue", "#2B60DE"},
{"royal_blue_1", "#306EFF"},
{"royal_blue_2", "#2B65EC"},
{"royal_blue_3", "#2554C7"},
{"royal_blue_4", "#15317E"},
{"salmon_1", "#F88158"},
{"salmon_2", "#E67451"},
{"salmon_3", "#C36241"},
{"salmon_4", "#7E3817"},
{"sandy_brown", "#EE9A4D"},
{"sea_green", "#4E8975"},
{"sea_green_1", "#6AFB92"},
{"sea_green_2", "#64E986"},
{"sea_green_3", "#54C571"},
{"sea_green_4", "#387C44"},
{"sienna", "#8A4117"},
{"sienna_1", "#F87431"},
{"sienna_2", "#E66C2C"},
{"sienna_3", "#C35817"},
{"sienna_4", "#7E3517"},
{"sky_blue", "#82CAFF"},
{"sky_blue_1", "#6698FF"},
{"sky_blue_2", "#79BAEC"},
{"sky_blue_3", "#659EC7"},
{"sky_blue_4", "#41627E"},
{"slate_blue", "#357EC7"},
{"slate_blue_1", "#737CA1"},
{"slate_blue_2", "#6960EC"},
{"slate_blue_3", "#342D7E"},
{"slate_gray", "#657383"},
{"slate_gray_1", "#C2DFFF"},
{"slate_gray_2", "#B4CFEC"},
{"slate_gray_3", "#98AFC7"},
{"slate_gray_4", "#616D7E"},
{"spring_green", "#4AA02C"},
{"spring_green_1", "#5EFB6E"},
{"spring_green_2", "#57E964"},
{"spring_green_3", "#4CC552"},
{"spring_green_4", "#347C2C"},
{"steel_blue", "#4863A0"},
{"steel_blue_1", "#5CB3FF"},
{"steel_blue_2", "#56A5EC"},
{"steel_blue_3", "#488AC7"},
{"steel_blue_4", "#2B547E"},
{"thistle", "#D2B9D3"},
{"thistle_1", "#FCDFFF"},
{"thistle_2", "#E9CFEC"},
{"thistle_3", "#C6AEC7"},
{"thistle_4", "#806D7E"},
{"turquoise", "#00FFFF"},
{"turquoise_1", "#43C6DB"},
{"turquoise_2", "#52F3FF"},
{"turquoise_3", "#4EE2EC"},
{"turquoise_4", "#43BFC7"},
{"violet", "#8D38C9"},
{"violet_red", "#F6358A"},
{"violet_red_1", "#F6358A"},
{"violet_red_2", "#E4317F"},
{"violet_red_3", "#C12869"},
{"violet_red_4", "#7D0541"},
{"white", "#FFFFFF"},
{"yellow", "#FFFF00"},
{"yellow_1", "#FFFC17"},
{"yellow_green", "#52D017"}
};
const std::map<std::string, int> animations = {
{"kick", 1},
{"pierce", 2},
{"2hslash", 3},
{"2hblunt", 4},
{"2hpierce", 4},
{"throw", 5},
{"offhand", 6},
{"bash", 7},
{"mainhand", 8},
{"bow", 9},
{"swim", 10},
{"roundkick", 11},
{"gethit", 12},
{"gethit2", 13},
{"falling", 14},
{"drowning", 15},
{"death", 16},
{"standby", 17},
{"standby2", 18},
{"lunge", 19},
{"jump", 20},
{"falling2", 21},
{"duckwalk", 22},
{"ladderclimb", 23},
{"crouch", 24},
{"swim2", 25},
{"idle", 26},
{"cheer", 27},
{"disgusted", 28},
{"wave", 29},
{"rude", 30},
{"yawn", 31},
{"movetoside", 33},
{"iceslide", 35},
{"kneel", 36},
{"swim3", 37},
{"sit", 38},
{"cast", 42},
{"cast2", 43},
{"cast3", 44},
{"flykick", 45},
{"tigerclaw", 46},
{"eaglestrike", 47},
{"nodyes", 48},
{"shakeno", 49},
{"plead", 50},
{"clap", 51},
{"blush", 52},
{"chuckle", 54},
{"headtilt", 57},
{"dance", 58},
{"disagree", 59},
{"glare", 60},
{"peer", 61},
{"kneel", 62},
{"laugh", 63},
{"point", 64},
{"shrug", 65},
{"handraise", 66},
{"salute", 67},
{"shiver", 68},
{"tapfoot", 69},
{"bowto", 70},
};
class DialogueWindow {
public:
static void Render(Client *c, std::string markdown);
};
#endif //EQEMU_DIALOGUE_WINDOW_H
+71 -126
View File
@@ -42,38 +42,38 @@
extern EntityList entity_list;
extern WorldServer worldserver;
Doors::Doors(const Door *door) :
Doors::Doors(const DoorsRepository::Doors& door) :
close_timer(5000),
m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading),
m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading) {
m_Position(door.pos_x, door.pos_y, door.pos_z, door.heading),
m_Destination(door.dest_x, door.dest_y, door.dest_z, door.dest_heading)
{
strn0cpy(zone_name, door.zone.c_str(), sizeof(zone_name));
strn0cpy(door_name, door.name.c_str(), sizeof(door_name));
strn0cpy(destination_zone_name, door.dest_zone.c_str(), sizeof(destination_zone_name));
strn0cpy(zone_name, door->zone_name, 32);
strn0cpy(door_name, door->door_name, 32);
strn0cpy(destination_zone_name, door->dest_zone, 16);
this->database_id = door->db_id;
this->door_id = door->door_id;
this->incline = door->incline;
this->open_type = door->opentype;
this->guild_id = door->guild_id;
this->lockpick = door->lock_pick;
this->key_item_id = door->keyitem;
this->no_key_ring = door->nokeyring;
this->trigger_door = door->trigger_door;
this->trigger_type = door->trigger_type;
this->triggered = false;
this->door_param = door->door_param;
this->size = door->size;
this->invert_state = door->invert_state;
this->destination_instance_id = door->dest_instance_id;
this->is_ldon_door = door->is_ldon_door;
this->client_version_mask = door->client_version_mask;
database_id = door.id;
door_id = door.doorid;
incline = door.incline;
open_type = door.opentype;
guild_id = door.guild;
lockpick = door.lockpick;
key_item_id = door.keyitem;
no_key_ring = door.nokeyring;
trigger_door = door.triggerdoor;
trigger_type = door.triggertype;
triggered = false;
door_param = door.door_param;
size = door.size;
invert_state = door.invert_state;
destination_instance_id = door.dest_instance;
is_ldon_door = door.is_ldon_door;
client_version_mask = door.client_version_mask;
SetOpenState(false);
close_timer.Disable();
disable_timer = (door->disable_timer == 1 ? true : false);
disable_timer = (door.disable_timer == 1 ? true : false);
}
Doors::Doors(const char *model, const glm::vec4 &position, uint8 open_type, uint16 size) :
@@ -211,13 +211,23 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
uint32 required_key_item = GetKeyItem();
uint8 disable_add_to_key_ring = GetNoKeyring();
uint32 player_has_key = 0;
bool player_has_key = false;
uint32 player_key = 0;
const EQ::ItemInstance *lock_pick_item = sender->GetInv().GetItem(EQ::invslot::slotCursor);
player_has_key = static_cast<uint32>(sender->GetInv().HasItem(required_key_item, 1));
if (player_has_key != INVALID_INDEX) {
// If classic key on cursor rule, check for it, otherwise owning it ok.
if (RuleB(Doors, RequireKeyOnCursor)) {
if (lock_pick_item != nullptr &&
lock_pick_item->GetItem()->ID == required_key_item) {
player_has_key = true;
}
}
else if (sender->GetInv().HasItem(required_key_item, 1) != INVALID_INDEX) {
player_has_key = true;
}
if (player_has_key) {
player_key = required_key_item;
}
@@ -278,8 +288,11 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
/**
* Key required
* If using a lock_pick_item leave messaging to that code below
*/
sender->Message(Chat::LightBlue, "This is locked...");
if (lock_pick_item == nullptr && !IsDoorOpen()) {
sender->Message(Chat::LightBlue, "This is locked...");
}
/**
* GM can always open locks
@@ -325,19 +338,30 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
*/
else if (lock_pick_item != nullptr) {
if (sender->GetSkill(EQ::skills::SkillPickLock)) {
Timer* pick_lock_timer = sender->GetPickLockTimer();
if (lock_pick_item->GetItem()->ItemType == EQ::item::ItemTypeLockPick) {
if (!pick_lock_timer->Check()) {
// Stop full scale mad spamming
safe_delete(outapp);
return;
}
float player_pick_lock_skill = sender->GetSkill(EQ::skills::SkillPickLock);
sender->CheckIncreaseSkill(EQ::skills::SkillPickLock, nullptr, 1);
LogSkills("Client has lockpicks: skill=[{}]", player_pick_lock_skill);
if (GetLockpick() <= player_pick_lock_skill) {
// Stop full scale spamming
pick_lock_timer->Start(1000, true);
if (!IsDoorOpen()) {
sender->CheckIncreaseSkill(EQ::skills::SkillPickLock, nullptr, 1);
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR);
sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK);
} else {
move_door_packet->action = static_cast<uint8>(invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR);
}
sender->MessageString(Chat::LightBlue, DOORS_SUCCESSFUL_PICK);
} else {
sender->MessageString(Chat::LightBlue, DOORS_INSUFFICIENT_SKILL);
safe_delete(outapp);
@@ -383,7 +407,9 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
if (!IsDoorOpen() || (open_type == 58)) {
if (!disable_timer)
close_timer.Start();
SetOpenState(true);
if(strncmp(destination_zone_name, "NONE", strlen("NONE")) == 0)
SetOpenState(true);
} else {
close_timer.Disable();
if (!disable_timer)
@@ -682,106 +708,19 @@ int32 ZoneDatabase::GetDoorsDBCountPlusOne(const char *zone_name, int16 version)
return atoi(row[0]) + 1;
}
bool ZoneDatabase::LoadDoors(int32 door_count, Door *into, const char *zone_name, int16 version) {
std::vector<DoorsRepository::Doors> ZoneDatabase::LoadDoors(const std::string& zone_name, int16 version)
{
LogInfo("Loading Doors from database");
std::string query = StringFormat(
" SELECT "
" id, "
" doorid, "
" zone, "
" NAME, "
" pos_x, "
" pos_y, "
" pos_z, "
" heading, "
" opentype, "
" guild, "
" lockpick, "
" keyitem, "
" nokeyring, "
" triggerdoor, "
" triggertype, "
" dest_zone, "
" dest_instance, "
" dest_x, "
" dest_y, "
" dest_z, "
" dest_heading, "
" door_param, "
" invert_state, "
" incline, "
" size, "
" is_ldon_door, "
" client_version_mask, "
" disable_timer "
" FROM "
" doors "
" WHERE "
" zone = '%s' "
" AND ( version = % u OR version = - 1 ) "
" %s "
" ORDER BY "
" doorid ASC ",
zone_name,
version,
ContentFilterCriteria::apply().c_str()
);
auto results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
auto door_entries = DoorsRepository::GetWhere(*this, fmt::format(
"zone = '{}' AND (version = {} OR version = -1) {} ORDER BY doorid ASC",
zone_name, version, ContentFilterCriteria::apply()));
int32 row_index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++row_index) {
if (row_index >= door_count) {
std::cerr << "Error, Door Count of " << door_count << " exceeded." << std::endl;
break;
}
LogDoors("Loaded [{}] doors for [{}] version [{}]", door_entries.size(), zone_name, version);
memset(&into[row_index], 0, sizeof(Door));
strn0cpy(into[row_index].zone_name, row[2], 32);
strn0cpy(into[row_index].door_name, row[3], 32);
strn0cpy(into[row_index].dest_zone, row[15], 32);
into[row_index].db_id = static_cast<uint32>(atoi(row[0]));
into[row_index].door_id = static_cast<uint8>(atoi(row[1]));
into[row_index].pos_x = (float) atof(row[4]);
into[row_index].pos_y = (float) atof(row[5]);
into[row_index].pos_z = (float) atof(row[6]);
into[row_index].heading = (float) atof(row[7]);
into[row_index].opentype = static_cast<uint8>(atoi(row[8]));
into[row_index].guild_id = static_cast<uint32>(atoi(row[9]));
into[row_index].lock_pick = static_cast<uint16>(atoi(row[10]));
into[row_index].keyitem = static_cast<uint32>(atoi(row[11]));
into[row_index].nokeyring = static_cast<uint8>(atoi(row[12]));
into[row_index].trigger_door = static_cast<uint8>(atoi(row[13]));
into[row_index].trigger_type = static_cast<uint8>(atoi(row[14]));
into[row_index].dest_instance_id = static_cast<uint32>(atoi(row[16]));
into[row_index].dest_x = (float) atof(row[17]);
into[row_index].dest_y = (float) atof(row[18]);
into[row_index].dest_z = (float) atof(row[19]);
into[row_index].dest_heading = (float) atof(row[20]);
into[row_index].door_param = static_cast<uint32>(atoi(row[21]));
into[row_index].invert_state = atoi(row[22]);
into[row_index].incline = atoi(row[23]);
into[row_index].size = static_cast<uint16>(atoi(row[24]));
into[row_index].is_ldon_door = static_cast<uint8>(atoi(row[25]));
into[row_index].client_version_mask = (uint32) strtoul(row[26], nullptr, 10);
into[row_index].disable_timer = static_cast<uint8>(atoi(row[27]));
Log(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u disable_timer: %i",
into[row_index].db_id,
into[row_index].door_id,
into[row_index].disable_timer
);
}
return true;
return door_entries;
}
void Doors::SetLocation(float x, float y, float z)
{
entity_list.DespawnAllDoors();
@@ -801,6 +740,12 @@ void Doors::SetIncline(int in) {
entity_list.RespawnAllDoors();
}
void Doors::SetInvertState(int in) {
entity_list.DespawnAllDoors();
invert_state = in;
entity_list.RespawnAllDoors();
}
void Doors::SetOpenType(uint8 in) {
entity_list.DespawnAllDoors();
open_type = in;
+3 -6
View File
@@ -1,12 +1,8 @@
#ifndef DOORS_H
#define DOORS_H
#include "../common/emu_opcodes.h"
#include "../common/eq_packet_structs.h"
#include "../common/linked_list.h"
#include "mob.h"
#include "zonedump.h"
#include "../common/repositories/doors_repository.h"
class Client;
class Mob;
@@ -19,7 +15,7 @@ public:
~Doors();
Doors(const char *model, const glm::vec4& position, uint8 open_type = 58, uint16 size = 100);
Doors(const Door* door);
Doors(const DoorsRepository::Doors& door);
bool GetDisableTimer() { return disable_timer; }
bool IsDoor() const { return true; }
@@ -54,6 +50,7 @@ public:
void SetDoorName(const char *name);
void SetEntityID(uint32 entity) { entity_id = entity; }
void SetIncline(int in);
void SetInvertState(int in);
void SetKeyItem(uint32 in) { key_item_id = in; }
void SetLocation(float x, float y, float z);
void SetLockpick(uint16 in) { lockpick = in; }
+660 -425
View File
File diff suppressed because it is too large Load Diff
+47 -82
View File
@@ -21,107 +21,72 @@
#ifndef DYNAMIC_ZONE_H
#define DYNAMIC_ZONE_H
#include <chrono>
#include "../common/dynamic_zone_base.h"
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
class MySQLRequestRow;
class Client;
class Database;
class EQApplicationPacket;
class ServerPacket;
enum class DynamicZoneType : uint8_t
{
None = 0,
Expedition,
Tutorial,
Task,
Mission, // Shared Task
Quest
};
extern const char* const CREATE_NOT_ALL_ADDED;
struct DynamicZoneLocation
{
uint32_t zone_id = 0;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float heading = 0.0f;
DynamicZoneLocation() = default;
DynamicZoneLocation(uint32_t zone_id_, float x_, float y_, float z_, float heading_)
: zone_id(zone_id_), x(x_), y(y_), z(z_), heading(heading_) {}
};
class DynamicZone
class DynamicZone : public DynamicZoneBase
{
public:
using DynamicZoneBase::DynamicZoneBase; // inherit base constructors
DynamicZone() = default;
DynamicZone(uint32_t zone_id, uint32_t version, uint32_t duration, DynamicZoneType type);
DynamicZone(std::string zone_shortname, uint32_t version, uint32_t duration, DynamicZoneType type);
DynamicZone(uint32_t dz_id) : m_id(dz_id) {}
DynamicZone(DynamicZoneType type) : m_type(type) {}
static std::unordered_map<uint32_t, DynamicZone> LoadMultipleDzFromDatabase(
const std::vector<uint32_t>& dynamic_zone_ids);
static void CacheAllFromDatabase();
static void CacheNewDynamicZone(ServerPacket* pack);
static DynamicZone* CreateNew(DynamicZone& dz_details, const std::vector<DynamicZoneMember>& members);
static DynamicZone* FindDynamicZoneByID(uint32_t dz_id);
static void HandleWorldMessage(ServerPacket* pack);
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint32_t GetID() const { return m_id; }
uint16_t GetInstanceID() const { return static_cast<uint16_t>(m_instance_id); }
uint32_t GetSecondsRemaining() const;
uint16_t GetZoneID() const { return static_cast<uint16_t>(m_zone_id); }
uint32_t GetZoneIndex() const { return (m_instance_id << 16) | (m_zone_id & 0xffff); }
uint32_t GetZoneVersion() const { return m_version; }
const std::string& GetLeaderName() const { return m_leader_name; }
const std::string& GetName() const { return m_name; }
DynamicZoneType GetType() const { return m_type; }
DynamicZoneLocation GetCompassLocation() const { return m_compass; }
DynamicZoneLocation GetSafeReturnLocation() const { return m_safereturn; }
DynamicZoneLocation GetZoneInLocation() const { return m_zonein; }
void SetSecondsRemaining(uint32_t seconds_remaining) override;
void AddCharacter(uint32_t character_id);
uint32_t Create();
uint32_t CreateInstance();
bool HasZoneInLocation() const { return m_has_zonein; }
bool IsCurrentZoneDzInstance() const;
bool IsInstanceID(uint32_t instance_id) const;
bool IsValid() const { return m_instance_id != 0; }
bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const;
void RemoveAllCharacters(bool enable_removal_timers = true);
void RemoveCharacter(uint32_t character_id);
void SaveInstanceMembersToDatabase(const std::vector<uint32_t>& character_ids);
void SendInstanceCharacterChange(uint32_t character_id, bool removed);
void SetCompass(const DynamicZoneLocation& location, bool update_db = false);
void SetLeaderName(const std::string& leader_name) { m_leader_name = leader_name; }
void SetName(const std::string& name) { m_name = name; }
void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false);
void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false);
void SetUpdatedDuration(uint32_t seconds);
void DoAsyncZoneMemberUpdates();
bool CanClientLootCorpse(Client* client, uint32_t npc_type_id, uint32_t entity_id);
bool IsCurrentZoneDzInstance() const;
void RegisterOnClientAddRemove(std::function<void(Client* client, bool removed, bool silent)> on_client_addremove);
void SendClientWindowUpdate(Client* client);
void SendLeaderNameToZoneMembers();
void SendMemberListToZoneMembers();
void SendMemberListNameToZoneMembers(const std::string& char_name, bool remove);
void SendMemberListStatusToZoneMembers(const DynamicZoneMember& member);
void SendRemoveAllMembersToZoneMembers(bool silent) { ProcessRemoveAllMembers(silent); }
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateLeaderNamePacket();
std::unique_ptr<EQApplicationPacket> CreateMemberListPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateMemberListNamePacket(const std::string& name, bool remove_name);
std::unique_ptr<EQApplicationPacket> CreateMemberListStatusPacket(const std::string& name, DynamicZoneMemberStatus status);
protected:
uint16_t GetCurrentInstanceID() override;
uint16_t GetCurrentZoneID() override;
Database& GetDatabase() override;
void ProcessCompassChange(const DynamicZoneLocation& location) override;
void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed) override;
bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status) override;
void ProcessRemoveAllMembers(bool silent = false) override;
bool SendServerPacket(ServerPacket* packet) override;
private:
static std::string DynamicZoneSelectQuery();
void LoadDatabaseResult(MySQLRequestRow& row);
void SaveCompassToDatabase();
void SaveSafeReturnToDatabase();
void SaveZoneInLocationToDatabase();
uint32_t SaveToDatabase();
static void StartAllClientRemovalTimers();
void ProcessLeaderChanged(uint32_t new_leader_id);
void SendCompassUpdateToZoneMembers();
void SendMembersExpireWarning(uint32_t minutes);
void SendUpdatesToZoneMembers(bool removing_all = false, bool silent = true);
void SetUpdatedDuration(uint32_t seconds);
uint32_t m_id = 0;
uint32_t m_zone_id = 0;
uint32_t m_instance_id = 0;
uint32_t m_version = 0;
bool m_never_expires = false;
bool m_has_zonein = false;
std::string m_name;
std::string m_leader_name;
DynamicZoneType m_type{ DynamicZoneType::None };
DynamicZoneLocation m_compass;
DynamicZoneLocation m_safereturn;
DynamicZoneLocation m_zonein;
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
std::function<void(Client*, bool, bool)> m_on_client_addremove;
};
#endif
+360 -243
View File
@@ -44,18 +44,16 @@ float Mob::GetActSpellRange(uint16 spell_id, float range, bool IsBard)
int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].targettype == ST_Self)
if (spells[spell_id].target_type == ST_Self)
return value;
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusDMG()/100;
Critical = false; //Mitch removed bool
int32 value_BaseEffect = 0;
bool Critical = false;
int32 base_value = value;
int chance = 0;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= (GetLevel() - 40) * 20;
@@ -99,15 +97,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (Critical){
value = value_BaseEffect*ratio/100;
value = base_value*ratio/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100;
if (target) {
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
@@ -115,12 +114,13 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt2, spell_id);
value -= GetFocusEffect(focusFcAmplifyAmt, spell_id);
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio / 100;
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio / 100;
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
else if (IsNPC() && CastToNPC()->GetSpellScale())
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
@@ -136,15 +136,16 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
}
//Non Crtical Hit Calculation pathway
value = value_BaseEffect;
value = base_value;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100;
if (target) {
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
value += base_value*target->GetVulnerability(this, spell_id, 0)/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
@@ -152,12 +153,13 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt2, spell_id);
value -= GetFocusEffect(focusFcAmplifyAmt, spell_id);
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
if (IsNPC() && CastToNPC()->GetSpellScale())
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
@@ -165,15 +167,43 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
return value;
}
int32 Mob::GetActReflectedSpellDamage(int32 spell_id, int32 value, int effectiveness) {
/*
Reflected spells use the spells base damage before any modifiers or formulas applied.
That value can then be modifier by the reflect spells 'max' value, defined here as effectiveness
Default effectiveness is set at 100.
Extra Spell Damage stat from the with the reflect effect will be applied to reflected damage
with no level limitation, this was confirmed with extensive parsing ~Kayen
*/
if (IsNPC()) {
value += value * CastToNPC()->GetSpellFocusDMG() / 100;
if (CastToNPC()->GetSpellScale()) {
value = int(static_cast<float>(value) * CastToNPC()->GetSpellScale() / 100.0f);
}
}
int32 base_spell_dmg = value;
value = value * effectiveness / 100;
if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_spell_dmg);
}
return value;
}
int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
return value;
if (IsNPC()) {
value += value * CastToNPC()->GetSpellFocusDMG() / 100;
}
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusDMG()/100;
int32 value_BaseEffect = 0;
int32 base_value = value;
int32 extra_dmg = 0;
int16 chance = 0;
chance += itembonuses.CriticalDoTChance + spellbonuses.CriticalDoTChance + aabonuses.CriticalDoTChance;
@@ -184,23 +214,32 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
chance = spells[spell_id].override_crit_chance;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
if (chance > 0 && (zone->random.Roll(chance))) {
if (!spells[spell_id].good_effect && chance > 0 && (zone->random.Roll(chance))) {
int32 ratio = 200;
ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease;
value = value_BaseEffect*ratio/100;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value = base_value*ratio/100;
value += int(base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
value += int(base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100)*ratio/100;
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio/100;
value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) +
GetFocusEffect(focusFcDamageAmt, spell_id) +
GetFocusEffect(focusFcDamageAmt2, spell_id);
GetFocusEffect(focusFcDamageAmt2, spell_id) +
GetFocusEffect(focusFcAmplifyAmt, spell_id);
if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
}
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100;
}
}
if (extra_dmg) {
int duration = CalcBuffDuration(this, this, spell_id);
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0)
extra_dmg /= duration;
}
@@ -209,18 +248,29 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
}
else {
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
value = base_value;
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100;
value += base_value*target->GetVulnerability(this, spell_id, 0)/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
GetFocusEffect(focusFcDamageAmtCrit, spell_id) +
GetFocusEffect(focusFcDamageAmt, spell_id) +
GetFocusEffect(focusFcDamageAmt2, spell_id);
GetFocusEffect(focusFcDamageAmt2, spell_id) +
GetFocusEffect(focusFcAmplifyAmt, spell_id);
if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
}
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
}
}
if (extra_dmg) {
int duration = CalcBuffDuration(this, this, spell_id);
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0)
extra_dmg /= duration;
}
@@ -253,69 +303,104 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s
else
extra_spell_amt = extra_spell_amt * total_cast_time / 7000;
if(extra_spell_amt*2 < base_spell_dmg)
return 0;
//Confirmed with parsing 10/9/21 ~Kayen
if (extra_spell_amt * 2 > abs(base_spell_dmg)) {
extra_spell_amt = abs(base_spell_dmg) / 2;
}
return extra_spell_amt;
return extra_spell_amt;
}
int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
target = this;
if (IsNPC())
value += value*CastToNPC()->GetSpellFocusHeal()/100;
if (IsNPC()) {
value += value * CastToNPC()->GetSpellFocusHeal() / 100;
}
int32 value_BaseEffect = 0;
int16 chance = 0;
int8 modifier = 1;
Critical = false; //mitch
Critical = false;
int32 base_value = value;
int16 critical_chance = 0;
int8 critical_modifier = 1;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
if (spells[spell_id].buff_duration < 1) {
critical_chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
value = value_BaseEffect;
if (spellbonuses.CriticalHealDecay) {
critical_chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
}
}
else {
critical_chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
if (spellbonuses.CriticalRegenDecay) {
critical_chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
}
}
// Instant Heals
if(spells[spell_id].buffduration < 1) {
if (critical_chance) {
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalHealDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
chance = spells[spell_id].override_crit_chance;
if(chance && (zone->random.Roll(chance))) {
Critical = true;
modifier = 2; //At present time no critical heal amount modifier SPA exists.
if (spells[spell_id].override_crit_chance > 0 && critical_chance > spells[spell_id].override_crit_chance) {
critical_chance = spells[spell_id].override_crit_chance;
}
value *= modifier;
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if (zone->random.Roll(critical_chance)) {
critical_modifier = 2; //At present time no critical heal amount modifier SPA exists.
}
}
if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%17) - 1] >= GetLevel() - 5)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
if (GetClass() == CLERIC) {
value += int(base_value*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus
}
value += int(base_value*GetFocusEffect(focusImprovedHeal, spell_id) / 100);
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100);
value += value*target->GetHealRate(spell_id, this)/100;
// Instant Heals
if (spells[spell_id].buff_duration < 1) {
if (IsNPC() && CastToNPC()->GetHealScale())
if (target) {
value += int(base_value * target->GetFocusEffect(focusFcHealPctIncoming, spell_id, this)/100); //SPA 393 Add before critical
value += int(base_value * target->GetFocusEffect(focusFcHealPctCritIncoming, spell_id, this)/100); //SPA 395 Add before critical (?)
}
value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical
//Using IgnoreSpellDmgLvlRestriction to also allow healing to scale
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) {
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical
}
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical
}
if (target) {
value += value * target->GetHealRate() / 100; //SPA 120 modifies value after Focus Applied but before critical
}
/*
Apply critical hit modifier
*/
value *= critical_modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id); //SPA 392 Add after critical
value += GetFocusEffect(focusFcAmplifyAmt, spell_id); //SPA 508 ? Add after critical
if (target) {
value += target->GetFocusEffect(focusFcHealAmtIncoming, spell_id, this); //SPA 394 Add after critical
}
if (IsNPC() && CastToNPC()->GetHealScale()) {
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
}
if (Critical) {
if (critical_modifier > 1) {
entity_list.MessageCloseString(
this, true, 100, Chat::SpellCrit,
OTHER_CRIT_HEAL, GetName(), itoa(value));
if (IsClient())
if (IsClient()) {
MessageString(Chat::SpellCrit, YOU_CRIT_HEAL, itoa(value));
}
}
return value;
@@ -323,20 +408,32 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
else {
//Using IgnoreSpellDmgLvlRestriction to also allow healing to scale
int32 extra_heal = 0;
if (RuleB(Spells, HOTsScaleWithHealAmt)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) {
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);
}
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);
}
}
if (extra_heal) {
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0) {
extra_heal /= duration;
value += extra_heal;
}
}
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalRegenDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
if(chance && zone->random.Roll(chance))
value *= 2;
if (critical_chance && zone->random.Roll(critical_chance))
value *= critical_modifier;
}
if (IsNPC() && CastToNPC()->GetHealScale())
if (IsNPC() && CastToNPC()->GetHealScale()) {
value = int(static_cast<float>(value) * CastToNPC()->GetHealScale() / 100.0f);
}
return value;
}
@@ -409,29 +506,6 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
return ifocused + 1;
}
int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime)
{
int32 cast_reducer = 0;
cast_reducer += GetFocusEffect(focusSpellHaste, spell_id);
//this function loops through the effects of spell_id many times
//could easily be consolidated.
if (GetLevel() >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id)
&& (GetClass() == SHADOWKNIGHT || GetClass() == RANGER
|| GetClass() == PALADIN || GetClass() == BEASTLORD ))
cast_reducer += (GetLevel()-50)*3;
//LIVE AA SpellCastingDeftness, QuickBuff, QuickSummoning, QuickEvacuation, QuickDamage
if (cast_reducer > RuleI(Spells, MaxCastTimeReduction))
cast_reducer = RuleI(Spells, MaxCastTimeReduction);
casttime = (casttime*(100 - cast_reducer)/100);
return casttime;
}
bool Client::TrainDiscipline(uint32 itemid) {
//get the item info
@@ -534,6 +608,90 @@ bool Client::TrainDiscipline(uint32 itemid) {
return(false);
}
bool Client::MemorizeSpellFromItem(uint32 item_id) {
const EQ::ItemData *item = database.GetItem(item_id);
if(item == nullptr) {
Message(Chat::Red, "Unable to find the scroll!");
LogError("Unable to find scroll id [{}]\n", (unsigned long)item_id);
return false;
}
if (!item->IsClassCommon() || item->ItemType != EQ::item::ItemTypeSpell) {
Message(Chat::Red, "Invalid item type, you cannot learn from this item.");
SummonItem(item_id);
return false;
}
if(!(
item->Name[0] == 'S' &&
item->Name[1] == 'p' &&
item->Name[2] == 'e' &&
item->Name[3] == 'l' &&
item->Name[4] == 'l' &&
item->Name[5] == ':' &&
item->Name[6] == ' '
)) {
Message(Chat::Red, "This item is not a scroll.");
SummonItem(item_id);
return false;
}
int player_class = GetClass();
uint32 cbit = 1 << (player_class - 1);
if(!(item->Classes & cbit)) {
Message(Chat::Red, "Your class cannot learn from this scroll.");
SummonItem(item_id);
return false;
}
uint32 spell_id = item->Scroll.Effect;
if(!IsValidSpell(spell_id)) {
Message(Chat::Red, "This scroll contains invalid knowledge.");
return false;
}
const SPDat_Spell_Struct &spell = spells[spell_id];
uint8 level_to_use = spell.classes[player_class - 1];
if(level_to_use == 255) {
Message(Chat::Red, "Your class cannot learn from this scroll.");
SummonItem(item_id);
return false;
}
if(level_to_use > GetLevel()) {
Message(Chat::Red, "You must be at least level %d to learn this spell.", level_to_use);
SummonItem(item_id);
return false;
}
for (int index = 0; index < EQ::spells::SPELLBOOK_SIZE; index++) {
if (!HasSpellScribed(spell_id)) {
auto next_slot = GetNextAvailableSpellBookSlot();
if (next_slot != -1) {
ScribeSpell(spell_id, next_slot);
return true;
}
else {
Message(
Chat::Red,
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
spell_id
);
SummonItem(item_id);
return false;
}
}
else {
Message(Chat::Red, "You already know this spell.");
SummonItem(item_id);
return false;
}
}
Message(Chat::Red, "You have learned too many spells and can learn no more.");
return false;
}
void Client::TrainDiscBySpellID(int32 spell_id)
{
int i;
@@ -570,8 +728,15 @@ void Client::SendDisciplineUpdate() {
bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
// Dont let client waste a reuse timer if they can't use the disc
if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet())
if ((IsStunned() && !IgnoreCastingRestriction(spell_id))||
IsFeared() ||
(IsMezzed() && !IgnoreCastingRestriction(spell_id)) ||
IsAmnesiad() ||
IsPet())
{
if (IsAmnesiad()) {
MessageString(Chat::Red, MELEE_SILENCE);
}
return(false);
}
@@ -590,6 +755,10 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
return(false);
}
if (DivineAura() && !IgnoreCastingRestriction(spell_id)) {
return false;
}
//can we use the spell?
const SPDat_Spell_Struct &spell = spells[spell_id];
uint8 level_to_use = spell.classes[GetClass() - 1];
@@ -605,7 +774,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
return(false);
}
if(GetEndurance() < spell.EndurCost) {
if(GetEndurance() < spell.endurance_cost) {
Message(11, "You are too fatigued to use this skill right now.");
return(false);
}
@@ -617,22 +786,26 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
// the client does this check before calling CastSpell, should prevent discs being eaten
if (spell.buffdurationformula != 0 && spell.targettype == ST_Self && HasDiscBuff())
if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && HasDiscBuff())
return false;
//Check the disc timer
pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.EndurTimerIndex;
pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.timer_id;
if(!p_timers.Expired(&database, DiscTimer, false)) { // lets not set the reuse timer in case CastSpell fails (or we would have to turn off the timer, but CastSpell will set it as well)
/*char val1[20]={0};*/ //unused
/*char val2[20]={0};*/ //unused
uint32 remain = p_timers.GetRemainingTime(DiscTimer);
//MessageString(Chat::White, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2));
Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60));
return(false);
uint32 remaining_time = p_timers.GetRemainingTime(DiscTimer);
Message(
Chat::White,
fmt::format(
"You can use this discipline in {}.",
ConvertSecondsToTime(remaining_time)
).c_str()
);
return false;
}
if(spell.recast_time > 0)
{
bool instant_recast = true;
if (spell.recast_time > 0) {
uint32 reduced_recast = spell.recast_time / 1000;
auto focus = GetFocusEffect(focusReduceRecastTime, spell_id);
// do stupid stuff because custom servers.
@@ -643,22 +816,36 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
reduced_recast = 0;
if (GetPTimers().Enabled((uint32)DiscTimer))
GetPTimers().Clear(&database, (uint32)DiscTimer);
} else {
}
else {
reduced_recast -= focus;
}
if (reduced_recast > 0)
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
else{
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
return true;
}
if (reduced_recast > 0) {
instant_recast = false;
SendDisciplineTimer(spells[spell_id].EndurTimerIndex, reduced_recast);
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false);
}
}
else {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
}
SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast);
}
}
else
{
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
if (instant_recast) {
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false);
}
}
else {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
}
}
return(true);
}
@@ -666,7 +853,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
uint32 Client::GetDisciplineTimer(uint32 timer_id) {
pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id;
uint32 disc_timer = 0;
if (GetPTimers().Enabled((uint32)disc_timer_id)) {
if (GetPTimers().Enabled(disc_timer_id)) {
disc_timer = GetPTimers().GetRemainingTime(disc_timer_id);
}
return disc_timer;
@@ -674,12 +861,22 @@ uint32 Client::GetDisciplineTimer(uint32 timer_id) {
void Client::ResetDisciplineTimer(uint32 timer_id) {
pTimerType disc_timer_id = pTimerDisciplineReuseStart + timer_id;
if (GetPTimers().Enabled((uint32)disc_timer_id)) {
GetPTimers().Clear(&database, (uint32)disc_timer_id);
if (GetPTimers().Enabled(disc_timer_id)) {
GetPTimers().Clear(&database, disc_timer_id);
}
SendDisciplineTimer(timer_id, 0);
}
void Client::ResetAllDisciplineTimers() {
for (pTimerType disc_timer_id = pTimerDisciplineReuseStart; disc_timer_id <= pTimerDisciplineReuseEnd; disc_timer_id++) {
uint32 current_timer_id = (disc_timer_id - pTimerDisciplineReuseStart);
if (GetPTimers().Enabled(disc_timer_id)) {
GetPTimers().Clear(&database, disc_timer_id);
}
SendDisciplineTimer(current_timer_id, 0);
}
}
bool Client::HasDisciplineLearned(uint16 spell_id) {
bool has_learned = false;
for (auto index = 0; index < MAX_PP_DISCIPLINES; ++index) {
@@ -763,7 +960,7 @@ void EntityList::AESpell(
)
{
const auto &cast_target_position =
spells[spell_id].targettype == ST_Ring ?
spells[spell_id].target_type == ST_Ring ?
caster_mob->GetTargetRingLocation() :
static_cast<glm::vec3>(center_mob->GetPosition());
@@ -786,14 +983,14 @@ void EntityList::AESpell(
/**
* Max AOE targets
*/
int max_targets_allowed = 0; // unlimited
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
if (max_targets) { // rains pass this in since they need to preserve the count through waves
max_targets_allowed = *max_targets;
}
else if (spells[spell_id].aemaxtargets) {
max_targets_allowed = spells[spell_id].aemaxtargets;
else if (spells[spell_id].aoe_max_targets) {
max_targets_allowed = spells[spell_id].aoe_max_targets;
}
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc) {
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) {
max_targets_allowed = 4;
}
@@ -823,15 +1020,15 @@ void EntityList::AESpell(
continue;
}
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) {
continue;
}
if (spells[spell_id].targettype == ST_AreaClientOnly && !current_mob->IsClient()) {
if (spells[spell_id].target_type == ST_AreaClientOnly && !current_mob->IsClient()) {
continue;
}
if (spells[spell_id].targettype == ST_AreaNPCOnly && !current_mob->IsNPC()) {
if (spells[spell_id].target_type == ST_AreaNPCOnly && !current_mob->IsNPC()) {
continue;
}
@@ -865,21 +1062,21 @@ void EntityList::AESpell(
}
if (is_npc && current_mob->IsNPC() &&
spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting
spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob);
if (is_detrimental_spell) {
//affect mobs that are on our hate list, or
//which have bad faction with us
if (
!(caster_mob->CheckAggro(current_mob) ||
faction_value == FACTION_THREATENLY ||
faction_value == FACTION_THREATENINGLY ||
faction_value == FACTION_SCOWLS)) {
continue;
}
}
else {
//only affect mobs we would assist.
if (!(faction_value <= FACTION_AMIABLE)) {
if (!(faction_value <= FACTION_AMIABLY)) {
continue;
}
}
@@ -919,6 +1116,9 @@ void EntityList::AESpell(
}
}
current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
caster_mob->SpellOnTarget(spell_id, current_mob, 0, true, resist_adjust);
/**
* Increment hit count if max targets
*/
@@ -928,9 +1128,6 @@ void EntityList::AESpell(
break;
}
}
current_mob->CalcSpellPowerDistanceMod(spell_id, distance_to_target);
caster_mob->SpellOnTarget(spell_id, current_mob, false, true, resist_adjust);
}
LogAoeCast("Done iterating [{}]", caster_mob->GetCleanName());
@@ -1001,90 +1198,6 @@ void EntityList::MassGroupBuff(
}
}
/**
* Causes caster to hit every mob within dist range of center with a bard pulse of spell_id
* NPC spells will only affect other NPCs with compatible faction
*
* @param caster
* @param center
* @param spell_id
* @param affect_caster
*/
void EntityList::AEBardPulse(
Mob *caster,
Mob *center,
uint16 spell_id,
bool affect_caster)
{
Mob *current_mob = nullptr;
float distance = caster->GetAOERange(spell_id);
float distance_squared = distance * distance;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
bool is_npc = caster->IsNPC();
for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
current_mob = it.second;
/**
* Skip self
*/
if (current_mob == center) {
continue;
}
if (current_mob == caster && !affect_caster) {
continue;
}
if (DistanceSquared(center->GetPosition(), current_mob->GetPosition()) > distance_squared) { //make sure they are in range
continue;
}
/**
* check npc->npc casting
*/
if (is_npc && current_mob->IsNPC()) {
FACTION_VALUE faction = current_mob->GetReverseFactionCon(caster);
if (is_detrimental_spell) {
//affect mobs that are on our hate list, or
//which have bad faction with us
if (!(caster->CheckAggro(current_mob) || faction == FACTION_THREATENLY || faction == FACTION_SCOWLS)) {
continue;
}
}
else {
//only affect mobs we would assist.
if (!(faction <= FACTION_AMIABLE)) {
continue;
}
}
}
/**
* LOS
*/
if (is_detrimental_spell) {
if (!center->CheckLosFN(current_mob)) {
continue;
}
}
else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// See notes in AESpell() above for more info.
if (caster->IsAttackAllowed(current_mob, true)) {
continue;
}
if (caster->CheckAggro(current_mob)) {
continue;
}
}
current_mob->BardPulse(spell_id, caster);
}
if (caster->IsClient()) {
caster->CastToClient()->CheckSongSkillIncrease(spell_id);
}
}
/**
* Rampage - Normal and Duration rampages
* NPCs handle it differently in Mob::Rampage
@@ -1100,7 +1213,8 @@ void EntityList::AEAttack(
float distance,
int Hand,
int count,
bool is_from_spell)
bool is_from_spell,
int attack_rounds)
{
Mob *current_mob = nullptr;
float distance_squared = distance * distance;
@@ -1112,15 +1226,18 @@ void EntityList::AEAttack(
if (current_mob->IsNPC()
&& current_mob != attacker //this is not needed unless NPCs can use this
&& (attacker->IsAttackAllowed(current_mob))
&& current_mob->GetRace() != 216 && current_mob->GetRace() != 472 /* dont attack horses */
&& !current_mob->IsHorse() /* dont attack mounts */
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared)
) {
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
}
else {
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
for (int i = 0; i < attack_rounds; i++) {
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
}
else {
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
}
}
hit_count++;
+107 -46
View File
@@ -119,7 +119,13 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_DEATH_ZONE",
"EVENT_USE_SKILL",
"EVENT_COMBINE_VALIDATE",
"EVENT_BOT_COMMAND"
"EVENT_BOT_COMMAND",
"EVENT_WARP",
"EVENT_TEST_BUFF",
"EVENT_COMBINE",
"EVENT_CONSIDER",
"EVENT_CONSIDER_CORPSE",
"EVENT_LOOT_ZONE"
};
PerlembParser::PerlembParser() : perl(nullptr)
@@ -165,7 +171,7 @@ void PerlembParser::ReloadQuests()
}
int PerlembParser::EventCommon(
QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, Mob *mob,
QuestEventID event, uint32 objid, const char *data, NPC *npcmob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct* spell, Mob *mob,
uint32 extradata, bool global, std::vector<EQ::Any> *extra_pointers
)
{
@@ -246,21 +252,17 @@ int PerlembParser::EventCommon(
}
if (isPlayerQuest || isGlobalPlayerQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr);
}
else if (isItemQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst);
}
else if (isSpellQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, nullptr);
} else if (isItemQuest) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, item_inst, nullptr);
} else if (isSpellQuest) {
if (mob) {
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr);
return SendCommands(package_name.c_str(), sub_name, 0, mob, mob, nullptr, spell);
} else {
return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr, spell);
}
else {
return SendCommands(package_name.c_str(), sub_name, 0, npcmob, mob, nullptr);
}
}
else {
return SendCommands(package_name.c_str(), sub_name, objid, npcmob, mob, nullptr);
} else {
return SendCommands(package_name.c_str(), sub_name, objid, npcmob, mob, nullptr, nullptr);
}
}
@@ -269,7 +271,7 @@ int PerlembParser::EventNPC(
std::vector<EQ::Any> *extra_pointers
)
{
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, false, extra_pointers);
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, false, extra_pointers);
}
int PerlembParser::EventGlobalNPC(
@@ -277,7 +279,7 @@ int PerlembParser::EventGlobalNPC(
std::vector<EQ::Any> *extra_pointers
)
{
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, true, extra_pointers);
return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, nullptr, init, extra_data, true, extra_pointers);
}
int PerlembParser::EventPlayer(
@@ -285,7 +287,7 @@ int PerlembParser::EventPlayer(
std::vector<EQ::Any> *extra_pointers
)
{
return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, false, extra_pointers);
return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, nullptr, client, extra_data, false, extra_pointers);
}
int PerlembParser::EventGlobalPlayer(
@@ -293,7 +295,7 @@ int PerlembParser::EventGlobalPlayer(
std::vector<EQ::Any> *extra_pointers
)
{
return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, true, extra_pointers);
return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, nullptr, client, extra_data, true, extra_pointers);
}
int PerlembParser::EventItem(
@@ -302,15 +304,15 @@ int PerlembParser::EventItem(
)
{
// needs pointer validation on 'item' argument
return EventCommon(evt, item->GetID(), nullptr, nullptr, item, client, extra_data, false, extra_pointers);
return EventCommon(evt, item->GetID(), nullptr, nullptr, item, nullptr, client, extra_data, false, extra_pointers);
}
int PerlembParser::EventSpell(
QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, uint32 extra_data,
QuestEventID evt, NPC *npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<EQ::Any> *extra_pointers
)
{
return EventCommon(evt, 0, itoa(spell_id), npc, nullptr, client, extra_data, false, extra_pointers);
return EventCommon(evt, spell_id, data.c_str(), npc, nullptr, &spells[spell_id], client, extra_data, false, extra_pointers);
}
bool PerlembParser::HasQuestSub(uint32 npcid, QuestEventID evt)
@@ -773,10 +775,11 @@ void PerlembParser::ExportVar(const char *pkgprefix, const char *varname, const
int PerlembParser::SendCommands(
const char *pkgprefix,
const char *event,
uint32 npcid,
uint32 object_id,
Mob *other,
Mob *mob,
EQ::ItemInstance *item_inst
EQ::ItemInstance *item_inst,
const SPDat_Spell_Struct *spell
)
{
if (!perl) {
@@ -785,10 +788,10 @@ int PerlembParser::SendCommands(
int ret_value = 0;
if (mob && mob->IsClient()) {
quest_manager.StartQuest(other, mob->CastToClient(), item_inst);
quest_manager.StartQuest(other, mob->CastToClient(), item_inst, spell);
}
else {
quest_manager.StartQuest(other, nullptr, nullptr);
quest_manager.StartQuest(other);
}
try {
@@ -802,6 +805,7 @@ int PerlembParser::SendCommands(
std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client";
std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc";
std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem";
std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell";
std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list";
if (clear_vars_.find(cl) != clear_vars_.end()) {
std::string eval_str = cl;
@@ -821,6 +825,12 @@ int PerlembParser::SendCommands(
perl->eval(eval_str.c_str());
}
if (clear_vars_.find(sp) != clear_vars_.end()) {
std::string eval_str = sp;
eval_str += " = undef;";
perl->eval(eval_str.c_str());
}
if (clear_vars_.find(enl) != clear_vars_.end()) {
std::string eval_str = enl;
eval_str += " = undef;";
@@ -858,6 +868,14 @@ int PerlembParser::SendCommands(
sv_setref_pv(questitem, "QuestItem", curi);
}
if (spell) {
const SPDat_Spell_Struct* current_spell = quest_manager.GetQuestSpell();
SPDat_Spell_Struct* real_spell = const_cast<SPDat_Spell_Struct*>(current_spell);
snprintf(namebuf, 64, "%s::spell", pkgprefix);
SV *spell = get_sv(namebuf, true);
sv_setref_pv(spell, "Spell", (void *) real_spell);
}
snprintf(namebuf, 64, "%s::entity_list", pkgprefix);
SV *el = get_sv(namebuf, true);
sv_setref_pv(el, "EntityList", &entity_list);
@@ -871,10 +889,12 @@ int PerlembParser::SendCommands(
std::string cl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::client";
std::string np = (std::string) "$" + (std::string) pkgprefix + (std::string) "::npc";
std::string qi = (std::string) "$" + (std::string) pkgprefix + (std::string) "::questitem";
std::string sp = (std::string) "$" + (std::string) pkgprefix + (std::string) "::spell";
std::string enl = (std::string) "$" + (std::string) pkgprefix + (std::string) "::entity_list";
clear_vars_[cl] = 1;
clear_vars_[np] = 1;
clear_vars_[qi] = 1;
clear_vars_[sp] = 1;
clear_vars_[enl] = 1;
}
#endif
@@ -965,6 +985,9 @@ void PerlembParser::MapFunctions()
"package QuestItem;"
"&boot_QuestItem;" // load quest Item XS
"package Spell;"
"&boot_Spell;" // load quest Spell XS
"package HateEntry;"
"&boot_HateEntry;" // load quest Hate XS
@@ -977,6 +1000,14 @@ void PerlembParser::MapFunctions()
"package Expedition;"
"&boot_Expedition;"
#ifdef BOTS
"package Bot;"
"our @ISA = qw(NPC);" // Bot inherits NPC
"&boot_Mob;" // load our Mob XS
"&boot_NPC;" // load our NPC XS
"&boot_Bot;" // load our Bot XS
#endif
#endif
"package main;"
"}"
@@ -990,8 +1021,8 @@ void PerlembParser::GetQuestTypes(
{
if (event == EVENT_SPELL_EFFECT_CLIENT ||
event == EVENT_SPELL_EFFECT_NPC ||
event == EVENT_SPELL_BUFF_TIC_CLIENT ||
event == EVENT_SPELL_BUFF_TIC_NPC ||
event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT ||
event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC ||
event == EVENT_SPELL_FADE ||
event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) {
isSpellQuest = true;
@@ -1028,31 +1059,31 @@ void PerlembParser::GetQuestPackageName(
bool global
)
{
if (!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) {
if (
!isPlayerQuest &&
!isGlobalPlayerQuest &&
!isItemQuest &&
!isSpellQuest
) {
if (global) {
isGlobalNPC = true;
package_name = "qst_global_npc";
}
else {
} else {
package_name = "qst_npc_";
package_name += itoa(npcmob->GetNPCTypeID());
package_name += std::to_string(npcmob->GetNPCTypeID());
}
}
else if (isItemQuest) {
} else if (isItemQuest) {
// need a valid EQ::ItemInstance pointer check here..unsure how to cancel this process
const EQ::ItemData *item = item_inst->GetItem();
package_name = "qst_item_";
package_name += itoa(item->ID);
}
else if (isPlayerQuest) {
package_name += std::to_string(item->ID);
} else if (isPlayerQuest) {
package_name = "qst_player";
}
else if (isGlobalPlayerQuest) {
} else if (isGlobalPlayerQuest) {
package_name = "qst_global_player";
}
else {
} else {
package_name = "qst_spell_";
package_name += data;
package_name += std::to_string(objid);
}
}
@@ -1398,12 +1429,14 @@ void PerlembParser::ExportEventVariables(
ExportVar(package_name.c_str(), "version", zone->GetInstanceVersion());
break;
}
case EVENT_LOOT_ZONE:
case EVENT_LOOT: {
Seperator sep(data);
ExportVar(package_name.c_str(), "looted_id", sep.arg[0]);
ExportVar(package_name.c_str(), "looted_charges", sep.arg[1]);
ExportVar(package_name.c_str(), "corpse", sep.arg[2]);
ExportVar(package_name.c_str(), "corpse_id", sep.arg[3]);
break;
}
@@ -1509,11 +1542,17 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT:
case EVENT_SPELL_EFFECT_BUFF_TIC_NPC:
case EVENT_SPELL_EFFECT_CLIENT:
case EVENT_SPELL_EFFECT_NPC:
case EVENT_SPELL_BUFF_TIC_CLIENT:
case EVENT_SPELL_BUFF_TIC_NPC: {
ExportVar(package_name.c_str(), "caster_id", extradata);
case EVENT_SPELL_FADE: {
Seperator sep(data);
ExportVar(package_name.c_str(), "spell_id", objid);
ExportVar(package_name.c_str(), "caster_id", sep.arg[0]);
ExportVar(package_name.c_str(), "tics_remaining", sep.arg[1]);
ExportVar(package_name.c_str(), "caster_level", sep.arg[2]);
ExportVar(package_name.c_str(), "buff_slot", sep.arg[3]);
break;
}
@@ -1627,6 +1666,28 @@ void PerlembParser::ExportEventVariables(
ExportVar(package_name.c_str(), "langid", extradata);
break;
}
case EVENT_WARP: {
Seperator sep(data);
ExportVar(package_name.c_str(), "from_x", sep.arg[0]);
ExportVar(package_name.c_str(), "from_y", sep.arg[1]);
ExportVar(package_name.c_str(), "from_z", sep.arg[2]);
break;
}
case EVENT_CONSIDER: {
ExportVar(package_name.c_str(), "entity_id", std::stoi(data));
break;
}
case EVENT_CONSIDER_CORPSE: {
ExportVar(package_name.c_str(), "corpse_entity_id", std::stoi(data));
break;
}
case EVENT_COMBINE: {
ExportVar(package_name.c_str(), "container_slot", std::stoi(data));
break;
}
default: {
break;
+3 -3
View File
@@ -58,7 +58,7 @@ public:
std::vector<EQ::Any> *extra_pointers);
virtual int EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<EQ::Any> *extra_pointers);
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data,
virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<EQ::Any> *extra_pointers);
virtual bool HasQuestSub(uint32 npcid, QuestEventID evt);
@@ -90,9 +90,9 @@ private:
void ExportVar(const char *pkgprefix, const char *varname, float value);
void ExportVarComplex(const char *pkgprefix, const char *varname, const char *value);
int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQ::ItemInstance* item_inst, Mob* mob,
int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, EQ::ItemInstance* item_inst, const SPDat_Spell_Struct* spell, Mob* mob,
uint32 extradata, bool global, std::vector<EQ::Any> *extra_pointers);
int SendCommands(const char *pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, EQ::ItemInstance *item_inst);
int SendCommands(const char *pkgprefix, const char *event, uint32 spell_id, Mob* other, Mob* mob, EQ::ItemInstance *item_inst, const SPDat_Spell_Struct *spell);
void MapFunctions();
void GetQuestTypes(bool &isPlayerQuest, bool &isGlobalPlayerQuest, bool &isGlobalNPC, bool &isItemQuest,
+3182 -1404
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -34,11 +34,15 @@ EXTERN_C XS(boot_Group);
EXTERN_C XS(boot_Raid);
EXTERN_C XS(boot_Inventory);
EXTERN_C XS(boot_QuestItem);
EXTERN_C XS(boot_Spell);
EXTERN_C XS(boot_HateEntry);
EXTERN_C XS(boot_Object);
EXTERN_C XS(boot_Doors);
EXTERN_C XS(boot_PerlPacket);
EXTERN_C XS(boot_Expedition);
#ifdef BOTS
EXTERN_C XS(boot_Bot);
#endif
#endif
#endif
@@ -87,10 +91,16 @@ EXTERN_C void xs_init(pTHX)
newXS(strcpy(buf, "Raid::boot_Raid"), boot_Raid, file);
newXS(strcpy(buf, "Inventory::boot_Inventory"), boot_Inventory, file);
newXS(strcpy(buf, "QuestItem::boot_QuestItem"), boot_QuestItem, file);
newXS(strcpy(buf, "Spell::boot_Spell"), boot_Spell, file);
newXS(strcpy(buf, "HateEntry::boot_HateEntry"), boot_HateEntry, file);
newXS(strcpy(buf, "Object::boot_Object"), boot_Object, file);
newXS(strcpy(buf, "Doors::boot_Doors"), boot_Doors, file);
newXS(strcpy(buf, "Expedition::boot_Expedition"), boot_Expedition, file);
#ifdef BOTS
newXS(strcpy(buf, "Bot::boot_Mob"), boot_Mob, file);
newXS(strcpy(buf, "Bot::boot_NPC"), boot_NPC, file);
newXS(strcpy(buf, "Bot::boot_Bot"), boot_Bot, file);
#endif
;
#endif
#endif
+417 -112
View File
@@ -43,6 +43,7 @@
#include "water_map.h"
#include "npc_scale_manager.h"
#include "../common/say_link.h"
#include "dialogue_window.h"
#ifdef _WINDOWS
#define snprintf _snprintf
@@ -517,13 +518,15 @@ void EntityList::MobProcess()
mob_settle_timer->Disable();
}
Spawn2* s2 = mob->CastToNPC()->respawn2;
// Perform normal mob processing if any of these are true:
// -- zone is not empty
// -- a quest has turned it on for this zone while zone is idle
// -- the entity's spawn2 point is marked as path_while_zone_idle
// -- the zone is newly empty and we're allowing mobs to settle
if (zone->process_mobs_while_empty || numclients > 0 ||
mob->GetWanderType() == 4 || mob->GetWanderType() == 6 ||
mob_settle_timer->Enabled()) {
// Normal processing, or assuring that spawns that should
// path and depop do that. Otherwise all of these type mobs
// will be up and at starting positions, or waiting at the zoneline
// if they chased the PCs when idle zone wakes up.
(s2 && s2->PathWhenZoneIdle()) || mob_settle_timer->Enabled()) {
mob_dead = !mob->Process();
}
else {
@@ -686,6 +689,14 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
{
npc->SetID(GetFreeID());
//If this is not set here we will despawn pets from new AC changes
auto owner_id = npc->GetOwnerID();
if(owner_id) {
auto owner = entity_list.GetMob(owner_id);
if (owner) {
owner->SetPetID(npc->GetID());
}
}
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
uint16 emoteid = npc->GetEmoteID();
@@ -720,10 +731,10 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
/* Zone controller process EVENT_SPAWN_ZONE */
if (RuleB(Zone, UseZoneController)) {
if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){
char data_pass[100] = { 0 };
snprintf(data_pass, 99, "%d %d", npc->GetID(), npc->GetNPCTypeID());
parse->EventNPC(EVENT_SPAWN_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
if (controller && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){
std::string data_pass = fmt::format("{} {}", npc->GetID(), npc->GetNPCTypeID());
parse->EventNPC(EVENT_SPAWN_ZONE, controller, nullptr, data_pass.c_str(), 0);
}
}
@@ -1198,6 +1209,34 @@ bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id)
return false;
}
bool EntityList::IsNPCSpawned(std::vector<uint32> npc_ids)
{
return CountSpawnedNPCs(npc_ids) != 0;
}
uint32 EntityList::CountSpawnedNPCs(std::vector<uint32> npc_ids)
{
uint32 npc_count = 0;
if (npc_list.empty() || npc_ids.empty()) {
return npc_count;
}
for (auto current_npc : npc_list) {
if (
std::find(
npc_ids.begin(),
npc_ids.end(),
current_npc.second->GetNPCTypeID()
) != npc_ids.end() &&
current_npc.second->GetID() != 0
) {
npc_count++;
}
}
return npc_count;
}
Object *EntityList::GetObjectByDBID(uint32 id)
{
if (id == 0 || object_list.empty())
@@ -1475,11 +1514,11 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
if (!m)
continue;
if (RemoveFromXTargets) {
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m->CastToClient())))
if (RemoveFromXTargets && mob) {
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m)))
m->CastToClient()->RemoveXTarget(mob, false);
// FadingMemories calls this function passing the client.
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob->CastToClient())))
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob)))
mob->CastToClient()->RemoveXTarget(m, false);
}
@@ -1487,6 +1526,32 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
}
}
void EntityList::RemoveFromTargetsFadingMemories(Mob *spell_target, bool RemoveFromXTargets, uint32 max_level)
{
for (auto &e : mob_list) {
auto &mob = e.second;
if (!mob) {
continue;
}
if (max_level && mob->GetLevel() > max_level)
continue;
if (mob->GetSpecialAbility(IMMUNE_FADING_MEMORIES))
continue;
if (RemoveFromXTargets && spell_target) {
if (mob->IsClient() && (spell_target->CheckAggro(mob) || spell_target->IsOnFeignMemory(mob)))
mob->CastToClient()->RemoveXTarget(spell_target, false);
else if (spell_target->IsClient() && (mob->CheckAggro(spell_target) || mob->IsOnFeignMemory(spell_target)))
spell_target->CastToClient()->RemoveXTarget(mob, false);
}
mob->RemoveFromHateList(spell_target);
}
}
void EntityList::RemoveFromXTargets(Mob *mob)
{
auto it = client_list.begin();
@@ -3163,50 +3228,75 @@ char *EntityList::RemoveNumbers(char *name)
void EntityList::ListNPCCorpses(Client *client)
{
uint32 x = 0;
auto it = corpse_list.begin();
client->Message(Chat::White, "NPC Corpses in the zone:");
while (it != corpse_list.end()) {
if (it->second->IsNPCCorpse()) {
client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName());
x++;
uint32 corpse_count = 0;
for (const auto& corpse : corpse_list) {
uint32 corpse_number = (corpse_count + 1);
if (corpse.second->IsNPCCorpse()) {
client->Message(
Chat::White,
fmt::format(
"Corpse {} | Name: {} ({})",
corpse_number,
corpse.second->GetName(),
corpse.second->GetID()
).c_str()
);
corpse_count++;
}
++it;
}
client->Message(Chat::White, "%d npc corpses listed.", x);
if (corpse_count > 0) {
client->Message(
Chat::White,
fmt::format(
"{} NPC corpses listed.",
corpse_count
).c_str()
);
}
}
void EntityList::ListPlayerCorpses(Client *client)
{
uint32 x = 0;
auto it = corpse_list.begin();
client->Message(Chat::White, "Player Corpses in the zone:");
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse()) {
client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName());
x++;
uint32 corpse_count = 0;
for (const auto& corpse : corpse_list) {
uint32 corpse_number = (corpse_count + 1);
if (corpse.second->IsPlayerCorpse()) {
client->Message(
Chat::White,
fmt::format(
"Corpse {} | Name: {} ({})",
corpse_number,
corpse.second->GetName(),
corpse.second->GetID()
).c_str()
);
corpse_count++;
}
++it;
}
client->Message(Chat::White, "%d player corpses listed.", x);
if (corpse_count > 0) {
client->Message(
Chat::White,
fmt::format(
"{} Player corpses listed.",
corpse_count
).c_str()
);
}
}
// returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeleteNPCCorpses()
uint32 EntityList::DeleteNPCCorpses()
{
int32 x = 0;
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsNPCCorpse()) {
it->second->DepopNPCCorpse();
x++;
uint32 corpse_count = 0;
for (const auto& corpse : corpse_list) {
if (corpse.second->IsNPCCorpse()) {
corpse.second->DepopNPCCorpse();
corpse_count++;
}
++it;
}
return x;
return corpse_count;
}
void EntityList::CorpseFix(Client* c)
@@ -3226,19 +3316,16 @@ void EntityList::CorpseFix(Client* c)
}
// returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeletePlayerCorpses()
uint32 EntityList::DeletePlayerCorpses()
{
int32 x = 0;
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse()) {
it->second->CastToCorpse()->Delete();
x++;
uint32 corpse_count = 0;
for (const auto& corpse : corpse_list) {
if (corpse.second->IsPlayerCorpse()) {
corpse.second->Delete();
corpse_count++;
}
++it;
}
return x;
return corpse_count;
}
void EntityList::SendPetitionToAdmins()
@@ -3254,7 +3341,7 @@ void EntityList::SendPetitionToAdmins()
pcus->quetotal=0;
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 80)
if (it->second->CastToClient()->Admin() >= AccountStatus::QuestTroupe)
it->second->CastToClient()->QueuePacket(outapp);
++it;
}
@@ -3282,7 +3369,7 @@ void EntityList::SendPetitionToAdmins(Petition *pet)
pcus->quetotal = petition_list.GetTotalPetitions();
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 80) {
if (it->second->CastToClient()->Admin() >= AccountStatus::QuestTroupe) {
if (pet->CheckedOut())
strcpy(pcus->gmsenttoo, "");
else
@@ -3307,7 +3394,7 @@ void EntityList::ClearClientPetitionQueue()
pet->quetotal = petition_list.GetTotalPetitions();
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 100) {
if (it->second->CastToClient()->Admin() >= AccountStatus::GMAdmin) {
int x = 0;
for (x = 0; x < 64; x++) {
pet->petnumber = x;
@@ -3469,7 +3556,7 @@ void EntityList::ClearFeignAggro(Mob *targ)
auto it = npc_list.begin();
while (it != npc_list.end()) {
// add Feign Memory check because sometimes weird stuff happens
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ->CastToClient()))) {
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ))) {
if (it->second->GetSpecialAbility(IMMUNE_FEIGN_DEATH)) {
++it;
continue;
@@ -3495,22 +3582,31 @@ void EntityList::ClearFeignAggro(Mob *targ)
it->second->RemoveFromHateList(targ);
if (targ->IsClient()) {
if (it->second->GetLevel() >= 35 && zone->random.Roll(60))
it->second->AddFeignMemory(targ->CastToClient());
else
if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) {
it->second->AddFeignMemory(targ);
}
else {
targ->CastToClient()->RemoveXTarget(it->second, false);
}
}
else if (targ->IsPet()){
if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) {
it->second->AddFeignMemory(targ);
}
}
}
++it;
}
}
void EntityList::ClearZoneFeignAggro(Client *targ)
void EntityList::ClearZoneFeignAggro(Mob *targ)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
it->second->RemoveFromFeignMemory(targ);
targ->CastToClient()->RemoveXTarget(it->second, false);
if (targ && targ->IsClient()) {
targ->CastToClient()->RemoveXTarget(it->second, false);
}
++it;
}
}
@@ -3542,12 +3638,8 @@ bool EntityList::MakeTrackPacket(Client *client)
uint32 distance = 0;
float MobDistance;
if (client->GetClass() == DRUID)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 10);
else if (client->GetClass() == RANGER)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 12);
else if (client->GetClass() == BARD)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 7);
distance = (client->GetSkill(EQ::skills::SkillTracking) * client->GetClassTrackingDistanceMultiplier(client->GetClass()));
if (distance <= 0)
return false;
if (distance < 300)
@@ -3682,7 +3774,7 @@ void EntityList::SendAlarm(Trap *trap, Mob *currenttarget, uint8 kos)
if (kos) {
uint8 factioncon = currenttarget->GetReverseFactionCon(cur);
if (factioncon == FACTION_THREATENLY || factioncon == FACTION_SCOWLS) {
if (factioncon == FACTION_THREATENINGLY || factioncon == FACTION_SCOWLS) {
cur->AddToHateList(currenttarget,1);
}
}
@@ -4184,7 +4276,7 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n && n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
if (
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
@@ -4199,6 +4291,35 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
}
}
void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, int32 spell_id)
{
if (!attacker || !owner)
return;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n && n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
if (
attacker &&
attacker != n &&
!n->IsEngaged() &&
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
!attacker->IsTrap() &&
!attacker->IsCorpse()
) {
n->AddToHateList(attacker, 1, 0, true, false, false, spell_id);
n->SetTarget(attacker);
}
}
}
++it;
}
}
bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
float trg_x, float trg_y, float trg_z, float perwalk)
{
@@ -4225,8 +4346,10 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
return false;
}
void EntityList::QuestJournalledSayClose(Mob *sender, float dist, const char *mobname, const char *message,
Journal::Options &opts)
void EntityList::QuestJournalledSayClose(
Mob *sender, float dist, const char *mobname, const char *message,
Journal::Options &opts
)
{
SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64);
@@ -4239,7 +4362,32 @@ void EntityList::QuestJournalledSayClose(Mob *sender, float dist, const char *mo
buf.WriteInt32(0); // location, client doesn't seem to do anything with this
buf.WriteInt32(0);
buf.WriteInt32(0);
buf.WriteString(message);
if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) {
for (auto &e : GetCloseMobList(sender, (dist * dist))) {
Mob *mob = e.second;
if (!mob->IsClient()) {
continue;
}
Client *client = mob->CastToClient();
if (client->GetTarget() && client->GetTarget()->IsMob() && client->GetTarget()->CastToMob() == sender) {
std::string window_markdown = message;
DialogueWindow::Render(client, window_markdown);
}
}
return;
}
else if (RuleB(Chat, AutoInjectSaylinksToSay)) {
std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message);
buf.WriteString(new_message);
}
else {
buf.WriteString(message);
}
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
@@ -4278,6 +4426,56 @@ Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name)
return ClosestCorpse;
}
void EntityList::TryWakeTheDead(Mob *sender, Mob *target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets)
{
if (!sender) {
return;
}
std::vector<int> used_corpse_list;
for (int i = 0; i < amount_pets; i++)
{
uint32 CurrentDistance, ClosestDistance = 4294967295u;
Corpse *CurrentCorpse, *ClosestCorpse = nullptr;
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
CurrentCorpse = it->second;
++it;
bool corpse_already_used = false;
for (auto itr = used_corpse_list.begin(); itr != used_corpse_list.end(); ++itr) {
if ((*itr) && (*itr) == CurrentCorpse->GetID()) {
corpse_already_used = true;
continue;
}
}
if (corpse_already_used) {
continue;
}
CurrentDistance = static_cast<uint32>(sender->CalculateDistance(CurrentCorpse->GetX(), CurrentCorpse->GetY(), CurrentCorpse->GetZ()));
if (max_distance && CurrentDistance > max_distance) {
continue;
}
if (CurrentDistance < ClosestDistance) {
ClosestDistance = CurrentDistance;
ClosestCorpse = CurrentCorpse;
}
}
if (ClosestCorpse) {
sender->WakeTheDead(spell_id, ClosestCorpse, target, duration);
used_corpse_list.push_back(ClosestCorpse->GetID());
}
}
}
void EntityList::ForceGroupUpdate(uint32 gid)
{
auto it = client_list.begin();
@@ -4536,6 +4734,45 @@ void EntityList::SendUntargetable(Client *c)
}
}
void EntityList::SendAppearanceEffects(Client *c)
{
if (!c)
return;
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
if (cur) {
if (cur == c) {
++it;
continue;
}
cur->SendSavedAppearenceEffects(c);
}
++it;
}
}
void EntityList::SendIllusionWearChange(Client *c)
{
if (!c) {
return;
}
for (auto &e : mob_list) {
auto &mob = e.second;
if (mob) {
if (mob == c) {
continue;
}
mob->SendIllusionWearChange(c);
}
}
}
void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
{
// This is only called for SoF clients, as regular /who is now handled server-side for that client.
@@ -4670,7 +4907,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM())
else if (ClientEntry->Admin() >= AccountStatus::Steward && ClientEntry->GetGM())
WAPP2->RankMSGID = 12312;
else
WAPP2->RankMSGID = 0xFFFFFFFF;
@@ -4809,6 +5046,16 @@ void EntityList::GetClientList(std::list<Client *> &c_list)
}
}
#ifdef BOTS
void EntityList::GetBotList(std::list<Bot *> &b_list)
{
b_list.clear();
for (auto bot_iterator : bot_list) {
b_list.push_back(bot_iterator);
}
}
#endif
void EntityList::GetCorpseList(std::list<Corpse *> &c_list)
{
c_list.clear();
@@ -5120,7 +5367,7 @@ void EntityList::ExpeditionWarning(uint32 minutes_left)
safe_delete(outapp);
}
Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType)
Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType, bool skip_client_pets)
{
if (!sender)
@@ -5137,6 +5384,10 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType)
if (CurrentMob->GetBodyType() != BodyType)
continue;
// Do not detect client pets
if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerClient())
continue;
CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) +
((CurrentMob->GetX() - sender->GetX()) * (CurrentMob->GetX() - sender->GetX()));
@@ -5192,59 +5443,103 @@ Client *EntityList::FindCorpseDragger(uint16 CorpseID)
return nullptr;
}
Mob *EntityList::GetTargetForVirus(Mob *spreader, int range)
std::vector<Mob*> EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *original_caster, int range, int pcnpc, int32 spell_id)
{
int max_spread_range = RuleI(Spells, VirusSpreadDistance);
/*
Live Mechanics
Virus spreader does NOT need LOS
There is no max target limit
*/
if (!spreader) {
return {};
}
if (range)
max_spread_range = range;
std::vector<Mob *> spreader_list = {};
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
for (auto &it : entity_list.GetCloseMobList(spreader, range)) {
Mob *mob = it.second;
std::vector<Mob *> TargetsInRange;
if (!mob) {
continue;
}
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
// Make sure the target is in range, has los and is not the mob doing the spreading
if ((cur->GetID() != spreader->GetID()) &&
(cur->CalculateDistance(spreader->GetX(), spreader->GetY(),
spreader->GetZ()) <= max_spread_range) &&
(spreader->CheckLosFN(cur))) {
// If the spreader is an npc it can only spread to other npc controlled mobs
if (spreader->IsNPC() && !spreader->IsPet() && !spreader->CastToNPC()->GetSwarmOwner() && cur->IsNPC()) {
TargetsInRange.push_back(cur);
if (mob == spreader) {
continue;
}
// check PC/NPC only flag 1 = PCs, 2 = NPCs
if (pcnpc == 1 && !mob->IsClient() && !mob->IsMerc() && !mob->IsBot()) {
continue;
}
else if (pcnpc == 2 && (mob->IsClient() || mob->IsMerc() || mob->IsBot())) {
continue;
}
if (mob->IsClient() && !mob->CastToClient()->ClientFinishedLoading()) {
continue;
}
if (mob->IsAura() || mob->IsTrap()) {
continue;
}
// Make sure the target is in range
if (mob->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= range) {
if (!original_caster) {
continue;
}
// If the spreader is an npc controlled pet it can spread to any other npc or an npc controlled pet
else if (spreader->IsNPC() && spreader->IsPet() && spreader->GetOwner()->IsNPC()) {
if (cur->IsNPC() && !cur->IsPet()) {
TargetsInRange.push_back(cur);
} else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
//Do not allow detrimental spread to anything the original caster couldn't normally attack.
if (is_detrimental_spell && !original_caster->IsAttackAllowed(mob, true)) {
continue;
}
//For non-NPCs, do not allow beneficial spread to anything the original caster could normally attack.
if (!is_detrimental_spell && !original_caster->IsNPC() && original_caster->IsAttackAllowed(mob, true)) {
continue;
}
// If the spreader is an npc and NOT a PET, then spread to other npc controlled mobs that are not pets
if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && !mob->IsPet() && !mob->IsTempPet()) {
spreader_list.push_back(mob);
}
// If the spreader is an npc and NOT a PET, then spread to npc controlled pet
else if (spreader->IsNPC() && !spreader->IsPet() && !spreader->IsTempPet() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) {
spreader_list.push_back(mob);
}
// If the spreader is an npc controlled PET it can spread to any other npc or an npc controlled pet
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && spreader->IsPetOwnerNPC()) {
if (mob->IsNPC() && (!mob->IsPet() || !mob->IsTempPet())) {
spreader_list.push_back(mob);
}
else if (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && mob->IsPetOwnerNPC()) {
spreader_list.push_back(mob);
}
}
// if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs
else if (!spreader->IsNPC() && !cur->IsNPC()) {
TargetsInRange.push_back(cur);
else if (!spreader->IsNPC() && !mob->IsNPC()) {
spreader_list.push_back(mob);
}
// if its a pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) {
if (!cur->IsNPC()) {
TargetsInRange.push_back(cur);
// if spreader is not an NPC, and Target is an NPC, then spread to non-NPC controlled pets
else if (!spreader->IsNPC() && mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
spreader_list.push_back(mob);
}
// if spreader is a non-NPC controlled pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->IsTempPet()) && !spreader->IsPetOwnerNPC()) {
//Spread to non-NPCs
if (!mob->IsNPC()) {
spreader_list.push_back(mob);
}
else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
//Spread to other non-NPC Pets
else if (mob->IsNPC() && (mob->IsPet() || mob->IsTempPet()) && !mob->IsPetOwnerNPC()) {
spreader_list.push_back(mob);
}
}
}
++it;
}
if(TargetsInRange.empty())
return nullptr;
return TargetsInRange[zone->random.Int(0, TargetsInRange.size() - 1)];
return spreader_list;
}
void EntityList::StopMobAI()
@@ -5257,6 +5552,7 @@ void EntityList::StopMobAI()
void EntityList::SendAlternateAdvancementStats() {
for(auto &c : client_list) {
c.second->SendClearPlayerAA();
c.second->SendAlternateAdvancementTable();
c.second->SendAlternateAdvancementStats();
c.second->SendAlternateAdvancementPoints();
@@ -5344,3 +5640,12 @@ int EntityList::MovePlayerCorpsesToGraveyard(bool force_move_from_instance)
return moved_count;
}
void EntityList::DespawnGridNodes(int32 grid_id) {
for (auto mob_iterator : mob_list) {
Mob *mob = mob_iterator.second;
if (mob->IsNPC() && mob->GetRace() == 2254 && mob->EntityVariableExists("grid_id") && atoi(mob->GetEntityVariable("grid_id")) == grid_id) {
mob->Depop();
}
}
}
+21 -8
View File
@@ -158,7 +158,8 @@ public:
Mob *GetMob(const char* name);
Mob *GetMobByNpcTypeID(uint32 get_id);
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
Mob *GetTargetForVirus(Mob* spreader, int range);
bool IsNPCSpawned(std::vector<uint32> npc_ids);
uint32 CountSpawnedNPCs(std::vector<uint32> npc_ids);
inline NPC *GetNPCByID(uint16 id)
{
auto it = npc_list.find(id);
@@ -238,6 +239,7 @@ public:
void RemoveAllCorpsesByCharID(uint32 charid);
void RemoveCorpseByDBID(uint32 dbid);
int RezzAllCorpsesByCharID(uint32 charid);
void DespawnGridNodes(int32 grid_id);
bool IsMobInZone(Mob *who);
void ClearClientPetitionQueue();
bool CanAddHateForMob(Mob *p);
@@ -317,6 +319,7 @@ public:
void DestroyTempPets(Mob *owner);
int16 CountTempPets(Mob *owner);
void AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy = false);
void AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, int32 spell_id);
Entity *GetEntityMob(uint16 id);
Entity *GetEntityMerc(uint16 id);
Entity *GetEntityDoor(uint16 id);
@@ -382,18 +385,21 @@ public:
void SendZoneAppearance(Client *c);
void SendNimbusEffects(Client *c);
void SendUntargetable(Client *c);
void SendAppearanceEffects(Client *c);
void SendIllusionWearChange(Client *c);
void DuelMessage(Mob* winner, Mob* loser, bool flee);
void QuestJournalledSayClose(Mob *sender, float dist, const char* mobname, const char* message, Journal::Options &opts);
void GroupMessage(uint32 gid, const char *from, const char *message);
void ExpeditionWarning(uint32 minutes_left);
void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false);
void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0);
void RemoveFromXTargets(Mob* mob);
void RemoveFromAutoXTargets(Mob* mob);
void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget);
void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float distance=200, Mob* skipped_mob = 0, bool is_ack_required = true, eqFilterType filter=FilterNone);
void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true);
void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0);
void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = AccountStatus::Player, uint8 maxstatus = AccountStatus::Player);
void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0);
void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID);
void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true,
@@ -408,7 +414,8 @@ public:
float distance,
int Hand = EQ::invslot::slotPrimary,
int count = 0,
bool is_from_spell = false
bool is_from_spell = false,
int attack_rounds = 1
);
void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0);
void AESpell(
@@ -420,7 +427,6 @@ public:
int *max_targets = nullptr
);
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);
void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);
//trap stuff
Mob* GetTrapTrigger(Trap* trap);
@@ -442,8 +448,8 @@ public:
void ListNPCCorpses(Client* client);
void ListPlayerCorpses(Client* client);
int32 DeleteNPCCorpses();
int32 DeletePlayerCorpses();
uint32 DeleteNPCCorpses();
uint32 DeletePlayerCorpses();
void CorpseFix(Client* c);
void WriteEntityIDs();
void HalveAggro(Mob* who);
@@ -455,7 +461,7 @@ public:
void ClearAggro(Mob* targ);
void ClearWaterAggro(Mob* targ);
void ClearFeignAggro(Mob* targ);
void ClearZoneFeignAggro(Client* targ);
void ClearZoneFeignAggro(Mob* targ);
void AggroZone(Mob* who, uint32 hate = 0);
bool Fighting(Mob* targ);
@@ -478,9 +484,10 @@ public:
uint32 CheckNPCsClose(Mob *center);
Corpse* GetClosestCorpse(Mob* sender, const char *Name);
void TryWakeTheDead(Mob* sender, Mob* target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets);
NPC* GetClosestBanker(Mob* sender, uint32 &distance);
void CameraEffect(uint32 duration, uint32 intensity);
Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType);
Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType, bool skip_client_pets=false);
void ForceGroupUpdate(uint32 gid);
void SendGroupLeave(uint32 gid, const char *name);
void SendGroupJoin(uint32 gid, const char *name);
@@ -513,11 +520,15 @@ public:
void GetDoorsList(std::list<Doors*> &d_list);
void GetSpawnList(std::list<Spawn2*> &d_list);
void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list<Mob*> &m_list);
std::vector<Mob*> GetTargetsForVirusEffect(Mob *spreader, Mob *orginal_caster, int range, int pcnpc, int32 spell_id);
inline const std::unordered_map<uint16, Mob *> &GetMobList() { return mob_list; }
inline const std::unordered_map<uint16, NPC *> &GetNPCList() { return npc_list; }
inline const std::unordered_map<uint16, Merc *> &GetMercList() { return merc_list; }
inline const std::unordered_map<uint16, Client *> &GetClientList() { return client_list; }
#ifdef BOTS
inline const std::list<Bot *> &GetBotList() { return bot_list; }
#endif
inline const std::unordered_map<uint16, Corpse *> &GetCorpseList() { return corpse_list; }
inline const std::unordered_map<uint16, Object *> &GetObjectList() { return object_list; }
inline const std::unordered_map<uint16, Doors *> &GetDoorsList() { return door_list; }
@@ -597,6 +608,8 @@ private:
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
void ScanCloseClientMobs(std::unordered_map<uint16, Mob*>& close_mobs, Mob* scanning_mob);
void GetBotList(std::list<Bot*> &b_list);
private:
std::list<Bot*> bot_list;
#endif
+8 -2
View File
@@ -43,8 +43,8 @@ typedef enum {
EVENT_HATE_LIST,
EVENT_SPELL_EFFECT_CLIENT,
EVENT_SPELL_EFFECT_NPC,
EVENT_SPELL_BUFF_TIC_CLIENT,
EVENT_SPELL_BUFF_TIC_NPC,
EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT,
EVENT_SPELL_EFFECT_BUFF_TIC_NPC,
EVENT_SPELL_FADE,
EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE,
EVENT_COMBINE_SUCCESS, //PC successfully combined a recipe
@@ -88,6 +88,12 @@ typedef enum {
EVENT_USE_SKILL,
EVENT_COMBINE_VALIDATE,
EVENT_BOT_COMMAND,
EVENT_WARP,
EVENT_TEST_BUFF,
EVENT_COMBINE,
EVENT_CONSIDER,
EVENT_CONSIDER_CORPSE,
EVENT_LOOT_ZONE,
_LargestEventID
} QuestEventID;
+9 -1
View File
@@ -327,6 +327,10 @@ void Client::CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resex
add_aaxp *= RuleR(Character, FinalExpMultiplier);
}
if (RuleB(Character, EnableCharacterEXPMods)) {
add_aaxp *= GetAAEXPModifier(this->GetZoneID());
}
add_aaxp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
}
@@ -486,6 +490,10 @@ void Client::CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp,
add_exp *= RuleR(Character, FinalExpMultiplier);
}
if (RuleB(Character, EnableCharacterEXPMods)) {
add_exp *= GetEXPModifier(this->GetZoneID());
}
add_exp = GetEXP() + add_exp;
}
@@ -785,7 +793,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
FastQueuePacket(&outapp);
}
if (admin>=100 && GetGM()) {
if (admin >= AccountStatus::GMAdmin && GetGM()) {
char val1[20]={0};
char val2[20]={0};
char val3[20]={0};
+200 -1002
View File
File diff suppressed because it is too large Load Diff
+19 -95
View File
@@ -22,8 +22,8 @@
#define EXPEDITION_H
#include "dynamic_zone.h"
#include "../common/eq_constants.h"
#include "../common/expedition_lockout_timer.h"
#include "../common/repositories/expeditions_repository.h"
#include <cstdint>
#include <memory>
#include <string>
@@ -34,21 +34,10 @@ class Client;
class EQApplicationPacket;
struct ExpeditionInvite;
class ExpeditionRequest;
class MySQLRequestResult;
class ServerPacket;
extern const char* const DZ_YOU_NOT_ASSIGNED;
extern const char* const EXPEDITION_OTHER_BELONGS;
extern const char* const CREATE_NOT_ALL_ADDED;
enum class ExpeditionMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
enum class ExpeditionLockMessage : uint8_t
{
@@ -57,29 +46,14 @@ enum class ExpeditionLockMessage : uint8_t
Begin
};
struct ExpeditionMember
{
uint32_t char_id = 0;
std::string name;
ExpeditionMemberStatus status = ExpeditionMemberStatus::Online;
ExpeditionMember() = default;
ExpeditionMember(uint32_t char_id_, const std::string& name_)
: char_id(char_id_), name(name_) {}
ExpeditionMember(uint32_t char_id_, const std::string& name_, ExpeditionMemberStatus status_)
: char_id(char_id_), name(name_), status(status_) {}
bool IsValid() const { return char_id != 0 && !name.empty(); }
};
class Expedition
{
public:
Expedition() = delete;
Expedition(uint32_t id, const std::string& uuid, DynamicZone&& dz, const std::string& expedition_name,
const ExpeditionMember& leader, uint32_t min_players, uint32_t max_players);
Expedition(DynamicZone* dz) : m_dynamic_zone(dz) { assert(m_dynamic_zone != nullptr); }
Expedition(DynamicZone* dz, uint32_t id, uint32_t dz_id);
static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, ExpeditionRequest& request);
static Expedition* TryCreate(Client* requester, DynamicZone& dynamiczone, bool disable_messages);
static void CacheFromDatabase(uint32_t expedition_id);
static bool CacheAllFromDatabase();
@@ -104,27 +78,15 @@ public:
const std::string& expedition_name = {}, const std::string& event_name = {});
static void AddLockoutClients(const ExpeditionLockoutTimer& lockout, uint32_t exclude_id = 0);
uint32_t GetDynamicZoneID() const { return m_dynamiczone.GetID(); }
uint32_t GetID() const { return m_id; }
uint16_t GetInstanceID() const { return m_dynamiczone.GetInstanceID(); }
uint32_t GetLeaderID() const { return m_leader.char_id; }
uint32_t GetMinPlayers() const { return m_min_players; }
uint32_t GetMaxPlayers() const { return m_max_players; }
uint32_t GetMemberCount() const { return static_cast<uint32_t>(m_members.size()); }
DynamicZone& GetDynamicZone() { return m_dynamiczone; }
const std::string& GetName() const { return m_expedition_name; }
const std::string& GetLeaderName() const { return m_leader.name; }
const std::string& GetUUID() const { return m_uuid; }
uint32_t GetDynamicZoneID() const { return m_dynamic_zone_id; }
DynamicZone* GetDynamicZone() const { return m_dynamic_zone; }
const DynamicZoneMember& GetLeader() { return GetDynamicZone()->GetLeader(); }
uint32_t GetLeaderID() { return GetDynamicZone()->GetLeaderID(); }
const std::string& GetLeaderName() { return GetDynamicZone()->GetLeaderName(); }
const std::unordered_map<std::string, ExpeditionLockoutTimer>& GetLockouts() const { return m_lockouts; }
const std::vector<ExpeditionMember>& GetMembers() const { return m_members; }
bool AddMember(const std::string& add_char_name, uint32_t add_char_id);
bool HasMember(const std::string& character_name);
bool HasMember(uint32_t character_id);
void RemoveAllMembers(bool enable_removal_timers = true);
bool RemoveMember(const std::string& remove_char_name);
void SetMemberStatus(Client* client, ExpeditionMemberStatus status);
void SwapMember(Client* add_client, const std::string& remove_char_name);
const std::string& GetName() { return GetDynamicZone()->GetName(); }
void RegisterDynamicZoneCallbacks();
bool IsLocked() const { return m_is_locked; }
void SetLocked(bool lock_expedition, ExpeditionLockMessage lock_msg,
@@ -147,7 +109,6 @@ public:
void SetLootEventByNPCTypeID(uint32_t npc_type_id, const std::string& event_name);
void SetLootEventBySpawnID(uint32_t spawn_id, const std::string& event_name);
void SendClientExpeditionInfo(Client* client);
void SendWorldMakeLeaderRequest(uint32_t requester_id, const std::string& new_leader_name);
void SendWorldPendingInvite(const ExpeditionInvite& invite, const std::string& add_name);
@@ -161,43 +122,26 @@ public:
void DzQuit(Client* requester);
void DzKickPlayers(Client* requester);
void SetDzCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false);
void SetDzCompass(const std::string& zone_name, float x, float y, float z, bool update_db = false);
void SetDzSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false);
void SetDzSafeReturn(const std::string& zone_name, float x, float y, float z, float heading, bool update_db = false);
void SetDzSecondsRemaining(uint32_t seconds_remaining);
void SetDzZoneInLocation(float x, float y, float z, float heading, bool update_db = false);
static const int32_t REPLAY_TIMER_ID;
static const int32_t EVENT_TIMER_ID;
private:
static void CacheExpeditions(MySQLRequestResult& results);
static void SendWorldGetOnlineMembers(const std::vector<std::pair<uint32_t, uint32_t>>& expedition_character_ids);
static void CacheExpeditions(std::vector<ExpeditionsRepository::Expeditions>&& expeditions);
static void SendWorldCharacterLockout(uint32_t character_id, const ExpeditionLockoutTimer& lockout, bool remove);
void AddLockout(const ExpeditionLockoutTimer& lockout, bool members_only = false);
void AddLockoutDurationClients(const ExpeditionLockoutTimer& lockout, int seconds, uint32_t exclude_id = 0);
void AddInternalMember(const std::string& char_name, uint32_t char_id, ExpeditionMemberStatus status);
bool ConfirmLeaderCommand(Client* requester);
void OnClientAddRemove(Client* client, bool removed, bool silent);
void LoadRepositoryResult(const ExpeditionsRepository::Expeditions& entry);
bool ProcessAddConflicts(Client* leader_client, Client* add_client, bool swapping);
void ProcessLeaderChanged(uint32_t new_leader_id);
void ProcessLockoutDuration(const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false);
void ProcessLockoutUpdate(const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false);
void ProcessMakeLeader(Client* old_leader, Client* new_leader,
const std::string& new_leader_name, bool is_success, bool is_online);
void ProcessMemberAdded(const std::string& added_char_name, uint32_t added_char_id);
void ProcessMemberRemoved(const std::string& removed_char_name, uint32_t removed_char_id);
void SaveLockouts(ExpeditionRequest& request);
void SaveMembers(ExpeditionRequest& request);
void SendClientExpeditionInvite(
Client* client, const std::string& inviter_name, const std::string& swap_remove_name);
void SendLeaderMessage(Client* leader_client, uint16_t chat_type, uint32_t string_id,
const std::initializer_list<std::string>& args = {});
void SendMembersExpireWarning(uint32_t minutes);
void SendNewMemberAddedToZoneMembers(const std::string& added_name);
void SendUpdatesToZoneMembers(bool clear = false, bool message_on_clear = true);
void SendWorldDzLocationUpdate(uint16_t server_opcode, const DynamicZoneLocation& location);
void SendWorldExpeditionUpdate(uint16_t server_opcode);
void SendWorldAddPlayerInvite(const std::string& inviter_name, const std::string& swap_remove_name,
const std::string& add_name, bool pending = false);
@@ -205,38 +149,18 @@ private:
const ExpeditionLockoutTimer& lockout, int seconds, bool members_only = false);
void SendWorldLockoutUpdate(
const ExpeditionLockoutTimer& lockout, bool remove, bool members_only = false);
void SendWorldMemberChanged(const std::string& char_name, uint32_t char_id, bool remove);
void SendWorldMemberStatus(uint32_t character_id, ExpeditionMemberStatus status);
void SendWorldMemberSwapped(const std::string& remove_char_name, uint32_t remove_char_id,
const std::string& add_char_name, uint32_t add_char_id);
void SendWorldSetSecondsRemaining(uint32_t seconds_remaining);
void SendWorldSettingChanged(uint16_t server_opcode, bool setting_value);
void SetDynamicZone(DynamicZone&& dz);
void TryAddClient(Client* add_client, const std::string& inviter_name,
const std::string& swap_remove_name, Client* leader_client = nullptr);
void UpdateDzDuration(uint32_t new_duration) { m_dynamiczone.SetUpdatedDuration(new_duration); }
void UpdateMemberStatus(uint32_t update_character_id, ExpeditionMemberStatus status);
ExpeditionMember GetMemberData(uint32_t character_id);
ExpeditionMember GetMemberData(const std::string& character_name);
std::unique_ptr<EQApplicationPacket> CreateExpireWarningPacket(uint32_t minutes_remaining);
std::unique_ptr<EQApplicationPacket> CreateInfoPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateInvitePacket(const std::string& inviter_name, const std::string& swap_remove_name);
std::unique_ptr<EQApplicationPacket> CreateMemberListPacket(bool clear = false);
std::unique_ptr<EQApplicationPacket> CreateMemberListNamePacket(const std::string& name, bool remove_name);
std::unique_ptr<EQApplicationPacket> CreateMemberListStatusPacket(const std::string& name, ExpeditionMemberStatus status);
std::unique_ptr<EQApplicationPacket> CreateLeaderNamePacket();
uint32_t m_id = 0;
uint32_t m_min_players = 0;
uint32_t m_max_players = 0;
bool m_is_locked = false;
bool m_add_replay_on_join = true;
std::string m_uuid;
std::string m_expedition_name;
DynamicZone m_dynamiczone { DynamicZoneType::Expedition };
ExpeditionMember m_leader;
std::vector<ExpeditionMember> m_members;
uint32_t m_id = 0;
uint32_t m_dynamic_zone_id = 0;
bool m_is_locked = false;
bool m_add_replay_on_join = true;
DynamicZone* m_dynamic_zone = nullptr; // should never be null, will exist for lifetime of expedition
std::unordered_map<std::string, ExpeditionLockoutTimer> m_lockouts;
std::unordered_map<uint32_t, std::string> m_npc_loot_events; // only valid inside dz zone
std::unordered_map<uint32_t, std::string> m_spawn_loot_events; // only valid inside dz zone
+12 -238
View File
@@ -26,77 +26,27 @@
#include "../common/string_util.h"
#include <fmt/core.h>
uint32_t ExpeditionDatabase::InsertExpedition(
const std::string& uuid, uint32_t dz_id, const std::string& expedition_name,
uint32_t leader_id, uint32_t min_players, uint32_t max_players)
uint32_t ExpeditionDatabase::InsertExpedition(uint32_t dz_id)
{
LogExpeditionsDetail(
"Inserting new expedition [{}] leader [{}] uuid [{}]", expedition_name, leader_id, uuid
);
LogExpeditionsDetail("Inserting new expedition dz [{}]", dz_id);
std::string query = fmt::format(SQL(
INSERT INTO expeditions
(uuid, dynamic_zone_id, expedition_name, leader_id, min_players, max_players)
(dynamic_zone_id)
VALUES
('{}', {}, '{}', {}, {}, {});
), uuid, dz_id, EscapeString(expedition_name), leader_id, min_players, max_players);
({});
), dz_id);
auto results = database.QueryDatabase(query);
if (!results.Success())
{
LogExpeditions("Failed to obtain an expedition id for [{}]", expedition_name);
LogExpeditions("Failed to obtain an expedition id for dz [{}]", dz_id);
return 0;
}
return results.LastInsertedID();
}
std::string ExpeditionDatabase::LoadExpeditionsSelectQuery()
{
return std::string(SQL(
SELECT
expeditions.id,
expeditions.uuid,
expeditions.dynamic_zone_id,
expeditions.expedition_name,
expeditions.leader_id,
expeditions.min_players,
expeditions.max_players,
expeditions.add_replay_on_join,
expeditions.is_locked,
character_data.name leader_name,
expedition_members.character_id,
member_data.name
FROM expeditions
INNER JOIN character_data ON expeditions.leader_id = character_data.id
INNER JOIN expedition_members ON expeditions.id = expedition_members.expedition_id
AND expedition_members.is_current_member = TRUE
INNER JOIN character_data member_data ON expedition_members.character_id = member_data.id
));
}
MySQLRequestResult ExpeditionDatabase::LoadExpedition(uint32_t expedition_id)
{
LogExpeditionsDetail("Loading expedition [{}]", expedition_id);
std::string query = fmt::format(SQL(
{} WHERE expeditions.id = {};
), LoadExpeditionsSelectQuery(), expedition_id);
return database.QueryDatabase(query);
}
MySQLRequestResult ExpeditionDatabase::LoadAllExpeditions()
{
LogExpeditionsDetail("Loading all expeditions from database");
std::string query = fmt::format(SQL(
{} ORDER BY expeditions.id;
), LoadExpeditionsSelectQuery());
return database.QueryDatabase(query);
}
std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Loading character [{}] lockouts", character_id);
@@ -170,54 +120,6 @@ std::vector<ExpeditionLockoutTimer> ExpeditionDatabase::LoadCharacterLockouts(
return lockouts;
}
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
ExpeditionDatabase::LoadMultipleExpeditionLockouts(
const std::vector<uint32_t>& expedition_ids)
{
LogExpeditionsDetail("Loading internal lockouts for [{}] expeditions", expedition_ids.size());
std::string in_expedition_ids_query = fmt::format("{}", fmt::join(expedition_ids, ","));
// these are loaded into the same container type expeditions use to store lockouts
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>> lockouts;
if (!in_expedition_ids_query.empty())
{
std::string query = fmt::format(SQL(
SELECT
expedition_lockouts.expedition_id,
expedition_lockouts.from_expedition_uuid,
expeditions.expedition_name,
expedition_lockouts.event_name,
UNIX_TIMESTAMP(expedition_lockouts.expire_time),
expedition_lockouts.duration
FROM expedition_lockouts
INNER JOIN expeditions ON expedition_lockouts.expedition_id = expeditions.id
WHERE expedition_id IN ({})
ORDER BY expedition_id;
), in_expedition_ids_query);
auto results = database.QueryDatabase(query);
if (results.Success())
{
for (auto row = results.begin(); row != results.end(); ++row)
{
auto expedition_id = strtoul(row[0], nullptr, 10);
lockouts[expedition_id].emplace(row[3], ExpeditionLockoutTimer{
row[1], // expedition_uuid
row[2], // expedition_name
row[3], // event_name
strtoull(row[4], nullptr, 10), // expire_time
static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) // original duration
});
}
}
}
return lockouts;
}
void ExpeditionDatabase::DeleteAllCharacterLockouts(uint32_t character_id)
{
LogExpeditionsDetail("Deleting all character [{}] lockouts", character_id);
@@ -268,7 +170,7 @@ void ExpeditionDatabase::DeleteCharacterLockout(
}
void ExpeditionDatabase::DeleteMembersLockout(
const std::vector<ExpeditionMember>& members,
const std::vector<DynamicZoneMember>& members,
const std::string& expedition_name, const std::string& event_name)
{
LogExpeditionsDetail("Deleting members lockout: [{}]:[{}]", expedition_name, event_name);
@@ -276,7 +178,7 @@ void ExpeditionDatabase::DeleteMembersLockout(
std::string query_character_ids;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(query_character_ids), "{},", member.char_id);
fmt::format_to(std::back_inserter(query_character_ids), "{},", member.id);
}
if (!query_character_ids.empty())
@@ -307,67 +209,6 @@ void ExpeditionDatabase::DeleteLockout(uint32_t expedition_id, const std::string
database.QueryDatabase(query);
}
uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_id)
{
LogExpeditionsDetail("Getting expedition id for character [{}]", character_id);
uint32_t expedition_id = 0;
auto query = fmt::format(SQL(
SELECT expedition_id FROM expedition_members
WHERE character_id = {} AND is_current_member = TRUE;
), character_id);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
expedition_id = strtoul(row[0], nullptr, 10);
}
return expedition_id;
}
uint32_t ExpeditionDatabase::GetMemberCount(uint32_t expedition_id)
{
LogExpeditionsDetail("Getting expedition [{}] member count from db", expedition_id);
uint32_t member_count = 0;
if (expedition_id != 0)
{
auto query = fmt::format(SQL(
SELECT COUNT(*)
FROM expedition_members
WHERE expedition_id = {} AND is_current_member = TRUE;
), expedition_id);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
member_count = strtoul(row[0], nullptr, 10);
}
}
return member_count;
}
bool ExpeditionDatabase::HasMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Checking db expedition [{}] for character [{}]", expedition_id, character_id);
if (expedition_id == 0 || character_id == 0)
{
return false;
}
auto query = fmt::format(SQL(
SELECT id
FROM expedition_members
WHERE expedition_id = {} AND character_id = {} AND is_current_member = TRUE;
), expedition_id, character_id);
auto results = database.QueryDatabase(query);
return (results.Success() && results.RowCount() > 0);
}
void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts)
{
@@ -406,7 +247,7 @@ void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id,
}
void ExpeditionDatabase::InsertMembersLockout(
const std::vector<ExpeditionMember>& members, const ExpeditionLockoutTimer& lockout)
const std::vector<DynamicZoneMember>& members, const ExpeditionLockoutTimer& lockout)
{
LogExpeditionsDetail(
"Inserting members lockout [{}]:[{}] with expire time [{}]",
@@ -418,7 +259,7 @@ void ExpeditionDatabase::InsertMembersLockout(
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
member.char_id,
member.id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
@@ -509,50 +350,6 @@ void ExpeditionDatabase::InsertLockouts(
}
}
void ExpeditionDatabase::InsertMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Inserting character [{}] into expedition [{}]", character_id, expedition_id);
auto query = fmt::format(SQL(
INSERT INTO expedition_members
(expedition_id, character_id)
VALUES
({}, {})
ON DUPLICATE KEY UPDATE is_current_member = TRUE;
), expedition_id, character_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::InsertMembers(
uint32_t expedition_id, const std::vector<ExpeditionMember>& members)
{
LogExpeditionsDetail("Inserting characters into expedition [{}]", expedition_id);
std::string insert_values;
for (const auto& member : members)
{
fmt::format_to(std::back_inserter(insert_values),
"({}, {}),",
expedition_id, member.char_id
);
}
if (!insert_values.empty())
{
insert_values.pop_back(); // trailing comma
auto query = fmt::format(SQL(
INSERT INTO expedition_members
(expedition_id, character_id)
VALUES {}
ON DUPLICATE KEY UPDATE is_current_member = TRUE;
), insert_values);
database.QueryDatabase(query);
}
}
void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked)
{
LogExpeditionsDetail("Updating lock state [{}] for expedition [{}]", is_locked, expedition_id);
@@ -564,29 +361,6 @@ void ExpeditionDatabase::UpdateLockState(uint32_t expedition_id, bool is_locked)
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Removing member [{}] from expedition [{}]", character_id, expedition_id);
auto query = fmt::format(SQL(
UPDATE expedition_members SET is_current_member = FALSE
WHERE expedition_id = {} AND character_id = {};
), expedition_id, character_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::DeleteAllMembers(uint32_t expedition_id)
{
LogExpeditionsDetail("Removing all members of expedition [{}]", expedition_id);
auto query = fmt::format(SQL(
UPDATE expedition_members SET is_current_member = FALSE WHERE expedition_id = {};
), expedition_id);
database.QueryDatabase(query);
}
void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join)
{
LogExpeditionsDetail("Updating replay lockout on join [{}] for expedition [{}]", add_on_join, expedition_id);
@@ -598,7 +372,7 @@ void ExpeditionDatabase::UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool
database.QueryDatabase(query);
}
void ExpeditionDatabase::AddLockoutDuration(const std::vector<ExpeditionMember>& members,
void ExpeditionDatabase::AddLockoutDuration(const std::vector<DynamicZoneMember>& members,
const ExpeditionLockoutTimer& lockout, int seconds)
{
LogExpeditionsDetail(
@@ -610,7 +384,7 @@ void ExpeditionDatabase::AddLockoutDuration(const std::vector<ExpeditionMember>&
{
fmt::format_to(std::back_inserter(insert_values),
"({}, FROM_UNIXTIME({}), {}, '{}', '{}', '{}'),",
member.char_id,
member.id,
lockout.GetExpireTime(),
lockout.GetDuration(),
lockout.GetExpeditionUUID(),
+5 -38
View File
@@ -30,66 +30,33 @@
class Expedition;
class ExpeditionLockoutTimer;
struct ExpeditionMember;
struct DynamicZoneMember;
class MySQLRequestResult;
namespace ExpeditionDatabase
{
uint32_t InsertExpedition(
const std::string& uuid, uint32_t instance_id, const std::string& expedition_name,
uint32_t leader_id, uint32_t min_players, uint32_t max_players);
std::string LoadExpeditionsSelectQuery();
MySQLRequestResult LoadExpedition(uint32_t expedition_id);
MySQLRequestResult LoadAllExpeditions();
uint32_t InsertExpedition(uint32_t dz_id);
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id);
std::vector<ExpeditionLockoutTimer> LoadCharacterLockouts(uint32_t character_id,
const std::string& expedition_name);
std::unordered_map<uint32_t, std::unordered_map<std::string, ExpeditionLockoutTimer>>
LoadMultipleExpeditionLockouts(const std::vector<uint32_t>& expedition_ids);
void DeleteAllMembers(uint32_t expedition_id);
void DeleteMember(uint32_t expedition_id, uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id);
void DeleteAllCharacterLockouts(uint32_t character_id, const std::string& expedition_name);
void DeleteCharacterLockout(uint32_t character_id, const std::string& expedition_name,
const std::string& event_name);
void DeleteLockout(uint32_t expedition_id, const std::string& event_name);
void DeleteMembersLockout(const std::vector<ExpeditionMember>& members,
void DeleteMembersLockout(const std::vector<DynamicZoneMember>& members,
const std::string& expedition_name, const std::string& event_name);
uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id);
uint32_t GetMemberCount(uint32_t expedition_id);
bool HasMember(uint32_t expedition_id, uint32_t character_id);
void InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts);
void InsertMembersLockout(const std::vector<ExpeditionMember>& members,
void InsertMembersLockout(const std::vector<DynamicZoneMember>& members,
const ExpeditionLockoutTimer& lockout);
void InsertLockout(uint32_t expedition_id, const ExpeditionLockoutTimer& lockout);
void InsertLockouts(uint32_t expedition_id,
const std::unordered_map<std::string, ExpeditionLockoutTimer>& lockouts);
void InsertMember(uint32_t expedition_id, uint32_t character_id);
void InsertMembers(uint32_t expedition_id, const std::vector<ExpeditionMember>& members);
void UpdateLockState(uint32_t expedition_id, bool is_locked);
void UpdateReplayLockoutOnJoin(uint32_t expedition_id, bool add_on_join);
void AddLockoutDuration(const std::vector<ExpeditionMember>& members,
void AddLockoutDuration(const std::vector<DynamicZoneMember>& members,
const ExpeditionLockoutTimer& lockout, int seconds);
};
namespace LoadExpeditionColumns
{
enum eLoadExpeditionColumns
{
id = 0,
uuid,
dz_id,
expedition_name,
leader_id,
min_players,
max_players,
add_replay_on_join,
is_locked,
leader_name,
member_id,
member_name
};
};
#endif
+5 -7
View File
@@ -30,12 +30,10 @@
constexpr char SystemName[] = "expedition";
ExpeditionRequest::ExpeditionRequest(std::string expedition_name,
uint32_t min_players, uint32_t max_players, bool disable_messages
) :
m_expedition_name(std::move(expedition_name)),
m_min_players(min_players),
m_max_players(max_players),
ExpeditionRequest::ExpeditionRequest(const DynamicZone& dz, bool disable_messages) :
m_expedition_name(dz.GetName()),
m_min_players(dz.GetMinPlayers()),
m_max_players(dz.GetMaxPlayers()),
m_disable_messages(disable_messages)
{
}
@@ -221,7 +219,7 @@ bool ExpeditionRequest::CheckMembersForConflicts(const std::vector<std::string>&
return true;
}
m_members.emplace_back(character.id, character.name, ExpeditionMemberStatus::Online);
m_members.emplace_back(character.id, character.name, DynamicZoneMemberStatus::Online);
character_ids.emplace_back(character.id);
}
+4 -5
View File
@@ -35,8 +35,7 @@ class Raid;
class ExpeditionRequest
{
public:
ExpeditionRequest(std::string expedition_name, uint32_t min_players,
uint32_t max_players, bool disable_messages = false);
ExpeditionRequest(const DynamicZone& dz, bool disable_messages = false);
bool Validate(Client* requester);
@@ -47,8 +46,8 @@ public:
const std::string& GetNotAllAddedMessage() const { return m_not_all_added_msg; }
uint32_t GetMinPlayers() const { return m_min_players; }
uint32_t GetMaxPlayers() const { return m_max_players; }
std::vector<ExpeditionMember> GetMembers() const { return m_members; }
std::unordered_map<std::string, ExpeditionLockoutTimer> GetLockouts() const { return m_lockouts; }
const std::vector<DynamicZoneMember>& GetMembers() const { return m_members; }
const std::unordered_map<std::string, ExpeditionLockoutTimer>& GetLockouts() const { return m_lockouts; }
private:
bool CanMembersJoin(const std::vector<std::string>& member_names);
@@ -72,7 +71,7 @@ private:
std::string m_expedition_name;
std::string m_leader_name;
std::string m_not_all_added_msg;
std::vector<ExpeditionMember> m_members;
std::vector<DynamicZoneMember> m_members;
std::unordered_map<std::string, ExpeditionLockoutTimer> m_lockouts;
};
+2 -2
View File
@@ -367,7 +367,7 @@ void Client::GoFish()
PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if (RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(ActivityFish, food_id);
UpdateTasksForItem(TaskActivityType::Fish, food_id);
safe_delete(inst);
inst = m_inv.GetItem(EQ::invslot::slotCursor);
@@ -487,7 +487,7 @@ void Client::ForageItem(bool guarantee) {
PushItemOnCursor(*inst);
SendItemPacket(EQ::invslot::slotCursor, inst, ItemPacketLimbo);
if(RuleB(TaskSystem, EnableTaskSystem))
UpdateTasksForItem(ActivityForage, foragedfood);
UpdateTasksForItem(TaskActivityType::Forage, foragedfood);
safe_delete(inst);
inst = m_inv.GetItem(EQ::invslot::slotCursor);
+11
View File
@@ -0,0 +1,11 @@
#include "../client.h"
void command_acceptrules(Client *c, const Seperator *sep)
{
if (!database.GetAgreementFlag(c->AccountID())) {
database.SetAgreementFlag(c->AccountID());
c->SendAppearancePacket(AT_Anim, ANIM_STAND);
c->Message(Chat::White, "It is recorded you have agreed to the rules.");
}
}
+534
View File
@@ -0,0 +1,534 @@
#include "../client.h"
#include "../groups.h"
void command_advnpcspawn(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments) {
c->Message(
Chat::White,
"Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"
);
c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup");
c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup");
c->Message(
Chat::White,
"Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"
);
c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC");
c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location");
c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version");
return;
}
std::string spawn_command = str_tolower(sep->arg[1]);
bool is_add_entry = spawn_command.find("addentry") != std::string::npos;
bool is_add_spawn = spawn_command.find("addspawn") != std::string::npos;
bool is_clear_box = spawn_command.find("clearbox") != std::string::npos;
bool is_delete_spawn = spawn_command.find("deletespawn") != std::string::npos;
bool is_edit_box = spawn_command.find("editgroup") != std::string::npos;
bool is_edit_respawn = spawn_command.find("editrespawn") != std::string::npos;
bool is_make_group = spawn_command.find("makegroup") != std::string::npos;
bool is_make_npc = spawn_command.find("makenpc") != std::string::npos;
bool is_move_spawn = spawn_command.find("movespawn") != std::string::npos;
bool is_set_version = spawn_command.find("setversion") != std::string::npos;
if (
!is_add_entry &&
!is_add_spawn &&
!is_clear_box &&
!is_delete_spawn &&
!is_edit_box &&
!is_edit_respawn &&
!is_make_group &&
!is_make_npc &&
!is_move_spawn &&
!is_set_version
) {
c->Message(
Chat::White,
"Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance] - Adds a new Spawngroup Entry"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn addspawn [Spawngroup ID] - Adds a new Spawngroup Entry from an existing Spawngroup"
);
c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID] - Clears the roambox of a Spawngroup");
c->Message(Chat::White, "Usage: #advnpcspawn deletespawn - Deletes a Spawngroup");
c->Message(
Chat::White,
"Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Edit the roambox of a Spawngroup"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance] - Edit the Respawn Timer of a Spawngroup"
);
c->Message(
Chat::White,
"Usage: #advnpcspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay] - Makes a new Spawngroup"
);
c->Message(Chat::White, "Usage: #advnpcspawn makenpc - Makes a new NPC");
c->Message(Chat::White, "Usage: #advnpcspawn movespawn - Moves a Spawngroup to your current location");
c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version] - Sets a Spawngroup's Version");
return;
}
if (is_add_entry) {
if (arguments < 4) {
c->Message(Chat::White, "Usage: #advnpcspawn addentry [Spawngroup ID] [NPC ID] [Spawn Chance]");
return;
}
auto spawngroup_id = std::stoi(sep->arg[2]);
auto npc_id = std::stoi(sep->arg[2]);
auto spawn_chance = std::stoi(sep->arg[2]);
std::string query = fmt::format(
SQL(
INSERT INTO spawnentry(spawngroupID, npcID, chance)
VALUES({}, {}, {})
),
spawngroup_id,
npc_id,
spawn_chance
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to add entry to Spawngroup.");
return;
}
c->Message(
Chat::White,
fmt::format(
"{} ({}) added to Spawngroup {}, its spawn chance is {}%%.",
database.GetCleanNPCNameByID(npc_id),
npc_id,
spawngroup_id,
spawn_chance
).c_str()
);
return;
}
else if (is_add_spawn) {
content_db.NPCSpawnDB(
NPCSpawnTypes::AddSpawnFromSpawngroup,
zone->GetShortName(),
zone->GetInstanceVersion(),
c,
0,
std::stoi(sep->arg[2])
);
c->Message(
Chat::White,
fmt::format(
"Spawn Added | Added spawn from Spawngroup ID {}.",
std::stoi(sep->arg[2])
).c_str()
);
return;
}
else if (is_clear_box) {
if (arguments != 2 || !sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #advnpcspawn clearbox [Spawngroup ID]");
return;
}
std::string query = fmt::format(
"UPDATE spawngroup SET dist = 0, min_x = 0, max_x = 0, min_y = 0, max_y = 0, delay = 0 WHERE id = {}",
std::stoi(sep->arg[2])
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to clear Spawngroup box.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Cleared | Delay: 0 Distance: 0.00",
std::stoi(sep->arg[2])
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Cleared | Minimum X: 0.00 Maximum X: 0.00",
std::stoi(sep->arg[2])
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Cleared | Minimum Y: 0.00 Maximum Y: 0.00",
std::stoi(sep->arg[2])
).c_str()
);
return;
}
else if (is_delete_spawn) {
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC *target = c->GetTarget()->CastToNPC();
Spawn2 *spawn2 = target->respawn2;
if (!spawn2) {
c->Message(Chat::White, "Failed to delete spawn because NPC has no Spawn2.");
return;
}
auto spawn2_id = spawn2->GetID();
std::string query = fmt::format(
"DELETE FROM spawn2 WHERE id = {}",
spawn2_id
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to delete spawn.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawn2 {} Deleted | Name: {} ({})",
spawn2_id,
target->GetCleanName(),
target->GetID()
).c_str()
);
target->Depop(false);
return;
}
else if (is_edit_box) {
if (
arguments != 8 ||
!sep->IsNumber(3) ||
!sep->IsNumber(4) ||
!sep->IsNumber(5) ||
!sep->IsNumber(6) ||
!sep->IsNumber(7) ||
!sep->IsNumber(8)
) {
c->Message(
Chat::White,
"Usage: #advnpcspawn editbox [Spawngroup ID] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"
);
return;
}
auto spawngroup_id = std::stoi(sep->arg[2]);
auto distance = std::stof(sep->arg[3]);
auto minimum_x = std::stof(sep->arg[4]);
auto maximum_x = std::stof(sep->arg[5]);
auto minimum_y = std::stof(sep->arg[6]);
auto maximum_y = std::stof(sep->arg[7]);
auto delay = std::stoi(sep->arg[8]);
std::string query = fmt::format(
"UPDATE spawngroup SET dist = {:.2f}, min_x = {:.2f}, max_x = {:.2f}, max_y = {:.2f}, min_y = {:.2f}, delay = {} WHERE id = {}",
distance,
minimum_x,
maximum_x,
minimum_y,
maximum_y,
delay,
spawngroup_id
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to edit Spawngroup box.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Edited | Delay: {} Distance: {:.2f}",
spawngroup_id,
delay,
distance
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Edited | Minimum X: {:.2f} Maximum X: {:.2f}",
spawngroup_id,
minimum_x,
maximum_x
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Roambox Edited | Minimum Y: {:.2f} Maximum Y: {:.2f}",
spawngroup_id,
minimum_y,
maximum_y
).c_str()
);
return;
}
else if (is_edit_respawn) {
if (arguments < 2 || !sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #advnpcspawn editrespawn [Respawn Timer] [Variance]");
return;
}
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC *target = c->GetTarget()->CastToNPC();
Spawn2 *spawn2 = target->respawn2;
if (!spawn2) {
c->Message(Chat::White, "Failed to edit respawn because NPC has no Spawn2.");
return;
}
auto spawn2_id = spawn2->GetID();
uint32 respawn_timer = std::stoi(sep->arg[2]);
uint32 variance = (
sep->IsNumber(3) ?
std::stoi(sep->arg[3]) :
spawn2->GetVariance()
);
std::string query = fmt::format(
"UPDATE spawn2 SET respawntime = {}, variance = {} WHERE id = {}",
respawn_timer,
variance,
spawn2_id
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to edit respawn.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawn2 {} Respawn Modified | Name: {} ({}) Respawn Timer: {} Variance: {}",
spawn2_id,
target->GetCleanName(),
target->GetID(),
respawn_timer,
variance
).c_str()
);
spawn2->SetRespawnTimer(respawn_timer);
spawn2->SetVariance(variance);
return;
}
else if (is_make_group) {
if (
arguments != 9 ||
!sep->IsNumber(3) ||
!sep->IsNumber(4) ||
!sep->IsNumber(5) ||
!sep->IsNumber(6) ||
!sep->IsNumber(7) ||
!sep->IsNumber(8) ||
!sep->IsNumber(9)
) {
c->Message(
Chat::White,
"Usage: #advncspawn makegroup [Spawn Group Name] [Spawn Limit] [Distance] [Minimum X] [Maximum X] [Minimum Y] [Maximum Y] [Delay]"
);
return;
}
std::string spawngroup_name = sep->arg[2];
auto spawn_limit = std::stoi(sep->arg[3]);
auto distance = std::stof(sep->arg[4]);
auto minimum_x = std::stof(sep->arg[5]);
auto maximum_x = std::stof(sep->arg[6]);
auto minimum_y = std::stof(sep->arg[7]);
auto maximum_y = std::stof(sep->arg[8]);
auto delay = std::stoi(sep->arg[9]);
std::string query = fmt::format(
"INSERT INTO spawngroup"
"(name, spawn_limit, dist, min_x, max_x, min_y, max_y, delay)"
"VALUES ('{}', {}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {:.2f}, {})",
spawngroup_name,
spawn_limit,
distance,
minimum_x,
maximum_x,
minimum_y,
maximum_y,
delay
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to make Spawngroup.");
return;
}
auto spawngroup_id = results.LastInsertedID();
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Created | Name: {} Spawn Limit: {}",
spawngroup_id,
spawngroup_name,
spawn_limit
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Created | Delay: {} Distance: {:.2f}",
spawngroup_id,
delay,
distance
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Created | Minimum X: {:.2f} Maximum X: {:.2f}",
spawngroup_id,
minimum_x,
maximum_x
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Created | Minimum Y: {:.2f} Maximum Y: {:.2f}",
spawngroup_id,
minimum_y,
maximum_y
).c_str()
);
return;
}
else if (is_make_npc) {
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC *target = c->GetTarget()->CastToNPC();
content_db.NPCSpawnDB(
NPCSpawnTypes::CreateNewNPC,
zone->GetShortName(),
zone->GetInstanceVersion(),
c,
target
);
return;
}
else if (is_move_spawn) {
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC *target = c->GetTarget()->CastToNPC();
Spawn2 *spawn2 = target->respawn2;
if (!spawn2) {
c->Message(Chat::White, "Failed to move spawn because NPC has no Spawn2.");
return;
}
auto client_position = c->GetPosition();
auto spawn2_id = spawn2->GetID();
std::string query = fmt::format(
"UPDATE spawn2 SET x = {:.2f}, y = {:.2f}, z = {:.2f}, heading = {:.2f} WHERE id = {}",
client_position.x,
client_position.y,
client_position.z,
client_position.w,
spawn2_id
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to move spawn.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawn2 {} Moved | Name: {} ({})",
spawn2_id,
target->GetCleanName(),
target->GetID()
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Spawn2 {} Moved | XYZ: {}, {}, {} Heading: {}",
spawn2_id,
client_position.x,
client_position.y,
client_position.z,
client_position.w
).c_str()
);
target->GMMove(
client_position.x,
client_position.y,
client_position.z,
client_position.w
);
return;
}
else if (is_set_version) {
if (arguments != 2 || !sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #advnpcspawn setversion [Version]");
return;
}
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC *target = c->GetTarget()->CastToNPC();
auto version = std::stoi(sep->arg[2]);
std::string query = fmt::format(
"UPDATE spawn2 SET version = {} WHERE spawngroupID = {}",
version,
target->GetSpawnGroupId()
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::White, "Failed to set version.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Spawngroup {} Version Modified | Name: {} ({}) Version: {}",
target->GetSpawnGroupId(),
target->GetCleanName(),
target->GetID(),
version
).c_str()
);
target->Depop(false);
return;
}
}
+28
View File
@@ -0,0 +1,28 @@
#include "../client.h"
void command_aggro(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #aggro [Distance] [-v] (-v is verbose Faction Information)");
return;
}
if (
!c->GetTarget() ||
(
c->GetTarget() &&
!c->GetTarget()->IsNPC()
)
) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
NPC* target = c->GetTarget()->CastToNPC();
float distance = std::stof(sep->arg[1]);
bool verbose = !strcasecmp("-v", sep->arg[2]);
entity_list.DescribeAggro(c, target, distance, verbose);
}
+31
View File
@@ -0,0 +1,31 @@
#include "../client.h"
void command_aggrozone(Client *c, const Seperator *sep)
{
Mob *target = c;
if (c->GetTarget()) {
target = c->GetTarget();
}
uint32 hate = 0;
if (sep->IsNumber(1)) {
hate = std::stoul(sep->arg[1]);
}
entity_list.AggroZone(target, hate);
c->Message(
Chat::White,
fmt::format(
"Aggroing zone on {}.",
(
c == target ?
"yourself" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
)
).c_str()
);
}
+263
View File
@@ -0,0 +1,263 @@
#include "../client.h"
void command_ai(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob");
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
return;
}
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
auto target = c->GetTarget()->CastToNPC();
bool is_consider = !strcasecmp(sep->arg[1], "consider");
bool is_faction = !strcasecmp(sep->arg[1], "faction");
bool is_guard = !strcasecmp(sep->arg[1], "guard");
bool is_roambox = !strcasecmp(sep->arg[1], "roambox");
bool is_spells = !strcasecmp(sep->arg[1], "spells");
if (
!is_consider &&
!is_faction &&
!is_guard &&
!is_roambox &&
!is_spells
) {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob");
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
return;
}
if (is_consider) {
if (arguments == 2) {
auto mob_name = sep->arg[2];
auto mob_to_consider = entity_list.GetMob(mob_name);
if (mob_to_consider) {
auto consider_level = static_cast<uint8>(mob_to_consider->GetReverseFactionCon(target));
c->Message(
Chat::White,
fmt::format(
"{} ({}) considers {} ({}) as {} ({}).",
target->GetCleanName(),
target->GetID(),
mob_to_consider->GetCleanName(),
mob_to_consider->GetID(),
EQ::constants::GetConsiderLevelName(consider_level),
consider_level
).c_str()
);
}
} else {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers a mob");
}
} else if (is_faction) {
if (sep->IsNumber(2)) {
auto faction_id = std::stoi(sep->arg[2]);
auto faction_name = content_db.GetFactionName(faction_id);
target->SetNPCFactionID(faction_id);
c->Message(
Chat::White,
fmt::format(
"{} ({}) is now on Faction {}.",
target->GetCleanName(),
target->GetID(),
(
faction_name.empty() ?
std::to_string(faction_id) :
fmt::format(
"{} ({})",
faction_name,
faction_id
)
)
).c_str()
);
} else {
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
}
} else if (is_guard) {
auto target_position = target->GetPosition();
target->SaveGuardSpot(target_position);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a guard spot of {:.2f}, {:.2f}, {:.2f} with a heading of {:.2f}.",
target->GetCleanName(),
target->GetID(),
target_position.x,
target_position.y,
target_position.z,
target_position.w
).c_str()
);
} else if (is_roambox) {
if (target->IsAIControlled()) {
if (
arguments >= 6 &&
arguments <= 8 &&
sep->IsNumber(2) &&
sep->IsNumber(3) &&
sep->IsNumber(4) &&
sep->IsNumber(5) &&
sep->IsNumber(6)
) {
auto distance = std::stof(sep->arg[2]);
auto min_x = std::stof(sep->arg[3]);
auto max_x = std::stof(sep->arg[4]);
auto min_y = std::stof(sep->arg[5]);
auto max_y = std::stof(sep->arg[6]);
uint32 delay = 2500;
uint32 minimum_delay = 2500;
if (sep->IsNumber(7)) {
delay = std::stoul(sep->arg[7]);
}
if (sep->IsNumber(8)) {
minimum_delay = std::stoul(sep->arg[8]);
}
target->CastToNPC()->AI_SetRoambox(
distance,
max_x,
min_x,
max_y,
min_y,
delay,
minimum_delay
);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a roambox from {}, {} to {}, {} with {} and {} and a distance of {}.",
target->GetCleanName(),
target->GetID(),
min_x,
min_y,
max_x,
max_y,
(
delay ?
fmt::format(
"a delay of {} ({})",
ConvertMillisecondsToTime(delay),
delay
):
"no delay"
),
(
minimum_delay ?
fmt::format(
"a minimum delay of {} ({})",
ConvertMillisecondsToTime(minimum_delay),
minimum_delay
):
"no minimum delay"
),
distance
).c_str()
);
} else if (
arguments >= 3 &&
arguments <= 4 &&
sep->IsNumber(2) &&
sep->IsNumber(3)
) {
auto max_distance = std::stof(sep->arg[2]);
auto roam_distance_variance = std::stof(sep->arg[3]);
uint32 delay = 2500;
uint32 minimum_delay = 2500;
if (sep->IsNumber(4)) {
delay = std::stoul(sep->arg[4]);
}
if (sep->IsNumber(5)) {
minimum_delay = std::stoul(sep->arg[5]);
}
target->CastToNPC()->AI_SetRoambox(
max_distance,
roam_distance_variance,
delay,
minimum_delay
);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a roambox with a max distance of {} and a roam distance variance of {} with {} and {}.",
target->GetCleanName(),
target->GetID(),
max_distance,
roam_distance_variance,
(
delay ?
fmt::format(
"a delay of {} ({})",
delay,
ConvertMillisecondsToTime(delay)
):
"no delay"
),
(
minimum_delay ?
fmt::format(
"a minimum delay of {} ({})",
minimum_delay,
ConvertMillisecondsToTime(delay)
):
"no minimum delay"
)
).c_str()
);
} else {
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
}
} else {
c->Message(Chat::White, "You must target an NPC with AI.");
}
} else if (is_spells) {
if (sep->IsNumber(2)) {
auto spell_list_id = std::stoul(sep->arg[2]);
if (spell_list_id >= 0) {
target->CastToNPC()->AI_AddNPCSpells(spell_list_id);
c->Message(
Chat::White,
fmt::format(
"{} ({}) is now using Spell List {}.",
target->GetCleanName(),
target->GetID(),
spell_list_id
).c_str()
);
} else {
c->Message(Chat::White, "Spell List ID must be greater than or equal to 0.");
}
} else {
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
}
}
}
+26
View File
@@ -0,0 +1,26 @@
#include "../client.h"
void command_appearance(Client *c, const Seperator *sep)
{
Mob *t = c->CastToMob();
// sends any appearance packet
// Dev debug command, for appearance types
if (sep->arg[2][0] == 0) {
c->Message(Chat::White, "Usage: #appearance type value");
}
else {
if ((c->GetTarget())) {
t = c->GetTarget();
}
t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2]));
c->Message(
Chat::White,
"Sending appearance packet: target=%s, type=%s, value=%s",
t->GetName(),
sep->arg[1],
sep->arg[2]
);
}
}
+44
View File
@@ -0,0 +1,44 @@
#include "../client.h"
void command_appearanceeffects(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) {
c->Message(Chat::White, "Syntax: #appearanceeffects [subcommand].");
c->Message(Chat::White, "[view] Display all appearance effects saved to your target. #appearanceffects view");
c->Message(Chat::White, "[set] Set an appearance effects saved to your target. #appearanceffects set [app_effectid] [slotid]");
c->Message(Chat::White, "[remove] Remove all appearance effects saved to your target. #appearanceffects remove");
}
if (!strcasecmp(sep->arg[1], "view")) {
Mob* m_target = c->GetTarget();
if (m_target) {
m_target->GetAppearenceEffects();
}
return;
}
if (!strcasecmp(sep->arg[1], "set")) {
int32 app_effectid = atof(sep->arg[2]);
int32 slot = atoi(sep->arg[3]);
Mob* m_target = c->GetTarget();
if (m_target) {
m_target->SendAppearanceEffect(app_effectid, 0, 0, 0, 0, nullptr, slot, 0, 0, 0, 0, 0, 0, 0, 0, 0);
c->Message(Chat::White, "Appearance Effect ID %i for slot %i has been set.", app_effectid, slot);
}
}
if (!strcasecmp(sep->arg[1], "remove")) {
Mob* m_target = c->GetTarget();
if (m_target) {
m_target->SendIllusionPacket(m_target->GetRace(), m_target->GetGender(), m_target->GetTexture(), m_target->GetHelmTexture(),
m_target->GetHairColor(), m_target->GetBeardColor(), m_target->GetEyeColor1(), m_target->GetEyeColor2(),
m_target->GetHairStyle(), m_target->GetLuclinFace(), m_target->GetBeard(), 0xFF,
m_target->GetDrakkinHeritage(), m_target->GetDrakkinTattoo(), m_target->GetDrakkinDetails(), m_target->GetSize(), false);
m_target->ClearAppearenceEffects();
c->Message(Chat::White, "All Appearance Effects have been removed.");
}
return;
}
}
+18
View File
@@ -0,0 +1,18 @@
#include "../client.h"
void command_attack(Client *c, const Seperator *sep)
{
if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) {
Mob *sictar = entity_list.GetMob(sep->argplus[1]);
if (sictar) {
c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0);
}
else {
c->Message(Chat::White, "Error: %s not found", sep->arg[1]);
}
}
else {
c->Message(Chat::White, "Usage: (needs NPC targeted) #attack targetname");
}
}
+18
View File
@@ -0,0 +1,18 @@
#include "../client.h"
#include "../object.h"
void command_augmentitem(Client *c, const Seperator *sep)
{
if (!c) {
return;
}
auto in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)];
in_augment->container_slot = 1000; // <watch>
in_augment->augment_slot = -1;
if (c->GetTradeskillObject() != nullptr) {
Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject());
}
safe_delete_array(in_augment);
}
+72
View File
@@ -0,0 +1,72 @@
#include "../client.h"
#include "../worldserver.h"
extern WorldServer worldserver;
void command_ban(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == 0 || sep->arg[2][0] == 0) {
c->Message(Chat::White, "Usage: #ban <charname> <message>");
return;
}
auto account_id = database.GetAccountIDByChar(sep->arg[1]);
std::string message;
int i = 2;
while (1) {
if (sep->arg[i][0] == 0) {
break;
}
if (message.length() > 0) {
message.push_back(' ');
}
message += sep->arg[i];
++i;
}
if (message.length() == 0) {
c->Message(Chat::White, "Usage: #ban <charname> <message>");
return;
}
if (account_id == 0) {
c->Message(Chat::Red, "Character does not exist.");
return;
}
std::string query = StringFormat(
"UPDATE account SET status = -2, ban_reason = '%s' "
"WHERE id = %i", EscapeString(message).c_str(), account_id
);
auto results = database.QueryDatabase(query);
c->Message(
Chat::Red,
"Account number %i with the character %s has been banned with message: \"%s\"",
account_id,
sep->arg[1],
message.c_str());
ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6);
*((uint32 *) &flagUpdatePack.pBuffer[0]) = account_id;
*((int16 *) &flagUpdatePack.pBuffer[4]) = -2;
worldserver.SendPacket(&flagUpdatePack);
Client *client = nullptr;
client = entity_list.GetClientByName(sep->arg[1]);
if (client) {
client->WorldKick();
return;
}
ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) kickPlayerPack.pBuffer;
strcpy(skp->adminname, c->GetName());
strcpy(skp->name, sep->arg[1]);
skp->adminrank = c->Admin();
worldserver.SendPacket(&kickPlayerPack);
}
+37
View File
@@ -0,0 +1,37 @@
#include "../client.h"
void command_beard(Client *c, const Seperator *sep)
{
Mob *target = c->GetTarget();
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #beard [number of beard style]");
}
else if (!target) {
c->Message(Chat::White, "Error: this command requires a target");
}
else {
uint16 Race = target->GetRace();
uint8 Gender = target->GetGender();
uint8 Texture = 0xFF;
uint8 HelmTexture = 0xFF;
uint8 HairColor = target->GetHairColor();
uint8 BeardColor = target->GetBeardColor();
uint8 EyeColor1 = target->GetEyeColor1();
uint8 EyeColor2 = target->GetEyeColor2();
uint8 HairStyle = target->GetHairStyle();
uint8 LuclinFace = target->GetLuclinFace();
uint8 Beard = atoi(sep->arg[1]);
uint32 DrakkinHeritage = target->GetDrakkinHeritage();
uint32 DrakkinTattoo = target->GetDrakkinTattoo();
uint32 DrakkinDetails = target->GetDrakkinDetails();
target->SendIllusionPacket(
Race, Gender, Texture, HelmTexture, HairColor, BeardColor,
EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF,
DrakkinHeritage, DrakkinTattoo, DrakkinDetails
);
c->Message(Chat::White, "Beard = %i", atoi(sep->arg[1]));
}
}
+37
View File
@@ -0,0 +1,37 @@
#include "../client.h"
void command_beardcolor(Client *c, const Seperator *sep)
{
Mob *target = c->GetTarget();
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #beardcolor [number of beard color]");
}
else if (!target) {
c->Message(Chat::White, "Error: this command requires a target");
}
else {
uint16 Race = target->GetRace();
uint8 Gender = target->GetGender();
uint8 Texture = 0xFF;
uint8 HelmTexture = 0xFF;
uint8 HairColor = target->GetHairColor();
uint8 BeardColor = atoi(sep->arg[1]);
uint8 EyeColor1 = target->GetEyeColor1();
uint8 EyeColor2 = target->GetEyeColor2();
uint8 HairStyle = target->GetHairStyle();
uint8 LuclinFace = target->GetLuclinFace();
uint8 Beard = target->GetBeard();
uint32 DrakkinHeritage = target->GetDrakkinHeritage();
uint32 DrakkinTattoo = target->GetDrakkinTattoo();
uint32 DrakkinDetails = target->GetDrakkinDetails();
target->SendIllusionPacket(
Race, Gender, Texture, HelmTexture, HairColor, BeardColor,
EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF,
DrakkinHeritage, DrakkinTattoo, DrakkinDetails
);
c->Message(Chat::White, "Beard Color = %i", atoi(sep->arg[1]));
}
}
+89
View File
@@ -0,0 +1,89 @@
#include "../client.h"
#include "../water_map.h"
void command_bestz(Client *c, const Seperator *sep)
{
if (zone->zonemap == nullptr) {
c->Message(Chat::White, "Map not loaded for this zone");
}
else {
glm::vec3 me;
me.x = c->GetX();
me.y = c->GetY();
me.z = c->GetZ() + (c->GetSize() == 0.0 ? 6 : c->GetSize()) * HEAD_POSITION;
glm::vec3 hit;
glm::vec3 bme(me);
bme.z -= 500;
float best_z = zone->zonemap->FindBestZ(me, &hit);
if (best_z != BEST_Z_INVALID) {
c->Message(Chat::White, "Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y);
}
else {
c->Message(Chat::White, "Found no Z.");
}
}
if (zone->watermap == nullptr) {
c->Message(Chat::White, "Water Region Map not loaded for this zone");
}
else {
WaterRegionType RegionType;
float z;
if (c->GetTarget()) {
z = c->GetTarget()->GetZ();
auto position = glm::vec3(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z);
RegionType = zone->watermap->ReturnRegionType(position);
c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position));
c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position));
}
else {
z = c->GetZ();
auto position = glm::vec3(c->GetX(), c->GetY(), z);
RegionType = zone->watermap->ReturnRegionType(position);
c->Message(Chat::White, "InWater returns %d", zone->watermap->InWater(position));
c->Message(Chat::White, "InLava returns %d", zone->watermap->InLava(position));
}
switch (RegionType) {
case RegionTypeNormal: {
c->Message(Chat::White, "There is nothing special about the region you are in!");
break;
}
case RegionTypeWater: {
c->Message(Chat::White, "You/your target are in Water.");
break;
}
case RegionTypeLava: {
c->Message(Chat::White, "You/your target are in Lava.");
break;
}
case RegionTypeVWater: {
c->Message(Chat::White, "You/your target are in VWater (Icy Water?).");
break;
}
case RegionTypePVP: {
c->Message(Chat::White, "You/your target are in a pvp enabled area.");
break;
}
case RegionTypeSlime: {
c->Message(Chat::White, "You/your target are in slime.");
break;
}
case RegionTypeIce: {
c->Message(Chat::White, "You/your target are in ice.");
break;
}
default:
c->Message(Chat::White, "You/your target are in an unknown region type.");
}
}
}
+56
View File
@@ -0,0 +1,56 @@
#include "../client.h"
void command_bind(Client *c, const Seperator *sep)
{
Client *target = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
}
target->SetBindPoint();
bool in_persistent_instance = (
zone->GetInstanceID() != 0 &&
zone->IsInstancePersistent()
);
auto target_string = (
c == target ?
"Yourself" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
);
c->Message(
Chat::White,
fmt::format(
"Set Bind Point for {} | Zone: {} ({}) ID: {} {}",
target_string,
zone->GetLongName(),
zone->GetShortName(),
zone->GetZoneID(),
(
in_persistent_instance ?
fmt::format(
" Instance ID: {}",
zone->GetInstanceID()
) :
""
)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Set Bind Point for {} | XYZ: {:.2f}, {:.2f}, {:.2f}",
target_string,
target->GetX(),
target->GetY(),
target->GetZ()
).c_str()
);
}
+33
View File
@@ -0,0 +1,33 @@
#include "../client.h"
#include "../worldserver.h"
extern WorldServer worldserver;
void command_camerashake(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1) || !sep->IsNumber(2)) {
c->Message(Chat::Red, "Usage: #camerashake [Duration (Milliseconds)] [Intensity (1-10)]");
return;
}
auto duration = std::stoi(sep->arg[1]);
auto intensity = std::stoi(sep->arg[2]);
auto pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct));
ServerCameraShake_Struct *camera_shake = (ServerCameraShake_Struct *) pack->pBuffer;
camera_shake->duration = duration;
camera_shake->intensity = intensity;
worldserver.SendPacket(pack);
c->Message(
Chat::White,
fmt::format(
"Sending camera shake to world with a duration of {} ({}) and an intensity of {}.",
ConvertMillisecondsToTime(duration),
duration,
intensity
).c_str()
);
safe_delete(pack);
}
+72
View File
@@ -0,0 +1,72 @@
#include "../client.h"
void command_castspell(Client *c, const Seperator *sep)
{
if (SPDAT_RECORDS <= 0) {
c->Message(Chat::White, "Spells not loaded.");
return;
}
Mob *target = c;
if (c->GetTarget()) {
target = c->GetTarget();
}
if (!sep->IsNumber(1)) {
c->Message(
Chat::White,
"Usage: #castspell [Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)]"
);
}
else {
uint16 spell_id = std::stoul(sep->arg[1]);
if (CastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) {
c->Message(Chat::Red, "Unable to cast spell.");
}
else if (spell_id >= SPDAT_RECORDS) {
c->Message(Chat::White, "Invalid Spell ID.");
}
else {
bool instant_cast = (c->Admin() >= commandInstacast ? true : false);
if (instant_cast && sep->IsNumber(2)) {
instant_cast = std::stoi(sep->arg[2]) ? true : false;
c->Message(Chat::White, fmt::format("{}", std::stoi(sep->arg[2])).c_str());
}
if (c->Admin() >= commandInstacast && instant_cast) {
c->SpellFinished(
spell_id,
target,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty
);
}
else {
c->CastSpell(spell_id, target->GetID(), EQ::spells::CastingSlot::Item, spells[spell_id].cast_time);
}
c->Message(
Chat::White,
fmt::format(
"Cast {} ({}) on {}{}.",
GetSpellName(spell_id),
spell_id,
(
c == target ?
"yourself" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
),
instant_cast ? " instantly" : ""
).c_str()
);
}
}
}
+15
View File
@@ -0,0 +1,15 @@
#include "../client.h"
#include "../worldserver.h"
extern WorldServer worldserver;
void command_chat(Client *c, const Seperator *sep)
{
if (sep->arg[2][0] == 0) {
c->Message(Chat::White, "Usage: #chat [channum] [message]");
}
else if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, 100, sep->argplus[2])) {
c->Message(Chat::White, "Error: World server disconnected");
}
}
+20
View File
@@ -0,0 +1,20 @@
#include "../client.h"
void command_checklos(Client *c, const Seperator *sep)
{
if (!c->GetTarget()) {
c->Message(Chat::White, "You must have a target to use this command.");
}
bool has_los = c->CheckLosFN(c->GetTarget());
c->Message(
Chat::White,
fmt::format(
"You {}have line of sight to {} ({}).",
has_los ? "" : "do not ",
c->GetTarget()->GetCleanName(),
c->GetTarget()->GetID()
).c_str()
);
}
+34
View File
@@ -0,0 +1,34 @@
#include "../client.h"
void command_copycharacter(Client *c, const Seperator *sep)
{
if (sep->argnum < 3) {
c->Message(
Chat::White,
"Usage: [source_character_name] [destination_character_name] [destination_account_name]"
);
return;
}
std::string source_character_name = sep->arg[1];
std::string destination_character_name = sep->arg[2];
std::string destination_account_name = sep->arg[3];
bool result = database.CopyCharacter(
source_character_name,
destination_character_name,
destination_account_name
);
c->Message(
Chat::Yellow,
fmt::format(
"Character Copy [{}] to [{}] via account [{}] [{}]",
source_character_name,
destination_character_name,
destination_account_name,
result ? "Success" : "Failed"
).c_str()
);
}
+356
View File
@@ -0,0 +1,356 @@
#include "../client.h"
#include "../corpse.h"
void command_corpse(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #corpse delete - Delete targeted corpse");
c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses");
c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse");
c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses");
c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked");
c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse");
c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse");
if (c->Admin() >= commandEditPlayerCorpses) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance");
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
}
return;
}
Mob *target = c->GetTarget();
bool is_character_id = !strcasecmp(sep->arg[1], "charid");
bool is_delete = !strcasecmp(sep->arg[1], "delete");
bool is_delete_npc_corpses = !strcasecmp(sep->arg[1], "deletenpccorpses");
bool is_delete_player_corpses = !strcasecmp(sep->arg[1], "deleteplayercorpses");
bool is_depop = !strcasecmp(sep->arg[1], "depop");
bool is_depop_all = !strcasecmp(sep->arg[1], "depopall");
bool is_inspect_loot = !strcasecmp(sep->arg[1], "inspectloot");
bool is_list_npc = !strcasecmp(sep->arg[1], "listnpc");
bool is_list_player = !strcasecmp(sep->arg[1], "listplayer");
bool is_lock = !strcasecmp(sep->arg[1], "lock");
bool is_move_all_to_graveyard = !strcasecmp(sep->arg[1], "moveallgraveyard");
bool is_remove_cash = !strcasecmp(sep->arg[1], "removecash");
bool is_reset_looter = !strcasecmp(sep->arg[1], "resetlooter");
bool is_unlock = !strcasecmp(sep->arg[1], "unlock");
if (
!is_character_id &&
!is_delete &&
!is_delete_npc_corpses &&
!is_delete_player_corpses &&
!is_depop &&
!is_depop_all &&
!is_inspect_loot &&
!is_list_npc &&
!is_list_player &&
!is_lock &&
!is_move_all_to_graveyard &&
!is_remove_cash &&
!is_reset_looter &&
!is_unlock
) {
c->Message(Chat::White, "Usage: #corpse delete - Delete targeted corpse");
c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses");
c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse");
c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses");
c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked");
c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse");
c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse");
if (c->Admin() >= commandEditPlayerCorpses) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance");
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
}
return;
}
if (is_delete_player_corpses) {
if (c->Admin() >= commandEditPlayerCorpses) {
auto corpses_deleted = entity_list.DeletePlayerCorpses();
auto deleted_string = (
corpses_deleted ?
fmt::format(
"{} Player corpse{} deleted.",
corpses_deleted,
corpses_deleted != 1 ? "s" : ""
) :
"There are no player corpses to delete."
);
c->Message(Chat::White, deleted_string.c_str());
} else {
c->Message(Chat::White, "Your status is not high enough to delete player corpses.");
return;
}
} else if (is_delete) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to delete a player corpse.");
return;
}
if (
target->IsNPCCorpse() ||
c->Admin() >= commandEditPlayerCorpses
) {
c->Message(
Chat::White,
fmt::format(
"Deleting {} corpse {} ({}).",
target->IsNPCCorpse() ? "NPC" : "player",
target->GetName(),
target->GetID()
).c_str()
);
target->CastToCorpse()->Delete();
}
} else if (is_list_npc) {
entity_list.ListNPCCorpses(c);
} else if (is_list_player) {
if (c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to list player corpses.");
return;
}
entity_list.ListPlayerCorpses(c);
} else if (is_delete_npc_corpses) {
auto corpses_deleted = entity_list.DeleteNPCCorpses();
auto deleted_string = (
corpses_deleted ?
fmt::format(
"{} NPC corpse{} deleted.",
corpses_deleted,
corpses_deleted != 1 ? "s" : ""
) :
"There are no NPC corpses to delete."
);
c->Message(Chat::White, deleted_string.c_str());
} else if (is_character_id) {
if (c->Admin() >= commandEditPlayerCorpses) {
if (!target || !target->IsPlayerCorpse()) {
c->Message(Chat::White, "You must target a player corpse to use this command.");
return;
}
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
return;
}
auto character_id = std::stoi(sep->arg[2]);
c->Message(
Chat::White,
fmt::format(
"Setting the owner to {} ({}) for the player corpse {} ({}).",
database.GetCharNameByID(character_id),
target->CastToCorpse()->SetCharID(character_id),
target->GetName(),
target->GetID()
).c_str()
);
} else {
c->Message(Chat::White, "Your status is not high enough to modify a player corpse's owner.");
return;
}
} else if (is_reset_looter) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to reset looter on a player corpse.");
return;
}
target->CastToCorpse()->ResetLooter();
c->Message(
Chat::White,
fmt::format(
"Reset looter for {} corpse {} ({}).",
target->IsNPCCorpse() ? "NPC" : "player",
target->GetName(),
target->GetID()
).c_str()
);
} else if (is_remove_cash) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to remove cash from a player corpse.");
return;
}
if (
target->IsNPCCorpse() ||
c->Admin() >= commandEditPlayerCorpses
) {
target->CastToCorpse()->RemoveCash();
c->Message(
Chat::White,
fmt::format(
"Removed cash from {} corpse {} ({}).",
target->IsNPCCorpse() ? "NPC" : "player",
target->GetName(),
target->GetID()
).c_str()
);
}
} else if (is_inspect_loot) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to inspect the loot of a player corpse.");
return;
}
target->CastToCorpse()->QueryLoot(c);
} else if (is_lock) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to lock player corpses.");
return;
}
target->CastToCorpse()->Lock();
c->Message(
Chat::White,
fmt::format(
"Locking {} corpse {} ({}).",
target->IsNPCCorpse() ? "NPC" : "player",
target->GetName(),
target->GetID()
).c_str()
);
} else if (is_unlock) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to unlock player corpses.");
return;
}
target->CastToCorpse()->UnLock();
c->Message(
Chat::White,
fmt::format(
"Unlocking {} corpse {} ({}).",
target->IsNPCCorpse() ? "NPC" : "player",
target->GetName(),
target->GetID()
).c_str()
);
} else if (is_depop) {
if (!target || !target->IsPlayerCorpse()) {
c->Message(Chat::White, "You must target a player corpse to use this command.");
return;
}
if (c->Admin() >= commandEditPlayerCorpses) {
bool bury_corpse = (
sep->IsNumber(2) ?
(
std::stoi(sep->arg[2]) != 0 ?
true :
false
) :
false
);
c->Message(
Chat::White,
fmt::format(
"Depopping player corpse {} ({}).",
target->GetName(),
target->GetID()
).c_str()
);
target->CastToCorpse()->DepopPlayerCorpse();
if (bury_corpse) {
target->CastToCorpse()->Bury();
}
} else {
c->Message(Chat::White, "Your status is not high enough to depop a player corpse.");
return;
}
} else if (is_depop_all) {
if (!target || !target->IsClient()) {
c->Message(Chat::White, "You must target a player to use this command.");
return;
}
if (c->Admin() >= commandEditPlayerCorpses) {
bool bury_corpse = (
sep->IsNumber(2) ?
(
std::stoi(sep->arg[2]) != 0 ?
true :
false
) :
false
);
c->Message(
Chat::White,
fmt::format(
"Depopping all player corpses for {} ({}).",
target->GetName(),
target->GetID()
).c_str()
);
target->CastToClient()->DepopAllCorpses();
if (bury_corpse) {
target->CastToClient()->BuryPlayerCorpses();
}
} else {
c->Message(Chat::White, "Your status is not high enough to depop all of a player's corpses.");
return;
}
} else if (is_move_all_to_graveyard) {
int moved_count = entity_list.MovePlayerCorpsesToGraveyard(true);
if (c->Admin() >= commandEditPlayerCorpses) {
if (moved_count) {
c->Message(
Chat::White,
fmt::format(
"Moved {} player corpse{} to graveyard in {} ({}).",
moved_count,
moved_count != 1 ? "s" : "",
ZoneLongName(zone->GetZoneID()),
ZoneName(zone->GetZoneID())
).c_str()
);
} else {
c->Message(Chat::White, "There are no player corpses to move to the graveyard.");
}
} else {
c->Message(Chat::White, "Your status is not high enough to move all player corpses to the graveyard.");
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#include "../client.h"
#include "../corpse.h"
void command_corpsefix(Client *c, const Seperator *sep)
{
entity_list.CorpseFix(c);
}
+64
View File
@@ -0,0 +1,64 @@
#include "../client.h"
void command_countitem(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #countitem [Item ID]");
return;
}
Mob* target = c;
if (
c->GetTarget() &&
(
c->GetTarget()->IsClient() ||
c->GetTarget()->IsNPC()
)
) {
target = c->GetTarget();
}
auto item_id = std::stoul(sep->arg[1]);
if (!database.GetItem(item_id)) {
c->Message(
Chat::White,
fmt::format(
"Item ID {} could not be found.",
item_id
).c_str()
);
return;
}
uint16 item_count = 0;
if (target->IsClient()) {
item_count = target->CastToClient()->CountItem(item_id);
} else if (target->IsNPC()) {
item_count = target->CastToNPC()->CountItem(item_id);
}
c->Message(
Chat::White,
fmt::format(
"{} {} {} {}.",
(
c == target ?
"You" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
),
c == target ? "have" : "has",
(
item_count ?
std::to_string(item_count) :
"no"
),
database.CreateItemLink(item_id)
).c_str()
);
}
+17
View File
@@ -0,0 +1,17 @@
#include "../client.h"
#include "../worldserver.h"
extern WorldServer worldserver;
void command_cvs(Client *c, const Seperator *sep)
{
auto pack = new ServerPacket(
ServerOP_ClientVersionSummary,
sizeof(ServerRequestClientVersionSummary_Struct)
);
auto srcvss = (ServerRequestClientVersionSummary_Struct *) pack->pBuffer;
strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name));
worldserver.SendPacket(pack);
safe_delete(pack);
}
+18
View File
@@ -0,0 +1,18 @@
#include "../client.h"
void command_damage(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #damage [Amount]");
return;
}
Mob* target = c;
if (c->GetTarget()) {
target = c->GetTarget();
}
int damage = static_cast<int>(std::min(std::stoll(sep->arg[1]), (long long) 2000000000));
target->Damage(c, damage, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand, false);
}
+92
View File
@@ -0,0 +1,92 @@
#include "../client.h"
#include "../data_bucket.h"
void command_databuckets(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == 0) {
c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)");
return;
}
if (strcasecmp(sep->arg[1], "view") == 0) {
std::string key_filter;
uint8 limit = 50;
for (int i = 2; i < 4; i++) {
if (sep->arg[i][0] == '\0') {
break;
}
if (strcasecmp(sep->arg[i], "limit") == 0) {
limit = (uint8) atoi(sep->arg[i + 1]);
continue;
}
}
if (sep->arg[2]) {
key_filter = str_tolower(sep->arg[2]);
}
std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets";
if (!key_filter.empty()) { query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str()); }
query += StringFormat(" LIMIT %u", limit);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
if (results.RowCount() == 0) {
c->Message(Chat::Yellow, "No data_buckets found");
return;
}
int _ctr = 0;
// put in window for easier readability in case want command line for something else
std::string window_title = "Data Buckets";
std::string window_text =
"<table>"
"<tr>"
"<td>ID</td>"
"<td>Expires</td>"
"<td>Key</td>"
"<td>Value</td>"
"</tr>";
for (auto row = results.begin(); row != results.end(); ++row) {
auto id = static_cast<uint32>(atoi(row[0]));
std::string key = row[1];
std::string value = row[2];
std::string expires = row[3];
window_text.append(
StringFormat(
"<tr>"
"<td>%u</td>"
"<td>%s</td>"
"<td>%s</td>"
"<td>%s</td>"
"</tr>",
id,
expires.c_str(),
key.c_str(),
value.c_str()
));
_ctr++;
std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str());
c->Message(
Chat::White,
"%s : %s",
EQ::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(),
key.c_str(),
" Value: ",
value.c_str());
}
window_text.append("</table>");
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str()
: "No Databuckets found.";
c->Message(Chat::Yellow, response.c_str());
}
else if (strcasecmp(sep->arg[1], "delete") == 0) {
if (DataBucket::DeleteData(sep->argplus[2])) {
c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]);
}
else {
c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]);
}
return;
}
}
+29
View File
@@ -0,0 +1,29 @@
#include "../client.h"
void command_date(Client *c, const Seperator *sep)
{
//yyyy mm dd hh mm local
if (sep->arg[3][0] == 0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) {
c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]");
}
else {
int h = 0, m = 0;
TimeOfDay_Struct eqTime;
zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime);
if (!sep->IsNumber(4)) {
h = eqTime.hour;
}
else {
h = atoi(sep->arg[4]);
}
if (!sep->IsNumber(5)) {
m = eqTime.minute;
}
else {
m = atoi(sep->arg[5]);
}
c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m);
zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m);
}
}
+31
View File
@@ -0,0 +1,31 @@
#include "../client.h"
void command_dbspawn2(Client *c, const Seperator *sep)
{
if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) {
LogInfo("Spawning database spawn");
uint16 cond = 0;
int16 cond_min = 0;
if (sep->IsNumber(4)) {
cond = atoi(sep->arg[4]);
if (sep->IsNumber(5)) {
cond_min = atoi(sep->arg[5]);
}
}
database.CreateSpawn2(
c,
atoi(sep->arg[1]),
zone->GetShortName(),
c->GetPosition(),
atoi(sep->arg[2]),
atoi(sep->arg[3]),
cond,
cond_min
);
}
else {
c->Message(Chat::White, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]");
}
}
+21
View File
@@ -0,0 +1,21 @@
#include "../client.h"
void command_delacct(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == 0) {
c->Message(Chat::White, "Format: #delacct accountname");
}
else {
std::string user;
std::string loginserver;
ParseAccountString(sep->arg[1], user, loginserver);
if (database.DeleteAccount(user.c_str(), loginserver.c_str())) {
c->Message(Chat::White, "The account was deleted.");
}
else {
c->Message(Chat::White, "Unable to delete account.");
}
}
}
+35
View File
@@ -0,0 +1,35 @@
#include "../client.h"
void command_deletegraveyard(Client *c, const Seperator *sep)
{
uint32 zoneid = 0;
uint32 graveyard_id = 0;
if (!sep->arg[1][0]) {
c->Message(Chat::White, "Usage: #deletegraveyard [zonename]");
return;
}
zoneid = ZoneID(sep->arg[1]);
graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0);
if (zoneid > 0 && graveyard_id > 0) {
if (content_db.DeleteGraveyard(zoneid, graveyard_id)) {
c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]);
}
else {
c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]);
}
}
else {
if (zoneid <= 0) {
c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]);
}
else if (graveyard_id <= 0) {
c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]);
}
}
return;
}
+20
View File
@@ -0,0 +1,20 @@
#include "../client.h"
void command_delpetition(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) {
c->Message(Chat::White, "Usage: #delpetition (petition number) Type #listpetition for a list");
return;
}
c->Message(Chat::Red, "Attempting to delete petition number: %i", atoi(sep->argplus[1]));
std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1]));
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return;
}
LogInfo("Delete petition request from [{}], petition number:", c->GetName(), atoi(sep->argplus[1]));
}
+14
View File
@@ -0,0 +1,14 @@
#include "../client.h"
#include "../corpse.h"
void command_depop(Client *c, const Seperator *sep)
{
if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) {
c->Message(Chat::White, "You must have a NPC target for this command. (maybe you meant #depopzone?)");
}
else {
c->Message(Chat::White, "Depoping '%s'.", c->GetTarget()->GetName());
c->GetTarget()->Depop();
}
}
+8
View File
@@ -0,0 +1,8 @@
#include "../client.h"
void command_depopzone(Client *c, const Seperator *sep)
{
zone->Depop();
c->Message(Chat::White, "Zone depoped.");
}
+37
View File
@@ -0,0 +1,37 @@
#include "../client.h"
void command_details(Client *c, const Seperator *sep)
{
Mob *target = c->GetTarget();
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #details [number of drakkin detail]");
}
else if (!target) {
c->Message(Chat::White, "Error: this command requires a target");
}
else {
uint16 Race = target->GetRace();
uint8 Gender = target->GetGender();
uint8 Texture = 0xFF;
uint8 HelmTexture = 0xFF;
uint8 HairColor = target->GetHairColor();
uint8 BeardColor = target->GetBeardColor();
uint8 EyeColor1 = target->GetEyeColor1();
uint8 EyeColor2 = target->GetEyeColor2();
uint8 HairStyle = target->GetHairStyle();
uint8 LuclinFace = target->GetLuclinFace();
uint8 Beard = target->GetBeard();
uint32 DrakkinHeritage = target->GetDrakkinHeritage();
uint32 DrakkinTattoo = target->GetDrakkinTattoo();
uint32 DrakkinDetails = atoi(sep->arg[1]);
target->SendIllusionPacket(
Race, Gender, Texture, HelmTexture, HairColor, BeardColor,
EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF,
DrakkinHeritage, DrakkinTattoo, DrakkinDetails
);
c->Message(Chat::White, "Details = %i", atoi(sep->arg[1]));
}
}
+22
View File
@@ -0,0 +1,22 @@
#include "../client.h"
#include "../data_bucket.h"
void command_devtools(Client *c, const Seperator *sep)
{
std::string dev_tools_key = StringFormat("%i-dev-tools-disabled", c->AccountID());
/**
* Handle window toggle
*/
if (strcasecmp(sep->arg[1], "disable") == 0) {
DataBucket::SetData(dev_tools_key, "true");
c->SetDevToolsEnabled(false);
}
if (strcasecmp(sep->arg[1], "enable") == 0) {
DataBucket::DeleteData(dev_tools_key);
c->SetDevToolsEnabled(true);
}
c->ShowDevToolsMenu();
}
+29
View File
@@ -0,0 +1,29 @@
#include "../client.h"
void command_disablerecipe(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #disablerecipe [Recipe ID]");
return;
}
auto recipe_id = std::stoul(sep->arg[1]);
if (!recipe_id) {
c->Message(Chat::White, "Usage: #disablerecipe [Recipe ID]");
return;
}
c->Message(
Chat::White,
fmt::format(
"Recipe ID {} {} disabled.",
recipe_id,
(
content_db.DisableRecipe(recipe_id) ?
"successfully" :
"failed to be"
)
).c_str()
);
}
+25
View File
@@ -0,0 +1,25 @@
#include "../client.h"
void command_disarmtrap(Client *c, const Seperator *sep)
{
Mob *target = c->GetTarget();
if (!target) {
c->Message(Chat::Red, "You must have a target.");
return;
}
if (target->IsNPC()) {
if (c->HasSkill(EQ::skills::SkillDisarmTraps)) {
if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) {
c->Message(Chat::Red, "%s is too far away.", target->GetCleanName());
return;
}
c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical);
}
else {
c->Message(Chat::Red, "You do not have the disarm trap skill.");
}
}
}
+23
View File
@@ -0,0 +1,23 @@
#include "../client.h"
void command_distance(Client *c, const Seperator *sep)
{
if (c->GetTarget()) {
Mob *target = c->GetTarget();
if (c != target) {
c->Message(
Chat::White,
fmt::format(
"{} ({}) is {:.2f} units from you.",
target->GetCleanName(),
target->GetID(),
Distance(
c->GetPosition(),
target->GetPosition()
)
).c_str()
);
}
}
}
+20
View File
@@ -0,0 +1,20 @@
#include "../client.h"
void command_doanim(Client *c, const Seperator *sep)
{
if (!sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #DoAnim [number]");
}
else if (c->Admin() >= commandDoAnimOthers) {
if (c->GetTarget() == 0) {
c->Message(Chat::White, "Error: You need a target.");
}
else {
c->GetTarget()->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2]));
}
}
else {
c->DoAnim(atoi(sep->arg[1]), atoi(sep->arg[2]));
}
}
+9
View File
@@ -0,0 +1,9 @@
#include "../client.h"
#include "door_manipulation.h"
#include "../doors.h"
void command_door(Client *c, const Seperator *sep)
{
DoorManipulation::CommandHandler(c, sep);
}
+786
View File
@@ -0,0 +1,786 @@
#include "door_manipulation.h"
#include "../doors.h"
#include "../../common/misc_functions.h"
#define MAX_CLIENT_MESSAGE_LENGTH 2000
void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
{
// this should never happen
if (!c) {
return;
}
// args
std::string arg1(sep->arg[1]);
std::string arg2(sep->arg[2]);
std::string arg3(sep->arg[3]);
// table check
std::string table_name = "tool_game_objects";
std::string url = "https://raw.githubusercontent.com/EQEmu/database-tool-sqls/main/tool_game_objects.sql";
if (!database.DoesTableExist(table_name)) {
c->Message(
Chat::White,
fmt::format(
"Table [{}] does not exist. Downloading from [{}] and installing locally",
table_name,
url
).c_str()
);
database.SourceDatabaseTableFromUrl(
table_name,
url
);
}
// option
if (arg1.empty()) {
DoorManipulation::CommandHeader(c);
c->Message(
Chat::White,
"#door create <modelname> | Creates a door from a model. (Example IT78 creates a campfire)"
);
c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state");
c->Message(Chat::White, "#door setincline <incline> | Sets selected door incline");
c->Message(Chat::White, "#door opentype <opentype> | Sets selected door opentype");
c->Message(
Chat::White,
fmt::format(
"#door model <modelname> | Changes door model for selected door or select from [{}] or [{}]",
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "local zone"),
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "global")
).c_str()
);
c->Message(
Chat::White,
"#door showmodelsfromfile <file.eqg|file.s3d> | Shows models from s3d or eqg file. Example tssequip.eqg or wallet01.eqg"
);
c->Message(
Chat::White,
fmt::format(
"{} | Shows available models in the current zone that you are in",
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "#door showmodelszone")
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"{} | Shows available models globally by first listing all global model files",
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "#door showmodelsglobal")
).c_str()
);
c->Message(Chat::White, "#door save | Creates database entry for selected door");
c->Message(
Chat::White,
fmt::format(
"{} - Brings up editing interface for selected door",
EQ::SayLinkEngine::GenerateQuestSaylink("#door edit", false, "#door edit")
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"{} - lists doors in zone",
EQ::SayLinkEngine::GenerateQuestSaylink("#list doors", false, "#list doors")
).c_str()
);
return;
}
// edit menu
if (arg1 == "edit") {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
c->Message(
Chat::White,
fmt::format(
"Door Selected ID [{}] Name [{}] OpenType [{}] Invertstate [{} | {}/{}] ",
c->GetDoorToolEntityId(),
door->GetDoorName(),
door->GetOpenType(),
door->GetInvertState(),
EQ::SayLinkEngine::GenerateQuestSaylink("#door setinvertstate 0", false, "0"),
EQ::SayLinkEngine::GenerateQuestSaylink("#door setinvertstate 1", false, "1")
).c_str()
);
const std::string move_x_action = "move_x";
const std::string move_y_action = "move_y";
const std::string move_z_action = "move_z";
const std::string move_h_action = "move_h";
const std::string set_size_action = "set_size";
std::vector<std::string> move_options = {
move_x_action,
move_y_action,
move_z_action,
move_h_action,
set_size_action
};
std::vector<std::string> move_x_options_positive;
std::vector<std::string> move_x_options_negative;
std::vector<std::string> move_y_options_positive;
std::vector<std::string> move_y_options_negative;
std::vector<std::string> move_z_options_positive;
std::vector<std::string> move_z_options_negative;
std::vector<std::string> move_h_options_positive;
std::vector<std::string> move_h_options_negative;
std::vector<std::string> set_size_options_positive;
std::vector<std::string> set_size_options_negative;
for (const auto &move_option : move_options) {
if (move_option == move_x_action) {
move_x_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} .25", move_option),
false,
".25"
)
);
for (int move_index = 0; move_index <= 15; move_index += 5) {
int value = (move_index == 0 ? 1 : move_index);
move_x_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
for (int move_index = -15; move_index <= 0; move_index += 5) {
int value = (move_index == 0 ? 1 : move_index);
move_x_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
move_x_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} -.25", move_option),
false,
".25"
)
);
}
else if (move_option == move_y_action) {
move_y_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} .25", move_option),
false,
".25"
)
);
for (int move_index = 0; move_index <= 15; move_index += 5) {
int value = (move_index == 0 ? 1 : move_index);
move_y_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
for (int move_index = -15; move_index <= 0; move_index += 5) {
int value = (move_index == 0 ? -1 : move_index);
move_y_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
move_y_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} -.25", move_option),
false,
".25"
)
);
}
else if (move_option == move_z_action) {
move_z_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} .25", move_option),
false,
".25"
)
);
for (int move_index = 0; move_index <= 15; move_index += 5) {
int value = (move_index == 0 ? 1 : move_index);
move_z_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
for (int move_index = -15; move_index <= 0; move_index += 5) {
int value = (move_index == 0 ? -1 : move_index);
move_z_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
move_z_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} -.25", move_option),
false,
".25"
)
);
}
else if (move_option == move_h_action) {
for (int move_index = 0; move_index <= 50; move_index += 5) {
int value = (move_index == 0 ? 1 : move_index);
move_h_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
for (int move_index = -50; move_index <= 0; move_index += 5) {
int value = (move_index == 0 ? -1 : move_index);
move_h_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
}
else if (move_option == set_size_action) {
for (int move_index = 0; move_index <= 100; move_index += 10) {
int value = (move_index == 0 ? 1 : move_index);
set_size_options_positive.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
for (int move_index = -100; move_index <= 0; move_index += 10) {
int value = (move_index == 0 ? -1 : move_index);
set_size_options_negative.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door edit {} {}", move_option, value),
false,
fmt::format("{}", std::abs(value))
)
);
}
}
}
// we're passing a move action here
if (!arg3.empty() && StringIsNumber(arg3)) {
float x_move = 0.0f;
float y_move = 0.0f;
float z_move = 0.0f;
float h_move = 0.0f;
float set_size = 0.0f;
if (arg2 == move_x_action) {
x_move = std::atof(arg3.c_str());
}
if (arg2 == move_y_action) {
y_move = std::atof(arg3.c_str());
}
if (arg2 == move_z_action) {
z_move = std::atof(arg3.c_str());
}
if (arg2 == move_h_action) {
h_move = std::atof(arg3.c_str());
}
if (arg2 == set_size_action) {
set_size = std::atof(arg3.c_str());
}
door->SetLocation(
door->GetX() + x_move,
door->GetY() + y_move,
door->GetZ() + z_move
);
glm::vec4 door_position = door->GetPosition();
door_position.w = door_position.w + h_move;
door->SetPosition(door_position);
door->SetSize(door->GetSize() + set_size);
}
// spawn and move helpers
uint16 helper_mob_x_negative = 0;
uint16 helper_mob_x_positive = 0;
uint16 helper_mob_y_positive = 0;
uint16 helper_mob_y_negative = 0;
for (auto &n: entity_list.GetNPCList()) {
NPC *npc = n.second;
std::string npc_name = npc->GetName();
if (npc_name.find("-X") != std::string::npos) {
helper_mob_x_negative = npc->GetID();
}
if (npc_name.find("-Y") != std::string::npos) {
helper_mob_y_negative = npc->GetID();
}
if (npc_name.find("+X") != std::string::npos) {
helper_mob_x_positive = npc->GetID();
}
if (npc_name.find("+Y") != std::string::npos) {
helper_mob_y_positive = npc->GetID();
}
}
// -X
glm::vec4 door_position = door->GetPosition();
if (helper_mob_x_negative == 0) {
door_position.x = door_position.x - 15;
helper_mob_x_negative = NPC::SpawnNodeNPC("-X", "", door_position)->GetID();
}
else {
auto n = entity_list.GetNPCByID(helper_mob_x_negative);
n->GMMove(door->GetX() - 15, door->GetY(), door->GetZ(), n->GetHeading());
}
// +X
door_position = door->GetPosition();
if (helper_mob_x_positive == 0) {
door_position.x = door_position.x + 15;
helper_mob_x_positive = NPC::SpawnNodeNPC("+X", "", door_position)->GetID();
}
else {
auto n = entity_list.GetNPCByID(helper_mob_x_positive);
n->GMMove(door->GetX() + 15, door->GetY(), door->GetZ(), n->GetHeading());
}
// -Y
door_position = door->GetPosition();
if (helper_mob_y_negative == 0) {
door_position.y = door_position.y - 15;
helper_mob_y_negative = NPC::SpawnNodeNPC("-Y", "", door_position)->GetID();
}
else {
auto n = entity_list.GetNPCByID(helper_mob_y_negative);
n->GMMove(door->GetX(), door->GetY() - 15, door->GetZ(), n->GetHeading());
}
// +Y
door_position = door->GetPosition();
if (helper_mob_y_positive == 0) {
door_position.y = door_position.y + 15;
helper_mob_y_positive = NPC::SpawnNodeNPC("+Y", "", door_position)->GetID();
}
else {
auto n = entity_list.GetNPCByID(helper_mob_y_positive);
n->GMMove(door->GetX(), door->GetY() + 15, door->GetZ(), n->GetHeading());
}
c->Message(
Chat::White,
fmt::format(
"Name [{}] [{}] [{}] [{}]",
door->GetDoorName(),
EQ::SayLinkEngine::GenerateQuestSaylink(
"#door save",
false,
"Save"
),
EQ::SayLinkEngine::GenerateQuestSaylink(
"#door changemodelqueue",
false,
"Change Model"
),
EQ::SayLinkEngine::GenerateQuestSaylink(
"#door setinclineinc",
false,
"Incline"
)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[{}] - [X] + [{}]",
implode(" | ", move_x_options_negative),
implode(" | ", move_x_options_positive)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[{}] - [Y] + [{}]",
implode(" | ", move_y_options_negative),
implode(" | ", move_y_options_positive)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[{}] - [Z] + [{}]",
implode(" | ", move_z_options_negative),
implode(" | ", move_z_options_positive)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[{}] - [H] + [{}]",
implode(" | ", move_h_options_negative),
implode(" | ", move_h_options_positive)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[{}] - [Size] + [{}]",
implode(" | ", set_size_options_negative),
implode(" | ", set_size_options_positive)
).c_str()
);
return;
}
c->Message(Chat::Red, "Door selection invalid...");
}
// create
if (arg1 == "create") {
std::string model = str_toupper(arg2);
uint16 entity_id = entity_list.CreateDoor(
model.c_str(),
c->GetPosition(),
58,
100
);
c->Message(
Chat::White,
fmt::format("Creating door entity_id [{}] with model [{}]", entity_id, model).c_str());
c->SetDoorToolEntityId(entity_id);
}
// set model
if (arg1 == "model") {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
std::string model = str_toupper(arg2);
if (door) {
door->SetDoorName(model.c_str());
}
}
// change model queue
if (arg1 == "changemodelqueue") {
c->Message(
Chat::White,
fmt::format(
"#door model <modelname> | Changes door model for selected door or select from [{}] or [{}]",
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelszone", false, "local zone"),
EQ::SayLinkEngine::GenerateQuestSaylink("#door showmodelsglobal", false, "global")
).c_str()
);
}
// open type
if (arg1 == "opentype" && !arg2.empty() && StringIsNumber(arg2)) {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
door->SetOpenType(std::atoi(arg2.c_str()));
}
}
// incline
if (arg1 == "setincline" && !arg2.empty() && StringIsNumber(arg2)) {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
door->SetIncline(std::atoi(arg2.c_str()));
}
}
// incline
if (arg1 == "setinvertstate" && !arg2.empty() && StringIsNumber(arg2)) {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
door->SetInvertState(std::atoi(arg2.c_str()));
}
}
// save
if (arg1 == "save") {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
door->CreateDatabaseEntry();
c->Message(Chat::White, "Door saved");
}
}
// incline incremental
if (arg1 == "setinclineinc" && !arg2.empty() && StringIsNumber(arg2)) {
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
if (door) {
door->SetIncline(door->GetIncline() + std::atoi(arg2.c_str()));
}
}
if (arg1 == "setinclineinc") {
std::map<float, std::string> incline_values = {
{.01, "Upright"},
{63.75, "45 Degrees",},
{130, "90 Degrees"},
{192.5, "135 Degrees"},
{255, "180 Degrees"},
{321.25, "225 Degrees"},
{385, "270 Degrees"},
{448.75, "315 Degrees"},
{512.5, "360 Degrees"}
};
std::vector<std::string> incline_normal_options;
std::vector<std::string> incline_positive_options;
std::vector<std::string> incline_negative_options;
for (auto incline_value : incline_values) {
incline_normal_options.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format(
"#door setincline {}",
incline_value.first
),
false,
incline_value.second
)
);
}
for (int incline_index = 0; incline_index <= 100; incline_index += 10) {
int incline_value = (incline_index == 0 ? 1 : incline_index);
incline_positive_options.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format(
"#door setinclineinc {}",
incline_value
),
false,
itoa(std::abs(incline_value))
)
);
}
for (int incline_index = -100; incline_index <= 1; incline_index += 10) {
int incline_value = (incline_index == 0 ? -1 : incline_index);
incline_negative_options.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format(
"#door setinclineinc {}",
incline_value
),
false,
itoa(std::abs(incline_value))
)
);
}
c->Message(
Chat::White,
fmt::format(
"[Incline] [{}]",
implode(" | ", incline_normal_options)
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"[Incline Increments] [{}] - | + [{}]",
implode(" | ", incline_negative_options),
implode(" | ", incline_positive_options)
).c_str()
);
}
// show models in zone
if (arg1 == "showmodelsglobal") {
auto game_objects = ToolGameObjectsRepository::GetWhere(
database,
"object_name LIKE '%IT%' AND zoneid = 0 AND object_name NOT LIKE '%OBJ%' GROUP by file_from"
);
if (game_objects.empty()) {
c->Message(Chat::White, "There are no models to display...");
}
c->Message(Chat::White, "------------------------------------------------");
c->Message(Chat::White, "# Models (Global)");
c->Message(Chat::White, "------------------------------------------------");
DisplayModelsFromFileResults(c, game_objects);
}
// show models in zone
if (arg1 == "showmodelszone") {
auto game_objects = ToolGameObjectsRepository::GetWhere(
database,
fmt::format("zoneid = {}", zone->GetZoneID())
);
if (game_objects.empty()) {
c->Message(Chat::White, "There are no models for this zone...");
}
c->Message(Chat::White, "------------------------------------------------");
c->Message(Chat::White, "# Models from zone");
c->Message(Chat::White, "------------------------------------------------");
DisplayObjectResultToClient(c, game_objects);
}
// show models from file name
if (arg1 == "showmodelsfromfile" && !arg2.empty()) {
const std::string &file_name = arg2;
auto game_objects = ToolGameObjectsRepository::GetWhere(
database,
fmt::format("file_from = '{}'", file_name)
);
if (game_objects.empty()) {
c->Message(Chat::White, "There are no models for this zone...");
}
c->Message(Chat::White, "------------------------------------------------");
c->Message(Chat::White, fmt::format("# Models from file name [{}]", file_name).c_str());
c->Message(Chat::White, "------------------------------------------------");
DisplayObjectResultToClient(c, game_objects);
}
}
void DoorManipulation::CommandHeader(Client *c)
{
c->Message(Chat::White, "------------------------------------------------");
c->Message(Chat::White, "# Door Commands");
c->Message(Chat::White, "------------------------------------------------");
}
void DoorManipulation::DisplayObjectResultToClient(
Client *c,
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
)
{
std::vector<std::string> say_links;
for (auto &g: game_objects) {
say_links.emplace_back(
fmt::format(
"[{}] ",
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door model {}", g.object_name),
false,
g.object_name
)
)
);
}
int character_length = 0;
std::vector<std::string> buffered_links;
for (auto &links: say_links) {
buffered_links.emplace_back(links);
character_length += links.length();
// empty buffer
if (character_length > MAX_CLIENT_MESSAGE_LENGTH) {
std::string message_buffer;
for (auto &buffered_link: buffered_links) {
message_buffer += buffered_link;
}
c->Message(Chat::White, message_buffer.c_str());
// reset
character_length = 0;
buffered_links = {};
}
}
if (!buffered_links.empty()) {
c->Message(Chat::White, implode(" ", buffered_links).c_str());
}
}
void DoorManipulation::DisplayModelsFromFileResults(
Client *c,
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
)
{
std::vector<std::string> say_links;
for (auto &g: game_objects) {
say_links.emplace_back(
fmt::format(
"[{}] ",
EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#door showmodelsfromfile {}", g.file_from),
false,
g.file_from
)
)
);
}
int character_length = 0;
std::vector<std::string> buffered_links;
for (auto &links: say_links) {
buffered_links.emplace_back(links);
character_length += links.length();
// empty buffer
if (character_length > MAX_CLIENT_MESSAGE_LENGTH) {
std::string message_buffer;
for (auto &buffered_link: buffered_links) {
message_buffer += buffered_link;
}
c->Message(Chat::White, message_buffer.c_str());
// reset
character_length = 0;
buffered_links = {};
}
}
if (!buffered_links.empty()) {
c->Message(Chat::White, implode(" ", buffered_links).c_str());
}
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef EQEMU_DOOR_MANIPULATION_H
#define EQEMU_DOOR_MANIPULATION_H
#include "../client.h"
#include "../../common/repositories/tool_game_objects_repository.h"
class DoorManipulation {
public:
static void CommandHandler(Client *c, const Seperator *sep);
static void CommandHeader(Client *c);
static void DisplayObjectResultToClient(
Client *c,
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
);
static void DisplayModelsFromFileResults(
Client *c,
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
);
};
#endif //EQEMU_DOOR_MANIPULATION_H
+87
View File
@@ -0,0 +1,87 @@
#include "../client.h"
void command_dye(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (arguments == 0) {
c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]");
return;
}
uint8 slot = 0;
uint8 red = 255;
uint8 green = 255;
uint8 blue = 255;
uint8 use_tint = 255;
std::vector<std::string> dye_slots = {
"Helmet",
"Chest",
"Arms",
"Wrist",
"Hands",
"Legs",
"Feet"
};
if (arguments == 1 && !strcasecmp(sep->arg[1], "help")) {
int slot_id = 0;
std::vector<std::string> slot_messages;
c->Message(Chat::White, "Command Syntax: #dye help | #dye [slot] [red] [green] [blue] [use_tint]");
c->Message(Chat::White, "Red, Green, and Blue go from 0 to 255.");
for (const auto &slot : dye_slots) {
slot_messages.push_back(fmt::format("({}) {}", slot_id, slot));
slot_id++;
}
c->Message(
Chat::White,
fmt::format(
"{} {}",
"Slots are as follows:",
implode(", ", slot_messages)
).c_str()
);
return;
}
if (arguments >= 1 && sep->IsNumber(1)) {
slot = atoi(sep->arg[1]);
}
if (arguments >= 2 && sep->IsNumber(2)) {
red = atoi(sep->arg[2]);
}
if (arguments >= 3 && sep->IsNumber(3)) {
green = atoi(sep->arg[3]);
}
if (arguments >= 4 && sep->IsNumber(4)) {
blue = atoi(sep->arg[4]);
}
if (arguments >= 5 && sep->IsNumber(5)) {
use_tint = atoi(sep->arg[5]);
}
if (RuleB(Command, DyeCommandRequiresDyes)) {
uint32 dye_item_id = 32557;
if (c->CountItem(dye_item_id) >= 1) {
c->RemoveItem(dye_item_id);
}
else {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
const EQ::ItemData *dye_item = database.GetItem(dye_item_id);
linker.SetItemData(dye_item);
c->Message(Chat::White, fmt::format("This command requires a {} to use.", linker.GenerateLink()).c_str());
return;
}
}
c->DyeArmorBySlot(slot, red, green, blue, use_tint);
}
+244
View File
@@ -0,0 +1,244 @@
#include "../client.h"
#include "../expedition.h"
void command_dz(Client *c, const Seperator *sep)
{
if (!c || !zone) {
return;
}
if (strcasecmp(sep->arg[1], "cache") == 0) {
if (strcasecmp(sep->arg[2], "reload") == 0) {
DynamicZone::CacheAllFromDatabase();
Expedition::CacheAllFromDatabase();
c->Message(
Chat::White, fmt::format(
"Reloaded [{}] dynamic zone(s) and [{}] expedition(s) from database",
zone->dynamic_zone_cache.size(), zone->expedition_cache.size()
).c_str());
}
}
else if (strcasecmp(sep->arg[1], "expedition") == 0) {
if (strcasecmp(sep->arg[2], "list") == 0) {
std::vector<Expedition *> expeditions;
for (const auto &expedition : zone->expedition_cache) {
expeditions.emplace_back(expedition.second.get());
}
std::sort(
expeditions.begin(), expeditions.end(),
[](const Expedition *lhs, const Expedition *rhs) {
return lhs->GetID() < rhs->GetID();
}
);
c->Message(Chat::White, fmt::format("Total Active Expeditions: [{}]", expeditions.size()).c_str());
for (const auto &expedition : expeditions) {
auto dz = expedition->GetDynamicZone();
if (!dz) {
LogExpeditions("Expedition [{}] has an invalid dz [{}] in cache",
expedition->GetID(),
expedition->GetDynamicZoneID());
continue;
}
auto leader_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format(
"#goto {}", expedition->GetLeaderName()), false, expedition->GetLeaderName());
auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format(
"#zoneinstance {}", dz->GetInstanceID()), false, "zone"
);
auto seconds = dz->GetSecondsRemaining();
c->Message(
Chat::White, fmt::format(
"expedition id: [{}] dz id: [{}] name: [{}] leader: [{}] {}: [{}]:[{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
expedition->GetID(),
expedition->GetDynamicZoneID(),
expedition->GetName(),
leader_saylink,
zone_saylink,
ZoneName(dz->GetZoneID()),
dz->GetZoneID(),
dz->GetInstanceID(),
dz->GetZoneVersion(),
dz->GetMemberCount(),
seconds / 3600, // hours
(seconds / 60) % 60, // minutes
seconds % 60 // seconds
).c_str());
}
}
else if (strcasecmp(sep->arg[2], "reload") == 0) {
Expedition::CacheAllFromDatabase();
c->Message(
Chat::White, fmt::format(
"Reloaded [{}] expeditions to cache from database.", zone->expedition_cache.size()
).c_str());
}
else if (strcasecmp(sep->arg[2], "destroy") == 0 && sep->IsNumber(3)) {
auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition) {
c->Message(
Chat::White, fmt::format(
"Destroying expedition [{}] ({})",
expedition_id, expedition->GetName()).c_str());
expedition->GetDynamicZone()->RemoveAllMembers();
}
else {
c->Message(Chat::Red, fmt::format("Failed to destroy expedition [{}]", sep->arg[3]).c_str());
}
}
else if (strcasecmp(sep->arg[2], "unlock") == 0 && sep->IsNumber(3)) {
auto expedition_id = std::strtoul(sep->arg[3], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition) {
c->Message(Chat::White, fmt::format("Unlocking expedition [{}]", expedition_id).c_str());
expedition->SetLocked(false, ExpeditionLockMessage::None, true);
}
else {
c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", sep->arg[3]).c_str());
}
}
}
else if (strcasecmp(sep->arg[1], "list") == 0) {
c->Message(
Chat::White,
fmt::format("Total Dynamic Zones (cache): [{}]", zone->dynamic_zone_cache.size()).c_str());
std::vector<DynamicZone *> dynamic_zones;
for (const auto &dz : zone->dynamic_zone_cache) {
dynamic_zones.emplace_back(dz.second.get());
}
std::sort(
dynamic_zones.begin(), dynamic_zones.end(),
[](const DynamicZone *lhs, const DynamicZone *rhs) {
return lhs->GetID() < rhs->GetID();
}
);
for (const auto &dz : dynamic_zones) {
auto seconds = dz->GetSecondsRemaining();
auto zone_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#zoneinstance {}", dz->GetInstanceID()), false, "zone"
);
std::string aligned_type = fmt::format(
"[{}]",
DynamicZone::GetDynamicZoneTypeName(static_cast<DynamicZoneType>(dz->GetType())));
c->Message(
Chat::White, fmt::format(
"id: [{}] type: {:>10} {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
dz->GetID(),
aligned_type,
zone_saylink,
dz->GetZoneID(),
dz->GetInstanceID(),
dz->GetZoneVersion(),
dz->GetMemberCount(),
seconds / 3600, // hours
(seconds / 60) % 60, // minutes
seconds % 60 // seconds
).c_str());
}
}
else if (strcasecmp(sep->arg[1], "listdb") == 0) {
auto dz_list = DynamicZonesRepository::AllDzInstancePlayerCounts(database);
c->Message(Chat::White, fmt::format("Total Dynamic Zones (database): [{}]", dz_list.size()).c_str());
auto now = std::chrono::system_clock::now();
for (const auto &dz : dz_list) {
auto expire_time = std::chrono::system_clock::from_time_t(dz.start_time + dz.duration);
auto remaining = std::chrono::duration_cast<std::chrono::seconds>(expire_time - now);
auto seconds = std::max(0, static_cast<int>(remaining.count()));
bool is_expired = now > expire_time;
if (!is_expired || strcasecmp(sep->arg[2], "all") == 0) {
auto zone_saylink = is_expired ? "zone" : EQ::SayLinkEngine::GenerateQuestSaylink(
fmt::format("#zoneinstance {}", dz.instance), false, "zone"
);
c->Message(
Chat::White, fmt::format(
"id: [{}] type: [{}] {}: [{}]:[{}]:[{}] members: [{}] remaining: [{:02}:{:02}:{:02}]",
dz.id,
DynamicZone::GetDynamicZoneTypeName(static_cast<DynamicZoneType>(dz.type)),
zone_saylink,
dz.zone,
dz.instance,
dz.version,
dz.member_count,
seconds / 3600, // hours
(seconds / 60) % 60, // minutes
seconds % 60 // seconds
).c_str());
}
}
}
else if (strcasecmp(sep->arg[1], "lockouts") == 0) {
if (strcasecmp(sep->arg[2], "remove") == 0 && sep->arg[3][0] != '\0') {
if (sep->arg[5][0] == '\0') {
c->Message(
Chat::White, fmt::format(
"Removing [{}] lockouts on [{}].", sep->arg[4][0] ? sep->arg[4] : "all", sep->arg[3]
).c_str());
}
else {
c->Message(
Chat::White, fmt::format(
"Removing [{}]:[{}] lockout on [{}].", sep->arg[4], sep->arg[5], sep->arg[3]
).c_str());
}
Expedition::RemoveLockoutsByCharacterName(sep->arg[3], sep->arg[4], sep->arg[5]);
}
}
else if (strcasecmp(sep->arg[1], "makeleader") == 0 && sep->IsNumber(2) && sep->arg[3][0] != '\0') {
auto expedition_id = std::strtoul(sep->arg[2], nullptr, 10);
auto expedition = Expedition::FindCachedExpeditionByID(expedition_id);
if (expedition) {
auto char_name = FormatName(sep->arg[3]);
c->Message(
Chat::White,
fmt::format("Setting expedition [{}] leader to [{}]", expedition_id, char_name).c_str());
expedition->SendWorldMakeLeaderRequest(c->CharacterID(), char_name);
}
else {
c->Message(Chat::Red, fmt::format("Failed to find expedition [{}]", expedition_id).c_str());
}
}
else {
c->Message(Chat::White, "#dz usage:");
c->Message(
Chat::White,
"#dz cache reload - reload the current zone cache from db (also reloads expedition cache dependency)"
);
c->Message(Chat::White, "#dz expedition list - list expeditions in current zone cache");
c->Message(Chat::White, "#dz expedition reload - reload expedition zone cache from database");
c->Message(
Chat::White,
"#dz expedition destroy <expedition_id> - destroy expedition globally (must be in cache)"
);
c->Message(Chat::White, "#dz expedition unlock <expedition_id> - unlock expedition");
c->Message(Chat::White, "#dz list - list all dynamic zone instances from current zone cache");
c->Message(
Chat::White,
"#dz listdb [all] - list dynamic zone instances from database -- 'all' includes expired"
);
c->Message(Chat::White, "#dz lockouts remove <char_name> - delete all of character's expedition lockouts");
c->Message(
Chat::White,
"#dz lockouts remove <char_name> \"<expedition_name>\" - delete lockouts by expedition"
);
c->Message(
Chat::White,
"#dz lockouts remove <char_name> \"<expedition_name>\" \"<event_name>\" - delete lockout by expedition event"
);
c->Message(Chat::White, "#dz makeleader <expedition_id> <character_name> - set new expedition leader");
}
}
+13
View File
@@ -0,0 +1,13 @@
#include "../client.h"
#include "../expedition.h"
void command_dzkickplayers(Client *c, const Seperator *sep)
{
if (c) {
auto expedition = c->GetExpedition();
if (expedition) {
expedition->DzKickPlayers(c);
}
}
}
+140
View File
@@ -0,0 +1,140 @@
#include "../client.h"
void command_editmassrespawn(Client *c, const Seperator *sep)
{
if (strcasecmp(sep->arg[1], "usage") == 0) {
c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)");
return;
}
std::string search_npc_type;
if (sep->arg[1]) {
search_npc_type = sep->arg[1];
}
int change_respawn_seconds = 0;
if (sep->arg[2] && sep->IsNumber(2)) {
change_respawn_seconds = atoi(sep->arg[2]);
}
bool change_apply = false;
if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) {
change_apply = true;
}
std::string search_encapsulator = "%";
if (search_npc_type[0] == '=') {
search_npc_type = search_npc_type.substr(1);
search_encapsulator = "";
}
std::string query = fmt::format(
SQL(
SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime
FROM spawn2
INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID
INNER JOIN npc_types ON spawnentry.npcID = npc_types.id
WHERE spawn2.zone LIKE '{}'
AND spawn2.version = '{}'
AND npc_types.name LIKE '{}{}{}'
ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id
),
zone->GetShortName(),
zone->GetInstanceVersion(),
search_encapsulator,
search_npc_type,
search_encapsulator
);
std::string status = "(Searching)";
if (change_apply) {
status = "(Applying)";
}
int results_count = 0;
auto results = content_db.QueryDatabase(query);
if (results.Success() && results.RowCount()) {
results_count = results.RowCount();
for (auto row : results) {
c->Message(
Chat::Yellow,
fmt::format(
"NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}",
row[0],
row[1],
row[2],
row[3],
row[4],
change_respawn_seconds,
status
).c_str()
);
}
c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count);
if (change_respawn_seconds > 0) {
if (change_apply) {
results = content_db.QueryDatabase(
fmt::format(
SQL(
UPDATE spawn2
SET respawntime = '{}'
WHERE id IN(
SELECT spawn2.id
FROM spawn2
INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID
INNER JOIN npc_types ON spawnentry.npcID = npc_types.id
WHERE spawn2.zone LIKE '{}'
AND spawn2.version = '{}'
AND npc_types.name LIKE '{}{}{}'
)
),
change_respawn_seconds,
zone->GetShortName(),
zone->GetInstanceVersion(),
search_encapsulator,
search_npc_type,
search_encapsulator
)
);
if (results.Success()) {
c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count);
zone->Repop();
}
else {
c->Message(Chat::Yellow, "Found (0) NPC's that match this search...");
}
}
else {
std::string saylink = fmt::format(
"#editmassrespawn {}{} {} apply",
(search_encapsulator.empty() ? "=" : ""),
search_npc_type,
change_respawn_seconds
);
c->Message(
Chat::Yellow, "To apply these changes, click <%s> or type [%s]",
EQ::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(),
saylink.c_str()
);
}
}
}
else {
c->Message(Chat::Yellow, "Found (0) NPC's that match this search...");
}
}
+45
View File
@@ -0,0 +1,45 @@
#include "../client.h"
#include "../worldserver.h"
extern WorldServer worldserver;
void command_emote(Client *c, const Seperator *sep)
{
if (sep->arg[3][0] == 0) {
c->Message(Chat::White, "Usage: #emote [name | world | zone] type# message");
}
else {
if (strcasecmp(sep->arg[1], "zone") == 0) {
char *newmessage = 0;
if (strstr(sep->arg[3], "^") == 0) {
entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]);
}
else {
for (newmessage = strtok((char *) sep->arg[3], "^");
newmessage != nullptr;
newmessage = strtok(nullptr, "^"))
entity_list.Message(0, atoi(sep->arg[2]), newmessage);
}
}
else if (!worldserver.Connected()) {
c->Message(Chat::White, "Error: World server disconnected");
}
else if (!strcasecmp(sep->arg[1], "world")) {
worldserver.SendEmoteMessage(
0,
0,
atoi(sep->arg[2]),
sep->argplus[3]
);
}
else {
worldserver.SendEmoteMessage(
sep->arg[1],
0,
atoi(sep->arg[2]),
sep->argplus[3]
);
}
}
}
+78
View File
@@ -0,0 +1,78 @@
#include "../client.h"
void command_emotesearch(Client *c, const Seperator *sep)
{
if (sep->arg[1][0] == 0) {
c->Message(Chat::White, "Usage: #emotesearch [search string or emoteid]");
}
else {
const char *search_criteria = sep->argplus[1];
int count = 0;
if (Seperator::IsNumber(search_criteria)) {
uint16 emoteid = atoi(search_criteria);
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
NPC_Emote_Struct *nes = iterator.GetData();
if (emoteid == nes->emoteid) {
c->Message(
Chat::White,
"EmoteID: %i Event: %i Type: %i Text: %s",
nes->emoteid,
nes->event_,
nes->type,
nes->text
);
count++;
}
iterator.Advance();
}
if (count == 0) {
c->Message(Chat::White, "No emotes found.");
}
else {
c->Message(Chat::White, "%i emote(s) found", count);
}
}
else {
char sText[64];
char sCriteria[515];
strn0cpy(sCriteria, search_criteria, sizeof(sCriteria));
strupr(sCriteria);
char *pdest;
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
NPC_Emote_Struct *nes = iterator.GetData();
strn0cpy(sText, nes->text, sizeof(sText));
strupr(sText);
pdest = strstr(sText, sCriteria);
if (pdest != nullptr) {
c->Message(
Chat::White,
"EmoteID: %i Event: %i Type: %i Text: %s",
nes->emoteid,
nes->event_,
nes->type,
nes->text
);
count++;
}
if (count == 50) {
break;
}
iterator.Advance();
}
if (count == 50) {
c->Message(Chat::White, "50 emotes shown...too many results.");
}
else {
c->Message(Chat::White, "%i emote(s) found", count);
}
}
}
}
+39
View File
@@ -0,0 +1,39 @@
#include "../client.h"
void command_emoteview(Client *c, const Seperator *sep)
{
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target a NPC to view their emotes.");
return;
}
if (c->GetTarget() && c->GetTarget()->IsNPC()) {
int count = 0;
int emoteid = c->GetTarget()->CastToNPC()->GetEmoteID();
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
NPC_Emote_Struct *nes = iterator.GetData();
if (emoteid == nes->emoteid) {
c->Message(
Chat::White,
"EmoteID: %i Event: %i Type: %i Text: %s",
nes->emoteid,
nes->event_,
nes->type,
nes->text
);
count++;
}
iterator.Advance();
}
if (count == 0) {
c->Message(Chat::White, "No emotes found.");
}
else {
c->Message(Chat::White, "%i emote(s) found", count);
}
}
}
+29
View File
@@ -0,0 +1,29 @@
#include "../client.h"
void command_enablerecipe(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #enablerecipe [Recipe ID]");
return;
}
auto recipe_id = std::stoul(sep->arg[1]);
if (!recipe_id) {
c->Message(Chat::White, "Usage: #enablerecipe [Recipe ID]");
return;
}
c->Message(
Chat::White,
fmt::format(
"Recipe ID {} {} enabled.",
recipe_id,
(
content_db.EnableRecipe(recipe_id) ?
"successfully" :
"failed to be"
)
).c_str()
);
}

Some files were not shown because too many files have changed in this diff Show More