mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-22 14:42:24 +00:00
Merge branch 'master' of https://github.com/EQEmu/Server
This commit is contained in:
commit
09e9c0b504
@ -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
|
||||
@ -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"
|
||||
}
|
||||
"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}"
|
||||
}
|
||||
}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,6 +17,8 @@
|
||||
*.out
|
||||
*.app
|
||||
|
||||
.bash_history
|
||||
|
||||
# CMake
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
|
||||
101
.vscode/tasks.json
vendored
101
.vscode/tasks.json
vendored
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
1
utils/sql/git/required/2020_03_05_npc_always_aggro.sql
Normal file
1
utils/sql/git/required/2020_03_05_npc_always_aggro.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `npc_types` ADD COLUMN `always_aggros_foes` tinyint(4) NOT NULL DEFAULT 0;
|
||||
@ -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()));
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
183
zone/bot.cpp
183
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();
|
||||
|
||||
|
||||
@ -3928,6 +3928,16 @@ void bot_command_owner_option(Client *c, const Seperator *sep)
|
||||
"<td><c \"#00CCCC\">null</td>"
|
||||
"<td><c \"#888888\">(toggles)</td>"
|
||||
"</tr>"
|
||||
"<tr>"
|
||||
"<td><c \"#CCCCCC\">monkwumessage</td>"
|
||||
"<td><c \"#00CC00\">enable <c \"#CCCCCC\">| <c \"#00CC00\">disable</td>"
|
||||
"<td><c \"#888888\">displays monk wu trigger messages</td>"
|
||||
"</tr>"
|
||||
"<tr>"
|
||||
"<td></td>"
|
||||
"<td><c \"#00CCCC\">null</td>"
|
||||
"<td><c \"#888888\">(toggles)</td>"
|
||||
"</tr>"
|
||||
"<tr>"
|
||||
"<td><c \"#CCCCCC\">current</td>"
|
||||
"<td></td>"
|
||||
@ -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)
|
||||
"<td><c \"#FFFFFF\">Option<br>------</td>"
|
||||
"<td><c \"#00FF00\">Argument<br>-------</td>"
|
||||
"</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">deathmarquee</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">statsupdate</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">altcombat</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">autodefend</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">buffcounter</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">deathmarquee</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">statsupdate</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">spawnmessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">altcombat</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">autodefend</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">buffcounter</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"<tr>" "<td><c \"#CCCCCC\">monkwumessage</td>" "<td><c \"#00CC00\">{}</td>" "</tr>"
|
||||
"</table>",
|
||||
(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());
|
||||
|
||||
@ -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 ('{}', '{}', '{}')",
|
||||
|
||||
@ -121,7 +121,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
false
|
||||
),
|
||||
hpupdate_timer(2000),
|
||||
camp_timer(29000),
|
||||
@ -358,6 +359,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);
|
||||
|
||||
@ -1646,6 +1646,7 @@ public:
|
||||
booAltCombat,
|
||||
booAutoDefend,
|
||||
booBuffCounter,
|
||||
booMonkWuMessage,
|
||||
_booCount
|
||||
};
|
||||
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
16
zone/npc.cpp
16
zone/npc.cpp
@ -113,11 +113,13 @@ 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),
|
||||
classattack_timer(1000),
|
||||
monkattack_timer(1000),
|
||||
knightattack_timer(1000),
|
||||
assist_timer(AIassistcheck_delay),
|
||||
qglobal_purge_timer(30000),
|
||||
@ -307,7 +309,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 +3243,4 @@ void NPC::RecalculateSkills()
|
||||
skills[EQEmu::skills::SkillDoubleAttack] = level * 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -147,6 +147,7 @@ struct NPCType
|
||||
int8 stuck_behavior;
|
||||
uint16 use_model;
|
||||
int8 flymode;
|
||||
bool always_aggros_foes;
|
||||
};
|
||||
|
||||
namespace player_lootitem {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user