From 81e91d795662008acb69ec2b9126f553e0aaf8c5 Mon Sep 17 00:00:00 2001 From: Xackery Xtal Date: Sat, 29 Feb 2020 21:34:38 -0800 Subject: [PATCH 1/4] Improved vscode devcontainer setup --- .devcontainer/Dockerfile | 44 -------------- .devcontainer/devcontainer.json | 29 ++++----- .gitignore | 2 + .vscode/tasks.json | 101 +++++++++++++++++++++++++++++--- 4 files changed, 107 insertions(+), 69 deletions(-) delete mode 100644 .devcontainer/Dockerfile diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index ee959f3d1..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -# https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git/.devcontainer/Dockerfile -FROM ubuntu:18.04 - - ENV DEBIAN_FRONTEND=noninteractive - RUN apt-get update \ - && apt-get -y install --no-install-recommends build-essential \ - gcc-5 g++-5 libtool cmake curl debconf-utils \ - git git-core libio-stringy-perl liblua5.1 \ - liblua5.1-dev libluabind-dev libmysql++ \ - libperl-dev libperl5i-perl libsodium-dev \ - libsodium23 libmysqlclient-dev lua5.1 \ - minizip make mariadb-client \ -# optional, mariadb server - mariadb-server \ -# optional, debugging tools - gdb valgrind \ -# - nano open-vm-tools unzip uuid-dev \ - zlibc wget \ -# # -# # Clean up - && apt-get autoremove -y \ - && apt-get clean -y \ - && rm -rf /var/lib/apt/lists/* -ENV DEBIAN_FRONTEND=dialog -EXPOSE 3306 -EXPOSE 5558 -EXPOSE 5559 -EXPOSE 7000 -EXPOSE 7001 -EXPOSE 7002 -EXPOSE 7003 -EXPOSE 7004 -EXPOSE 7005 -EXPOSE 7778 -EXPOSE 9000 -EXPOSE 9001 -EXPOSE 9080 -EXPOSE 9081 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1723407c3..589bb2ed4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,28 +1,21 @@ // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: // https://github.com/microsoft/vscode-dev-containers/tree/v0.101.1/containers/ubuntu-18.04-git { - "name": "Ubuntu 18.04 EQEMU", - "dockerFile": "Dockerfile", + "name": "Ubuntu 18.04 EQEMU", + // Moved from dockerfile to image so it builds faster + "image": "eqemu/devcontainer:0.0.2", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": "/bin/bash" }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], + // Add the IDs of extensions you want installed when the container is created. - "extensions": [], - "appPort": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], - // "forwardPorts": [3306, 5558, 5559, 7000, 7001, 7002, 7003, 7004, 7005, 7778, 9000, 9001, 9080, 9081], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", - - // Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker. - // "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], - - // Uncomment when using a ptrace-based debugger like C++, Go, and Rust - // "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], - - // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "eqemu" -} \ No newline at end of file + "extensions": ["ms-vscode.cpptools", "ms-azuretools.vscode-docker"], + "mounts": ["source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"], + "remoteEnv": { + "HOST_PROJECT_PATH": "${localWorkspaceFolder}" + } +} diff --git a/.gitignore b/.gitignore index 89dd521c5..287c07412 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ *.out *.app +.bash_history + # CMake CMakeCache.txt CMakeFiles diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 429b59492..1c6e5c871 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -15,10 +15,22 @@ "$gcc" ] }, + { + "label": "make clean", + "type": "shell", + "command": "cd build && make clean", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, { "label": "cmake", "type": "shell", - "command": "mkdir -p build && cd build && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..", + "command": "mkdir -p build && cd build && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..", "group": { "kind": "build", "isDefault": true @@ -38,9 +50,66 @@ } }, { - "label": "zone", + "label": "download maps", "type": "shell", - "command": "cd build/bin && ./zone", + "command": "mkdir -p build/bin && cd build/bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "download quests", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "download eqemu_config", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "rebuild database (mariadb must be started)", + "type": "shell", + "command": "mkdir -p build/bin && cd build/bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl linux_login_server_setup'", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "zone 7000", + "type": "shell", + "command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "zone 7001", + "type": "shell", + "command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001", "group": { "kind": "test", "isDefault": true @@ -49,20 +118,38 @@ { "label": "loginserver", "type": "shell", - "command": "cd build/bin && ./loginserver", + "command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver", "group": { "kind": "test", "isDefault": true } }, { - "label": "world", + "label": "shared_memory, world", "type": "shell", - "command": "cd build/bin && ./world", + "command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "queryserv", + "type": "shell", + "command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv", + "group": { + "kind": "test", + "isDefault": true + } + }, + { + "label": "mariadb", + "type": "shell", + "command": "docker stop mariadb | true && cd build/bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest", "group": { "kind": "test", "isDefault": true } } ] -} \ No newline at end of file +} From 7f6414d685805e8c6ce8332d5a3229f4a2df8871 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 2 Mar 2020 12:09:55 -0500 Subject: [PATCH 2/4] Some bot-related changes (Master Wu's Technique, Tiger Claw timer) --- zone/bot.cpp | 183 ++++++++++++++++++++++++++++++++------- zone/bot_command.cpp | 44 ++++++++-- zone/bot_database.cpp | 1 + zone/client.cpp | 1 + zone/client.h | 1 + zone/npc.cpp | 13 ++- zone/npc.h | 1 + zone/special_attacks.cpp | 30 ++++--- 8 files changed, 221 insertions(+), 53 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5579b4bc5..6d14ce740 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6421,32 +6421,52 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); + bool ma_time = monkattack_timer.Check(false); bool ka_time = knightattack_timer.Check(false); - if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) + + if (taunt_time) { + + // Bots without this skill shouldn't be 'checking' on this timer..let's just disable it and avoid the extra IsAttackAllowed() checks + // Note: this is done here instead of NPC::ctor() because taunt skill can be acquired during level ups (the timer is re-enabled in CalcBotStats()) + if (!GetSkill(EQEmu::skills::SkillTaunt)) { + + taunt_timer.Disable(); + return; + } + + if (!IsAttackAllowed(target)) { + return; + } + } + + if ((ca_time || ma_time || ka_time) && !IsAttackAllowed(target)) { return; + } if(ka_time){ - int knightreuse = 1000; + switch(GetClass()){ - case SHADOWKNIGHT: - case SHADOWKNIGHTGM: { + case SHADOWKNIGHT: { CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightreuse = (HarmTouchReuseTime * 1000); + knightattack_timer.Start(HarmTouchReuseTime * 1000); + break; } - case PALADIN: - case PALADINGM: { + case PALADIN: { if(GetHPRatio() < 20) { CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightreuse = (LayOnHandsReuseTime * 1000); + knightattack_timer.Start(LayOnHandsReuseTime * 1000); + } + else { + knightattack_timer.Start(2000); } - else - knightreuse = 2000; break; } + default: { + break; + } } - knightattack_timer.Start(knightreuse); } if(taunting && target && target->IsNPC() && taunt_time) { @@ -6457,8 +6477,66 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if(!ca_time) + if (ma_time) { + switch (GetClass()) { + case MONK: { + int reuse = (MonkSpecialAttack(target, EQEmu::skills::SkillTigerClaw) - 1); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQEmu::skills::SkillTigerClaw)); + --extra; + } + } + + float HasteModifier = (GetHaste() * 0.01f); + monkattack_timer.Start((reuse * 1000) / HasteModifier); + + break; + } + default: + break;; + } + } + + if (!ca_time) { return; + } float HasteModifier = (GetHaste() * 0.01f); uint16 skill_to_use = -1; @@ -6493,18 +6571,22 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } break; case MONK: - if(GetLevel() >= 30) + if (GetLevel() >= 30) { skill_to_use = EQEmu::skills::SkillFlyingKick; - else if(GetLevel() >= 25) + } + else if (GetLevel() >= 25) { skill_to_use = EQEmu::skills::SkillDragonPunch; - else if(GetLevel() >= 20) + } + else if (GetLevel() >= 20) { skill_to_use = EQEmu::skills::SkillEagleStrike; - else if(GetLevel() >= 10) - skill_to_use = EQEmu::skills::SkillTigerClaw; - else if(GetLevel() >= 5) + } + else if (GetLevel() >= 5) { skill_to_use = EQEmu::skills::SkillRoundKick; - else + } + else { skill_to_use = EQEmu::skills::SkillKick; + } + break; case ROGUE: skill_to_use = EQEmu::skills::SkillBackstab; @@ -6555,19 +6637,54 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if (skill_to_use == EQEmu::skills::SkillFlyingKick || skill_to_use == EQEmu::skills::SkillDragonPunch || skill_to_use == EQEmu::skills::SkillEagleStrike || skill_to_use == EQEmu::skills::SkillTigerClaw || skill_to_use == EQEmu::skills::SkillRoundKick) { + if ( + skill_to_use == EQEmu::skills::SkillFlyingKick || + skill_to_use == EQEmu::skills::SkillDragonPunch || + skill_to_use == EQEmu::skills::SkillEagleStrike || + skill_to_use == EQEmu::skills::SkillRoundKick + ) { reuse = (MonkSpecialAttack(target, skill_to_use) - 1); - MonkSpecialAttack(target, skill_to_use); - uint32 bDoubleSpecialAttack = (itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack); - if(bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > zone->random.Int(0, 100))) { - int MonkSPA[5] = { EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, EQEmu::skills::SkillRoundKick }; - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += (TripleChance * (100 - bDoubleSpecialAttack) / 100); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if(TripleChance > zone->random.Int(0,100)) - MonkSpecialAttack(target, MonkSPA[zone->random.Int(0, 4)]); + if (wuchance) { + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; + } + + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } + + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : skill_to_use)); + --extra; + } } reuse *= 1000; @@ -8966,6 +9083,12 @@ void Bot::CalcBotStats(bool showtext) { skills[sindex] = database.GetSkillCap(GetClass(), (EQEmu::skills::SkillType)sindex, GetLevel()); } + taunt_timer.Start(1000); + + if (GetClass() == MONK && GetLevel() >= 10) { + monkattack_timer.Start(1000); + } + LoadAAs(); GenerateSpecialAttacks(); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index dc9d40aab..9c202fed8 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3928,6 +3928,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "null" "(toggles)" "" + "" + "monkwumessage" + "enable | disable" + "displays monk wu trigger messages" + "" + "" + "" + "null" + "(toggles)" + "" "" "current" "" @@ -4103,6 +4113,22 @@ void bot_command_owner_option(Client *c, const Seperator *sep) c->Message(m_action, "Bot 'buff counter' is now %s.", (c->GetBotOption(Client::booBuffCounter) == true ? "enabled" : "disabled")); } + else if (!owner_option.compare("monkwumessage")) { + + if (!argument.compare("enable")) { + c->SetBotOption(Client::booMonkWuMessage, true); + } + else if (!argument.compare("disable")) { + c->SetBotOption(Client::booMonkWuMessage, false); + } + else { + c->SetBotOption(Client::booMonkWuMessage, !c->GetBotOption(Client::booMonkWuMessage)); + } + + database.botdb.SaveOwnerOption(c->CharacterID(), Client::booMonkWuMessage, c->GetBotOption(Client::booMonkWuMessage)); + + c->Message(m_action, "Bot 'monk wu message' is now %s.", (c->GetBotOption(Client::booMonkWuMessage) == true ? "enabled" : "disabled")); + } else if (!owner_option.compare("current")) { std::string window_title = "Current Bot Owner Options Settings"; @@ -4112,13 +4138,14 @@ void bot_command_owner_option(Client *c, const Seperator *sep) "Option
------" "Argument
-------" "" - "" "deathmarquee" "{}" "" - "" "statsupdate" "{}" "" - "" "spawnmessage" "{}" "" - "" "spawnmessage" "{}" "" - "" "altcombat" "{}" "" - "" "autodefend" "{}" "" - "" "buffcounter" "{}" "" + "" "deathmarquee" "{}" "" + "" "statsupdate" "{}" "" + "" "spawnmessage" "{}" "" + "" "spawnmessage" "{}" "" + "" "altcombat" "{}" "" + "" "autodefend" "{}" "" + "" "buffcounter" "{}" "" + "" "monkwumessage" "{}" "" "", (c->GetBotOption(Client::booDeathMarquee) ? "enabled" : "disabled"), (c->GetBotOption(Client::booStatsUpdate) ? "enabled" : "disabled"), @@ -4126,7 +4153,8 @@ void bot_command_owner_option(Client *c, const Seperator *sep) (c->GetBotOption(Client::booSpawnMessageClassSpecific) ? "class" : "default"), (RuleB(Bots, AllowOwnerOptionAltCombat) ? (c->GetBotOption(Client::booAltCombat) ? "enabled" : "disabled") : "restricted"), (RuleB(Bots, AllowOwnerOptionAutoDefend) ? (c->GetBotOption(Client::booAutoDefend) ? "enabled" : "disabled") : "restricted"), - (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled") + (c->GetBotOption(Client::booBuffCounter) ? "enabled" : "disabled"), + (c->GetBotOption(Client::booMonkWuMessage) ? "enabled" : "disabled") ); c->SendPopupToClient(window_title.c_str(), window_text.c_str()); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 2f6f9c1c6..28b5288e0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2257,6 +2257,7 @@ bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool case Client::booAltCombat: case Client::booAutoDefend: case Client::booBuffCounter: + case Client::booMonkWuMessage: { query = fmt::format( "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')", diff --git a/zone/client.cpp b/zone/client.cpp index 2398db500..6bd9941f8 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -358,6 +358,7 @@ Client::Client(EQStreamInterface* ieqs) bot_owner_options[booAltCombat] = RuleB(Bots, AllowOwnerOptionAltCombat); bot_owner_options[booAutoDefend] = RuleB(Bots, AllowOwnerOptionAutoDefend); bot_owner_options[booBuffCounter] = false; + bot_owner_options[booMonkWuMessage] = false; SetBotPulling(false); SetBotPrecombat(false); diff --git a/zone/client.h b/zone/client.h index 52af842be..58b0d9af8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1646,6 +1646,7 @@ public: booAltCombat, booAutoDefend, booBuffCounter, + booMonkWuMessage, _booCount }; diff --git a/zone/npc.cpp b/zone/npc.cpp index 17e3c5957..0b22bab3a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -118,6 +118,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi attacked_timer(CombatEventTimer_expire), swarm_timer(100), classattack_timer(1000), + monkattack_timer(1000), knightattack_timer(1000), assist_timer(AIassistcheck_delay), qglobal_purge_timer(30000), @@ -307,7 +308,15 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi // some overrides -- really we need to be able to set skills for mobs in the DB // There are some known low level SHM/BST pets that do not follow this, which supports // the theory of needing to be able to set skills for each mob separately - if (!IsBot()) { + if (IsBot()) { + if (GetClass() != PALADIN && GetClass() != SHADOWKNIGHT) { + knightattack_timer.Disable(); + } + else if (GetClass() != MONK || GetLevel() < 10) { + monkattack_timer.Disable(); + } + } + else { if (moblevel > 50) { skills[EQEmu::skills::SkillDoubleAttack] = 250; skills[EQEmu::skills::SkillDualWield] = 250; @@ -3233,4 +3242,4 @@ void NPC::RecalculateSkills() skills[EQEmu::skills::SkillDoubleAttack] = level * 5; } } -} \ No newline at end of file +} diff --git a/zone/npc.h b/zone/npc.h index e545ffb38..2b8edc9b9 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -499,6 +499,7 @@ protected: Timer attacked_timer; //running while we are being attacked (damaged) Timer swarm_timer; + Timer monkattack_timer; //additional timer for tiger claw usage Timer classattack_timer; Timer knightattack_timer; Timer assist_timer; //ask for help from nearby mobs diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 42ec94616..9334e4f6c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -379,31 +379,35 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk) ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; // Live AA - Technique of Master Wu - int wuchance = - itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { - const int MonkSPA[5] = {EQEmu::skills::SkillFlyingKick, EQEmu::skills::SkillDragonPunch, - EQEmu::skills::SkillEagleStrike, EQEmu::skills::SkillTigerClaw, - EQEmu::skills::SkillRoundKick}; + const int MonkSPA[5] = { + EQEmu::skills::SkillFlyingKick, + EQEmu::skills::SkillDragonPunch, + EQEmu::skills::SkillEagleStrike, + EQEmu::skills::SkillTigerClaw, + EQEmu::skills::SkillRoundKick + }; int extra = 0; // always 1/4 of the double attack chance, 25% at rank 5 (100/4) while (wuchance > 0) { - if (zone->random.Roll(wuchance)) - extra++; - else + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { break; + } wuchance /= 4; } // They didn't add a string ID for this. - std::string msg = StringFormat( - "The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); // live uses 400 here -- not sure if it's the best for all clients though SendColoredText(400, msg); auto classic = RuleB(Combat, ClassicMasterWu); while (extra) { - MonkSpecialAttack(GetTarget(), - classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill); - extra--; + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : ca_atk->m_skill)); + --extra; } } From ac3b4ade10ba9bc68b96d42e36db546f866ae828 Mon Sep 17 00:00:00 2001 From: Noudess Date: Thu, 5 Mar 2020 10:44:01 -0500 Subject: [PATCH 3/4] Implement new always_aggro_foes field and functionality for mobs. --- utils/sql/db_update_manifest.txt | 1 + .../git/required/2020_03_05_npc_always_aggro.sql | 1 + zone/aggro.cpp | 8 +++++--- zone/beacon.cpp | 2 +- zone/client.cpp | 3 ++- zone/corpse.cpp | 10 ++++++---- zone/encounter.cpp | 2 +- zone/mob.cpp | 4 +++- zone/mob.h | 5 ++++- zone/npc.cpp | 3 ++- zone/zonedb.cpp | 14 ++++++++------ zone/zonedump.h | 1 + 12 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 utils/sql/git/required/2020_03_05_npc_always_aggro.sql diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 9d0aa9231..ca7a29f25 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -404,6 +404,7 @@ 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| 9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| 9150|2020_02_06_aa_reset_on_death.sql|SHOW COLUMNS FROM `aa_ability` LIKE 'reset_on_death'|empty| +9151|2020_03_05_npc_always_aggro.sql|SHOW COLUMNS FROM `npc_types` LIKE 'always_aggros_foes'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_03_05_npc_always_aggro.sql b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql new file mode 100644 index 000000000..d9e6351a3 --- /dev/null +++ b/utils/sql/git/required/2020_03_05_npc_always_aggro.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD COLUMN `always_aggros_foes` tinyint(4) NOT NULL DEFAULT 0; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index ff0077353..fac15457e 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -139,7 +139,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { if (RuleB(Aggro, UseLevelAggro)) { - if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3) + if (GetLevel() < RuleI(Aggro, MinAggroLevel) && mob->GetLevelCon(GetLevel()) == CON_GRAY && GetBodyType() != 3 && !AlwaysAggrosFoes()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -147,7 +147,7 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { } else { - if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY ) { + if(GetINT() > RuleI(Aggro, IntAggroThreshold) && mob->GetLevelCon(GetLevel()) == CON_GRAY && !AlwaysAggrosFoes()) { towho->Message(Chat::White, "...%s is red to me (basically)", mob->GetName(), dist2, iAggroRange2); return; @@ -318,7 +318,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetLevel() >= RuleI(Aggro, MinAggroLevel)) - ||(GetBodyType() == 3) + ||(GetBodyType() == 3) || AlwaysAggrosFoes() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -352,6 +352,7 @@ bool Mob::CheckWillAggro(Mob *mob) { //old InZone check taken care of above by !mob->CastToClient()->Connected() ( ( GetINT() <= RuleI(Aggro, IntAggroThreshold) ) + || AlwaysAggrosFoes() ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) ||( mob->GetLevelCon(GetLevel()) != CON_GRAY) @@ -383,6 +384,7 @@ bool Mob::CheckWillAggro(Mob *mob) { LogAggro("Dist^2: [{}]\n", dist2); LogAggro("Range^2: [{}]\n", iAggroRange2); LogAggro("Faction: [{}]\n", fv); + LogAggro("AlwaysAggroFlag: [{}]\n", AlwaysAggrosFoes()); LogAggro("Int: [{}]\n", GetINT()); LogAggro("Con: [{}]\n", GetLevelCon(mob->GetLevel())); diff --git a/zone/beacon.cpp b/zone/beacon.cpp index 81b964290..ac4beaacc 100644 --- a/zone/beacon.cpp +++ b/zone/beacon.cpp @@ -56,7 +56,7 @@ Beacon::Beacon(Mob *at_mob, int lifetime) :Mob ( nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, at_mob->GetPosition(), 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false ), remove_timer(lifetime), spell_timer(0) diff --git a/zone/client.cpp b/zone/client.cpp index 6bd9941f8..8b60b391a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -121,7 +121,8 @@ Client::Client(EQStreamInterface* ieqs) 0, 0, 0, - 0 + 0, + false ), hpupdate_timer(2000), camp_timer(29000), diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 0ac100997..5858d5acc 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -154,7 +154,7 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP in_npc->GetPosition(), in_npc->GetInnateLightType(), in_npc->GetTexture(),in_npc->GetHelmTexture(), 0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,EQEmu::TintProfile(),0xff,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - (*in_npctypedata)->use_model), + (*in_npctypedata)->use_model, false), corpse_decay_timer(in_decaytime), corpse_rez_timer(0), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), @@ -260,8 +260,9 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( 0, // uint8 in_bracertexture, 0, // uint8 in_handtexture, 0, // uint8 in_legtexture, - 0, - 0 // uint8 in_feettexture, + 0, // uint8 in_feettexture, + 0, // uint8 in_usemodel, + 0 // bool in_always_aggros_foes ), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), @@ -500,7 +501,8 @@ EQEmu::TintProfile(), 0, 0, 0, -0), +0, +false), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), diff --git a/zone/encounter.cpp b/zone/encounter.cpp index e0ec83d90..7d37844b6 100644 --- a/zone/encounter.cpp +++ b/zone/encounter.cpp @@ -36,7 +36,7 @@ Encounter::Encounter(const char* enc_name) :Mob ( nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, EQEmu::TintProfile(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false ) { encounter_name[0] = 0; diff --git a/zone/mob.cpp b/zone/mob.cpp index c7b7559da..2e3d3d290 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -93,7 +93,8 @@ Mob::Mob( uint8 in_handtexture, uint8 in_legtexture, uint8 in_feettexture, - uint16 in_usemodel + uint16 in_usemodel, + bool in_always_aggros_foes ) : attack_timer(2000), attack_dw_timer(2000), @@ -275,6 +276,7 @@ Mob::Mob( qglobal = 0; spawned = false; rare_spawn = false; + always_aggros_foes = in_always_aggros_foes; InitializeBuffSlots(); diff --git a/zone/mob.h b/zone/mob.h index 7c095a001..2a89ba384 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -161,7 +161,8 @@ public: uint8 in_handtexture, uint8 in_legtexture, uint8 in_feettexture, - uint16 in_usemodel + uint16 in_usemodel, + bool in_always_aggros_foes ); virtual ~Mob(); @@ -578,6 +579,7 @@ public: inline const GravityBehavior GetFlyMode() const { return flymode; } bool IsBoat() const; bool IsControllableBoat() const; + inline const bool AlwaysAggrosFoes() const { return always_aggros_foes; } //Group virtual bool HasRaid() = 0; @@ -1389,6 +1391,7 @@ protected: Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) int attack_delay; //delay between attacks in 10ths of seconds + bool always_aggros_foes; int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 0b22bab3a..307e7a926 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -113,7 +113,8 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi npc_type_data->handtexture, npc_type_data->legtexture, npc_type_data->feettexture, - npc_type_data->use_model + npc_type_data->use_model, + npc_type_data->always_aggros_foes ), attacked_timer(CombatEventTimer_expire), swarm_timer(100), diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index ea4287f9d..3b03d2563 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2506,7 +2506,8 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load "npc_types.rare_spawn, " "npc_types.stuck_behavior, " "npc_types.model, " - "npc_types.flymode " + "npc_types.flymode, " + "npc_types.always_aggros_foes " "FROM npc_types %s", where_condition.c_str() ); @@ -2703,11 +2704,12 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load temp_npctype_data->charm_avoidance_rating = atoi(row[105]); temp_npctype_data->charm_atk = atoi(row[106]); - temp_npctype_data->skip_global_loot = atoi(row[107]) != 0; - temp_npctype_data->rare_spawn = atoi(row[108]) != 0; - temp_npctype_data->stuck_behavior = atoi(row[109]); - temp_npctype_data->use_model = atoi(row[110]); - temp_npctype_data->flymode = atoi(row[111]); + temp_npctype_data->skip_global_loot = atoi(row[107]) != 0; + temp_npctype_data->rare_spawn = atoi(row[108]) != 0; + temp_npctype_data->stuck_behavior = atoi(row[109]); + temp_npctype_data->use_model = atoi(row[110]); + temp_npctype_data->flymode = atoi(row[111]); + temp_npctype_data->always_aggros_foes = atoi(row[112]); temp_npctype_data->skip_auto_scale = false; // hardcoded here for now diff --git a/zone/zonedump.h b/zone/zonedump.h index 6010c9480..80508f233 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -147,6 +147,7 @@ struct NPCType int8 stuck_behavior; uint16 use_model; int8 flymode; + bool always_aggros_foes; }; namespace player_lootitem { From d85469dff938132d35dd894fed36642268c051f8 Mon Sep 17 00:00:00 2001 From: Noudess Date: Thu, 5 Mar 2020 13:04:21 -0500 Subject: [PATCH 4/4] Update version for always_aggros_foes db change. --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index a950edbe0..359c27576 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9150 +#define CURRENT_BINARY_DATABASE_VERSION 9151 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026