diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh index eb9fe1a24..9dbe2652c 100644 --- a/apps/installer/includes/functions.sh +++ b/apps/installer/includes/functions.sh @@ -1,7 +1,11 @@ #!/usr/bin/env bash # Set SUDO variable - one liner -SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +if [[ "$OSTYPE" == "msys"* ]]; then + SUDO="" +else + SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +fi function inst_configureOS() { echo "Platform: $OSTYPE" @@ -67,7 +71,7 @@ function inst_dbCreate() { # In CI environments or when no password is set, try without password first if [[ "$CONTINUOUS_INTEGRATION" == "true" ]]; then echo "CI environment detected, attempting connection without password..." - + if $SUDO mysql -u root < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" 2>/dev/null; then echo "Database created successfully." return 0 @@ -75,7 +79,7 @@ function inst_dbCreate() { echo "Failed to connect without password, falling back to interactive mode..." fi fi - + # Try with password (interactive mode) echo "Please enter your sudo and your MySQL root password if prompted." $SUDO mysql -u root -p < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" @@ -178,4 +182,4 @@ function inst_download_client_data { && echo "unzip downloaded file in $path..." && unzip -q -o "$zipPath" -d "$path/" \ && echo "Remove downloaded file" && rm "$zipPath" \ && echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile" -} \ No newline at end of file +} diff --git a/apps/installer/main.sh b/apps/installer/main.sh index b1cba33e8..b0772f76c 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -1,16 +1,16 @@ #!/usr/bin/env bash # AzerothCore Dashboard Script -# +# # This script provides an interactive menu system for AzerothCore management # using the unified menu system library. -# +# # Usage: # ./acore.sh - Interactive mode with numeric and text selection # ./acore.sh [args] - Direct command execution (only text commands, no numbers) # # Interactive Mode: -# - Select options by number (1, 2, 3...), command name (init, compiler, etc.), +# - Select options by number (1, 2, 3...), command name (init, compiler, etc.), # or short alias (i, c, etc.) # - All selection methods work in interactive mode # @@ -35,6 +35,7 @@ menu_items=( "install-deps|d|Configure OS dep" "pull|u|Update Repository" "reset|r|Reset & Clean Repository" + "setup-db|r|Install db only" "compiler|c|Run compiler tool" "module|m|Module manager (search/install/update/remove)" "client-data|gd|download client data from github repository (beta)" @@ -51,52 +52,55 @@ menu_items=( function handle_menu_command() { local key="$1" shift - + case "$key" in - "init") - inst_allInOne + "init") + inst_allInOne ;; - "install-deps") - inst_configureOS + "install-deps") + inst_configureOS ;; - "pull") - inst_updateRepo + "pull") + inst_updateRepo ;; - "reset") - inst_resetRepo + "reset") + inst_resetRepo ;; - "compiler") - bash "$AC_PATH_APPS/compiler/compiler.sh" "$@" + "setup-db") + inst_dbCreate ;; - "module") - bash "$AC_PATH_APPS/installer/includes/modules-manager/module-main.sh" "$@" + "compiler") + bash "$AC_PATH_APPS/compiler/compiler.sh" "$@" ;; - "client-data") - inst_download_client_data + "module") + bash "$AC_PATH_APPS/installer/includes/modules-manager/module-main.sh" "$@" ;; - "run-worldserver") - inst_simple_restarter worldserver + "client-data") + inst_download_client_data ;; - "run-authserver") - inst_simple_restarter authserver + "run-worldserver") + inst_simple_restarter worldserver ;; - "docker") + "run-authserver") + inst_simple_restarter authserver + ;; + "docker") DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@" - exit + exit ;; - "version") + "version") printf "AzerothCore Rev. %s\n" "$ACORE_VERSION" - exit + exit ;; - "service-manager") + "service-manager") bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@" - exit + exit ;; - "quit") + "quit") echo "Goodbye!" - exit + exit ;; - *) + *) echo "Invalid option. Use --help to see available commands." return 1 ;; diff --git a/data/sql/updates/db_world/2025_09_23_02.sql b/data/sql/updates/db_world/2025_09_23_02.sql new file mode 100644 index 000000000..9a3cad72e --- /dev/null +++ b/data/sql/updates/db_world/2025_09_23_02.sql @@ -0,0 +1,7 @@ +-- DB update 2025_09_23_01 -> 2025_09_23_02 +-- +DELETE FROM `spell_script_names` WHERE `spell_id` IN (66050, 62062, 66052) AND `ScriptName` = 'spell_item_brewfest_hops'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(66050, 'spell_item_brewfest_hops'), +(62062, 'spell_item_brewfest_hops'), +(66052, 'spell_item_brewfest_hops'); diff --git a/data/sql/updates/db_world/2025_09_23_03.sql b/data/sql/updates/db_world/2025_09_23_03.sql new file mode 100644 index 000000000..1e05323d9 --- /dev/null +++ b/data/sql/updates/db_world/2025_09_23_03.sql @@ -0,0 +1,99 @@ +-- DB update 2025_09_23_02 -> 2025_09_23_03 +-- +UPDATE `spell_dbc` SET + `Attributes`=384, + `CastingTimeIndex`=1, + `DurationIndex`=3, + `RangeIndex`=7, + `EquippedItemClass`=-1, + `SchoolMask`=1, + `Effect_1`=76, + `ImplicitTargetA_1`=18, + `EffectMiscValue_1`=185541 +WHERE `Id`=39797; + +DELETE FROM `gameobject` WHERE `id`=185541; + +UPDATE `creature_template` SET `AIName`='SmartAI', `unit_flags`=768 WHERE `entry`=22972; +DELETE FROM `smart_scripts` WHERE `entryorguid`=22972 AND `source_type`=0; +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (2297200,2297201,2297202) AND `source_type`=9; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(22972, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 80, 2297200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - On Just Summoned - Action list'), +(22972, 0, 1, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 80, 2297201, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - On Just Summoned - Action list'), +(2297200, 9, 0, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(2297200, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 89, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Random Move'), +(2297200, 9, 2, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(2297200, 9, 3, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(2297200, 9, 4, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 30, 30, 90, 0, 'Cenarion Sparrowhawk - Action list - Move Offset'), +(2297200, 9, 5, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Despawn'), +(2297201, 9, 0, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(2297201, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 19, 22986, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Random Move'), +(2297201, 9, 2, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(22972, 0, 2, 0, 34, 0, 100, 0, 8, 1, 0, 0, 0, 80, 2297202, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - On Movement Inform - Action list'), +(2297202, 9, 0, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Say text'), +(2297202, 9, 1, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 11, 39797, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Cast Summon Raven Stone'), +(2297202, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 19, 22986, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Despawn'), +(2297202, 9, 3, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 114, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 30, 30, 90, 0, 'Cenarion Sparrowhawk - Action list - Move Offset'), +(2297202, 9, 4, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Cenarion Sparrowhawk - Action list - Despawn'); + +DELETE FROM `creature_text` WHERE `CreatureID`=22972; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(22972, 0, 0, "%s looks at you for a moment, then motions for you to follow.", 16, 0, 100, 0, 0, 0, 20689, 0, 'Cenarion Sparrowhawk'), +(22972, 1, 0, '%s surveys the ground for buried raven stones.', 16, 0, 100, 0, 0, 0, 20675, 0, 'Cenarion Sparrowhawk'), +(22972, 2, 0, '%s locates a buried raven stone.', 16, 0, 100, 0, 0, 0, 20676, 0, 'Cenarion Sparrowhawk'), +(22972, 3, 0, "%s doesn't seem to have had any luck finding raven stones nearby.", 16, 0, 100, 0, 0, 0, 21065, 0, 'Cenarion Sparrowhawk'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=22 AND `SourceEntry`=22972 AND `SourceId`=0; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 1, 22972, 0, 0, 29, 1, 22986, 40, 0, 1, 0, 0, '', 'Only run SAI if No Invis Rune Stone within 40 yards'), +(22, 2, 22972, 0, 0, 29, 1, 22986, 40, 0, 0, 0, 0, '', 'Only run SAI if Invis Rune Stone within 40 yards'); + +SET @CGUID := 132442; +-- Replace ALL with sniffed spawns +DELETE FROM `creature` WHERE `id1`=22986; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`) VALUES +(@CGUID+0 , 22986, 0, 0, 530, 3519, 3978, 1, 1, 0, -4180.95, 3124.89, 321.461, 6.14356, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+1 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4116.41, 3212.79, 299.878, 5.55015, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+2 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4088.1, 3247.53, 298.661, 0.191986, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+3 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4189.13, 3257.78, 292.119, 4.34587, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+4 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4056.92, 3349.51, 285.699, 3.80482, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+5 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4085.99, 3318.13, 288.276, 6.21337, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+6 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4135.11, 3297.11, 291.408, 5.34071, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+7 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4195.95, 3307.84, 284.269, 1.65806, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+8 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4145.48, 3366.98, 286.727, 2.46091, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+9 , 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4114.75, 3445.96, 291.581, 2.32129, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+10, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4045.76, 3442.87, 277.701, 0.872665, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+11, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4059.89, 3511.65, 284.039, 2.25147, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+12, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4108.45, 3544.02, 296.909, 2.56563, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+13, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4035.31, 3604.09, 289.025, 2.35619, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+14, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4047.76, 3670.26, 306.392, 2.87979, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+15, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3944.93, 3664.09, 287.99, 1.78024, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+16, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -4015.38, 3703.51, 296.596, 2.3911, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+17, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3904.37, 3726.72, 295.336, 2.37365, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+18, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3928.23, 3804, 296.871, 4.31096, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+19, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3868.97, 3813.03, 293.218, 2.60054, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+20, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3833.35, 3733.27, 285.779, 4.57276, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+21, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3758.84, 3732.39, 276.927, 0.575959, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+22, 22986, 0, 0, 530, 3519, 3974, 1, 1, 0, -3709.53, 3744.04, 277.156, 5.09636, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+23, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3687.76, 3677.07, 276.01, 4.31096, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+24, 22986, 0, 0, 530, 3519, 3680, 1, 1, 0, -3591.33, 3724.59, 286.079, 6.26573, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+25, 22986, 0, 0, 530, 3519, 3680, 1, 1, 0, -3585.6, 3606.65, 283.146, 4.7473, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+26, 22986, 0, 0, 530, 3519, 3680, 1, 1, 0, -3466.94, 3487.49, 286.668, 3.24631, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+27, 22986, 0, 0, 530, 3519, 3973, 1, 1, 0, -3518.43, 3586.55, 278.603, 0.488692, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+28, 22986, 0, 0, 530, 3519, 3978, 1, 1, 0, -4066.59, 3066.77, 318.167, 5.46288, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+29, 22986, 0, 0, 530, 3519, 3978, 1, 1, 0, -4002.09, 3002.9, 361.925, 2.21657, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+30, 22986, 0, 0, 530, 3519, 3977, 1, 1, 0, -3919.28, 3054.06, 359.032, 3.14159, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+31, 22986, 0, 0, 530, 3519, 3977, 1, 1, 0, -3868.7, 3107.64, 333.52, 4.39823, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+32, 22986, 0, 0, 530, 3519, 3977, 1, 1, 0, -3897.7, 3145.86, 326.602, 1.36136, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+33, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3889.21, 3200.03, 313.549, 4.85202, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+34, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3862.66, 3234.81, 307.935, 4.32842, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+35, 22986, 0, 0, 530, 3519, 3976, 1, 1, 0, -3777.56, 3341.35, 276.612, 2.56563, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+36, 22986, 0, 0, 530, 3519, 3975, 1, 1, 0, -3651.93, 3446.62, 279.142, 3.42085, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+37, 22986, 0, 0, 530, 3519, 3975, 1, 1, 0, -3690.24, 3390.89, 282.282, 1.23918, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+38, 22986, 0, 0, 530, 3519, 3975, 1, 1, 0, -3621.2, 3498.29, 277.993, 4.50295, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+39, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3623.24, 3372.64, 294.953, 2.33874, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+40, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3651.9, 3308.47, 285.5, 4.24115, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+41, 22986, 0, 0, 530, 3519, 3679, 1, 1, 0, -3716.68, 3343.7, 287.338, 3.89208, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+42, 22986, 0, 0, 530, 3519, 3680, 1, 1, 0, -3642.5, 3189.22, 314.679, 5.07891, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+43, 22986, 0, 0, 530, 3519, 3976, 1, 1, 0, -4015.76, 3328.22, 285.747, 3.82227, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745), +(@CGUID+44, 22986, 0, 0, 530, 3519, 3978, 1, 1, 0, -4259.69, 2987.85, 312.869, 2.05949, 120, 0, 0, 1604, 852, 0, 0, 0, 0, '', 45745); diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 5374858a4..cadd3b9f4 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -231,6 +231,9 @@ public: // Called when an aura is removed or expires. virtual void OnAuraRemove(AuraApplication* /*aurApp*/, AuraRemoveMode /*mode*/) { } + virtual void DistancingStarted() {} + virtual void DistancingEnded() {} + protected: virtual void MoveInLineOfSight(Unit* /*who*/); diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index db446525d..cb4067e0c 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -49,7 +49,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) mEvadeDisabled = false; mCanAutoAttack = true; - mCanCombatMove = true; mForcedPaused = false; @@ -80,6 +79,9 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) m_ConditionsTimer = 0; if (me->GetVehicleKit()) conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry()); + + _currentRangeMode = false; + _attackDistance = 0.f; } bool SmartAI::IsAIControlled() const @@ -846,7 +848,7 @@ void SmartAI::AttackStart(Unit* who) if (who && me->Attack(who, me->IsWithinMeleeRange(who))) { - if (mCanCombatMove) + if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) { SetRun(mRun); MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE); @@ -1047,37 +1049,49 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt) GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt); } -void SmartAI::SetCombatMove(bool on, float chaseRange) +void SmartAI::SetCombatMovement(bool on, bool stopOrStartMovement) { - if (mCanCombatMove == on) + if (on) + me->ClearUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT); + else + me->AddUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT); + + if (!IsAIControlled() || HasEscortState(SMART_ESCORT_ESCORTING)) return; - mCanCombatMove = on; - - if (!IsAIControlled()) - return; - - if (!HasEscortState(SMART_ESCORT_ESCORTING)) + if (stopOrStartMovement && me->GetVictim()) // Only change current movement while in combat { - if (on && me->GetVictim()) + if (!me->IsCrowdControlled()) { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) - { - SetRun(mRun); - me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange); - me->CastStop(); - } - } - else - { - me->StopMoving(); - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveIdle(); + if (on) + me->GetMotionMaster()->MoveChase(me->GetVictim()); + else if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + me->StopMoving(); } } } +void SmartAI::SetCurrentRangeMode(bool on, float range) +{ + _currentRangeMode = on; + _attackDistance = range; + + if (Unit* victim = me->GetVictim()) + me->GetMotionMaster()->MoveChase(victim, _attackDistance); +} + +void SmartAI::DistanceYourself(float range) +{ + Unit* victim = me->GetVictim(); + if (!victim || !victim->IsWithinMeleeRange(me)) + return; + + float combatReach = me->GetMeleeRange(victim); + float distance = DISTANCING_CONSTANT + std::max(combatReach * 1.5f, combatReach + range); + me->GetMotionMaster()->DistanceYourself(distance); + _pendingDistancing = distance; +} + void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, uint32 end, uint32 creditType, bool aliveState) { if (!target) @@ -1130,32 +1144,6 @@ void SmartAI::StopFollow(bool complete) GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player); } -void SmartAI::MoveAway(float distance) -{ - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) - return; - - mCanCombatMove = false; - - if (!IsAIControlled()) - return; - - if (!HasEscortState(SMART_ESCORT_ESCORTING)) - { - if (me->GetVictim()) - { - me->StopMoving(); - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) - me->GetMotionMaster()->Clear(false); - - float x, y, z; - me->GetClosePoint(x, y, z, me->GetObjectSize(), distance, M_PI); - if (me->GetVictim()->IsWithinLOS(x, y, z)) - me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z); - } - } -} - void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker) { if (invoker) @@ -1183,6 +1171,12 @@ void SmartAI::PathEndReached(uint32 /*pathId*/) me->LoadPath(0); } +void SmartAI::DistancingEnded() +{ + SetCurrentRangeMode(true, _pendingDistancing); + _pendingDistancing = 0.f; +} + void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/) { GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon); diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 7b3a36772..5bd9fcd8b 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -40,6 +40,8 @@ enum SmartEscortVars SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2, }; +#define DISTANCING_CONSTANT 1.f // buffer for better functionality of distancing + class SmartAI : public CreatureAI { public: @@ -63,11 +65,11 @@ public: bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); } void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; } void SetAutoAttack(bool on) { mCanAutoAttack = on; } - void SetCombatMove(bool on, float chaseRange = 0.0f); - bool CanCombatMove() { return mCanCombatMove; } + void SetCombatMovement(bool on, bool stopOrStartMovement); + void SetCurrentRangeMode(bool on, float range = 0.f); + void DistanceYourself(float range); void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true); void StopFollow(bool complete); - void MoveAway(float distance); void SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker); SmartScript* GetScript() { return &mScript; } @@ -212,8 +214,7 @@ public: // Xinef void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } - void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; } - [[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; } + void DistancingEnded() override; private: bool mIsCharmed; @@ -242,7 +243,6 @@ private: bool mRun; bool mEvadeDisabled; bool mCanAutoAttack; - bool mCanCombatMove; bool mForcedPaused; uint32 mInvincibilityHpLevel; @@ -263,6 +263,10 @@ private: bool _chaseOnInterrupt; std::unordered_map aiDataSet; + + bool _currentRangeMode; + float _attackDistance; + float _pendingDistancing; }; class SmartGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index ffedcdcf6..f4f9e82da 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -686,7 +686,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u float spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo); float meleeRange = me->GetMeleeRange(target->ToUnit()); - bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit()); + bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit(), VMAP::ModelIgnoreFlags::M2); bool isWithinMeleeRange = distanceToTarget <= meleeRange; bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE; bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT); @@ -703,7 +703,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u continue; float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE; - CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange)); + CAST_AI(SmartAI, me->AI())->DistanceYourself(std::min(minDistance, spellMaxRange)); continue; } @@ -715,7 +715,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (me->IsRooted()) // Rooted inhabit type, never move/reposition continue; - CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); continue; } else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS)) @@ -725,7 +725,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (me->IsRooted()) // Rooted inhabit type, never move/reposition continue; - CAST_AI(SmartAI, me->AI())->SetCombatMove(true); + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, 0.f); + if (e.action.cast.castFlags & SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS) + CAST_AI(SmartAI, me->AI())->SetCombatMovement(true, true); continue; } @@ -743,18 +745,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) { - CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true); - - if (!me->isMoving()) // Don't try to reposition while we are moving - { - // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. - if (result == SPELL_FAILED_OUT_OF_RANGE) - // if we are just out of range, we only chase until we are back in spell range. - CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); - else - // if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful. - CAST_AI(SmartAI, me->AI())->SetCombatMove(spellCastFailed); - } + // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. + if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK) + // if we are just out of range, we only chase until we are back in spell range. + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); + else // move into melee on any other fail + // if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful. + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f); } if (spellCastFailed) @@ -989,7 +986,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; bool move = e.action.combatMove.move; - CAST_AI(SmartAI, me->AI())->SetCombatMove(move); + CAST_AI(SmartAI, me->AI())->SetCombatMovement(move, true); LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}", me->GetGUID().ToString(), e.action.combatMove.move); break; @@ -2061,7 +2058,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (WorldObject* target : targets) if (Creature* creature = target->ToCreature()) if (IsSmart(creature) && creature->GetVictim()) - if (CAST_AI(SmartAI, creature->AI())->CanCombatMove()) + if (!creature->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, attackAngle); break; @@ -2730,26 +2727,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u me->InterruptNonMeleeSpells(false); } - if (e.action.castCustom.flags & SMARTCAST_COMBAT_MOVE) - { - // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed - // unless target is outside spell range, out of mana, or LOS. - - bool _allowMove = false; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell); // AssertSpellInfo? - int32 mana = me->GetPower(POWER_MANA); - - if (me->GetDistance(target->ToUnit()) > spellInfo->GetMaxRange(true) || - me->GetDistance(target->ToUnit()) < spellInfo->GetMinRange(true) || - !me->IsWithinLOSInMap(target->ToUnit()) || - mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask())) - _allowMove = true; - - CAST_AI(SmartAI, me->AI())->SetCombatMove(_allowMove); - } - if (!(e.action.castCustom.flags & SMARTCAST_AURA_NOT_PRESENT) || !target->ToUnit()->HasAura(e.action.castCustom.spell)) { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell); CustomSpellValues values; if (e.action.castCustom.bp1) values.AddSpellMod(SPELLVALUE_BASE_POINT0, e.action.castCustom.bp1); @@ -2757,7 +2737,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u values.AddSpellMod(SPELLVALUE_BASE_POINT1, e.action.castCustom.bp2); if (e.action.castCustom.bp3) values.AddSpellMod(SPELLVALUE_BASE_POINT2, e.action.castCustom.bp3); - me->CastCustomSpell(e.action.castCustom.spell, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE); + SpellCastResult result = me->CastCustomSpell(spellInfo, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE); + + float spellMaxRange = me->GetSpellMaxRangeForTarget(target->ToUnit(), spellInfo); + if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) + { + // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. + if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK) + // if we are just out of range, we only chase until we are back in spell range. + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f)); + else // move into melee on any other fail + // if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful. + CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f); + } } } } diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index c8b96a926..e589d7bf1 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1957,7 +1957,8 @@ enum SmartCastFlags SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment) - SMARTCAST_TARGET_POWER_MANA = 0x100 // Only cast if the target has power type mana (e.g. Mana Drain) + SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain) + SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200, }; enum SmartFollowType diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index bb6fa29c4..229cedd59 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1081,7 +1081,6 @@ void ConditionMgr::LoadConditions(bool isReload) LOG_INFO("server.loading", "Reloading `gossip_menu_option` Table for Conditions!"); sObjectMgr->LoadGossipMenuItems(); - sSpellMgr->UnloadSpellInfoImplicitTargetConditionLists(); } QueryResult result = WorldDatabase.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, SourceId, ElseGroup, ConditionTypeOrReference, ConditionTarget, " @@ -1405,7 +1404,7 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) // build new shared mask with found effect uint32 sharedMask = (1 << i); - ConditionList* cmp = spellInfo->Effects[i].ImplicitTargetConditions; + std::shared_ptr cmp = spellInfo->Effects[i].ImplicitTargetConditions; for (uint8 effIndex = i + 1; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { if (spellInfo->Effects[effIndex].ImplicitTargetConditions == cmp) @@ -1428,7 +1427,7 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) return false; // get shared data - ConditionList* sharedList = spellInfo->Effects[firstEffIndex].ImplicitTargetConditions; + std::shared_ptr sharedList = spellInfo->Effects[firstEffIndex].ImplicitTargetConditions; // there's already data entry for that sharedMask if (sharedList) @@ -1447,22 +1446,25 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond) else { // add new list, create new shared mask - sharedList = new ConditionList(); + auto newList = std::make_shared(); + bool assigned = false; for (uint8 i = firstEffIndex; i < MAX_SPELL_EFFECTS; ++i) { if ((1 << i) & commonMask) { - spellInfo->Effects[i].ImplicitTargetConditions = sharedList; - assigned = true; + spellInfo->Effects[i].ImplicitTargetConditions = newList; + assigned = true; } } - if (!assigned) - delete sharedList; + if (assigned) + sharedList = newList; } + if (sharedList) sharedList->push_back(cond); + break; } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index be2e864c2..d20731079 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1283,6 +1283,11 @@ SpellCastResult Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const& v return SPELL_FAILED_SPELL_UNAVAILABLE; } + return CastCustomSpell(spellInfo, value, victim, triggerFlags, castItem, triggeredByAura, originalCaster); +} + +SpellCastResult Unit::CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster) +{ SpellCastTargets targets; targets.SetUnitTarget(victim); @@ -4130,15 +4135,6 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi spell->SetReferencedFromCurrent(false); } - // SAI creatures only - // Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced. - if (IsCreature()) - { - if (SmartAI* ai = dynamic_cast(ToCreature()->AI())) - if (ai->CanChaseOnInterrupt()) - ai->SetCombatMove(true); - } - if (IsCreature() && IsAIEnabled) ToCreature()->AI()->OnSpellCastFinished(spell->GetSpellInfo(), SPELL_FINISHED_CANCELED); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a039be907..91ab18fd5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -736,6 +736,8 @@ public: [[nodiscard]] uint16 GetExtraUnitMovementFlags() const { return m_movementInfo.flags2; } void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; } + inline bool IsCrowdControlled() const { return HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_STUNNED)); } + /*********************************************************/ /*** UNIT TYPES, CLASSES, RACES... ***/ /*********************************************************/ @@ -1595,6 +1597,7 @@ public: SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); SpellCastResult CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); + SpellCastResult CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); /*********************************************************/ /*** METHODS RELATED TO GAMEOBJECT & DYNOBEJCTS ***/ diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index c0a39a03c..e6885fe16 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -197,6 +197,8 @@ enum UnitState UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator UNIT_STATE_NO_ENVIRONMENT_UPD = 0x20000000, + UNIT_STATE_NO_COMBAT_MOVEMENT, // serverside only - should never be changed outside of core and hence shouldnt have a defined static value and be at the end + UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE | UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED | UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 3fbff9639..81b487cb3 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -310,12 +310,29 @@ void MotionMaster::MoveConfused() /** * @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE */ -void MotionMaster::MoveChase(Unit* target, std::optional dist, std::optional angle) +void MotionMaster::MoveChase(Unit* target, std::optional dist, std::optional angle) { // ignore movement request if target not exist if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; + if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + { + if (_owner->IsPlayer()) + { + ChaseMovementGenerator* gen = (ChaseMovementGenerator*)top(); + gen->SetOffsetAndAngle(dist, angle); + gen->SetNewTarget(target); + } + else + { + ChaseMovementGenerator* gen = (ChaseMovementGenerator*)top(); + gen->SetOffsetAndAngle(dist, angle); + gen->SetNewTarget(target); + } + return; + } + //_owner->ClearUnitState(UNIT_STATE_FOLLOW); if (_owner->IsPlayer()) { @@ -331,6 +348,24 @@ void MotionMaster::MoveChase(Unit* target, std::optional dist, std: } } +void MotionMaster::DistanceYourself(float dist) +{ + if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE) + { + if (_owner->IsPlayer()) + { + ChaseMovementGenerator* gen = (ChaseMovementGenerator*)top(); + gen->DistanceYourself((Player*)_owner, dist); + } + else + { + ChaseMovementGenerator* gen = (ChaseMovementGenerator*)top(); + gen->DistanceYourself((Creature*)_owner, dist); + } + return; + } +} + void MotionMaster::MoveBackwards(Unit* target, float dist) { if (!target) diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index c3bb2120b..31c8568fa 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -250,6 +250,8 @@ public: void ReinitializeMovement(); bool GetDestination(float& x, float& y, float& z); + + void DistanceYourself(float range); private: void Mutate(MovementGenerator* m, MovementSlot slot); // use Move* functions instead diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 76fb0840a..4dfc68828 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -62,6 +62,81 @@ bool ChaseMovementGenerator::PositionOkay(T* owner, Unit* target, Optional +void ChaseMovementGenerator::SetOffsetAndAngle(std::optional dist, std::optional angle) +{ + _range = dist; + _angle = angle; + _lastTargetPosition.reset(); +} + +template +void ChaseMovementGenerator::SetNewTarget(Unit* target) +{ + i_target.link(target, this); + _lastTargetPosition.reset(); +} + +template +void ChaseMovementGenerator::DistanceYourself(T* owner, float distance) +{ + // make a new path if we have to... + if (!i_path) + i_path = std::make_unique(owner); + + float x, y, z; + i_target->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, i_target->GetAngle(owner)); + if (DispatchSplineToPosition(owner, x, y, z, false, false, 0.f, false, false)) + { + m_currentMode = CHASE_MODE_DISTANCING; + if constexpr (!std::is_same_v) + { + owner->AI()->DistancingStarted(); + } + } +} + +template +bool ChaseMovementGenerator::DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target) +{ + Creature* cOwner = owner->ToCreature(); + + if (owner->IsHovering()) + owner->UpdateAllowedPositionZ(x, y, z); + + bool success = i_path->CalculatePath(x, y, z, forceDest); + if (!success || i_path->GetPathType() & PATHFIND_NOPATH) + { + if (cOwner) + { + cOwner->SetCannotReachTarget(i_target.getTarget()->GetGUID()); + } + + owner->StopMoving(); + return true; + } + + if (cutPath) + i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget); + + if (cOwner) + { + cOwner->SetCannotReachTarget(); + } + + owner->AddUnitState(UNIT_STATE_CHASE_MOVE); + i_recalculateTravel = true; + + Movement::MoveSplineInit init(owner); + init.MovebyPath(i_path->GetPath()); + if (target) + init.SetFacing(i_target.getTarget()); + init.SetWalk(walk); + init.Launch(); + + return false; +} + template bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) { @@ -71,6 +146,13 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) if (!owner || !owner->IsAlive()) return false; + if (owner->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) // script paused combat movement + { + owner->StopMoving(); + _lastTargetPosition.reset(); + return true; + } + Creature* cOwner = owner->ToCreature(); bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting(); @@ -243,53 +325,23 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) shortenPath = false; } - if (owner->IsHovering()) - owner->UpdateAllowedPositionZ(x, y, z); - - bool success = i_path->CalculatePath(x, y, z, forceDest); - if (!success || i_path->GetPathType() & PATHFIND_NOPATH) - { - if (cOwner) - { - cOwner->SetCannotReachTarget(target->GetGUID()); - } - - owner->StopMoving(); - return true; - } - - if (shortenPath) - i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget); - - if (cOwner) - { - cOwner->SetCannotReachTarget(); - } - bool walk = false; if (cOwner && !cOwner->IsPet()) { switch (cOwner->GetMovementTemplate().GetChase()) { - case CreatureChaseMovementType::CanWalk: - walk = owner->IsWalking(); - break; - case CreatureChaseMovementType::AlwaysWalk: - walk = true; - break; - default: - break; + case CreatureChaseMovementType::CanWalk: + walk = owner->IsWalking(); + break; + case CreatureChaseMovementType::AlwaysWalk: + walk = true; + break; + default: + break; } } - owner->AddUnitState(UNIT_STATE_CHASE_MOVE); - i_recalculateTravel = true; - - Movement::MoveSplineInit init(owner); - init.MovebyPath(i_path->GetPath()); - init.SetFacing(target); - init.SetWalk(walk); - init.Launch(); + DispatchSplineToPosition(owner, x, y, z, walk, shortenPath, maxTarget, forceDest, true); } } @@ -339,9 +391,24 @@ void ChaseMovementGenerator::MovementInform(T* owner) if (!owner->IsCreature()) return; - // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle - if (CreatureAI* AI = owner->ToCreature()->AI()) - AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + switch (m_currentMode) + { + default: + { + // Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle + if (CreatureAI* AI = owner->ToCreature()->AI()) + AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter()); + break; + } + case CHASE_MODE_DISTANCING: + { + if (CreatureAI* AI = owner->ToCreature()->AI()) + AI->DistancingEnded(); + break; + } + } + + m_currentMode = CHASE_MODE_NORMAL; } //-----------------------------------------------// @@ -604,6 +671,13 @@ template bool ChaseMovementGenerator::DoUpdate(Player*, uint32); template bool ChaseMovementGenerator::DoUpdate(Creature*, uint32); template void ChaseMovementGenerator::MovementInform(Unit*); +template void ChaseMovementGenerator::SetOffsetAndAngle(std::optional, std::optional); +template void ChaseMovementGenerator::SetNewTarget(Unit*); +template void ChaseMovementGenerator::DistanceYourself(Creature*, float); +template void ChaseMovementGenerator::SetOffsetAndAngle(std::optional, std::optional); +template void ChaseMovementGenerator::SetNewTarget(Unit*); +template void ChaseMovementGenerator::DistanceYourself(Player*, float); + template void FollowMovementGenerator::DoInitialize(Player*); template void FollowMovementGenerator::DoInitialize(Creature*); template void FollowMovementGenerator::DoFinalize(Player*); diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h index 82ba2df47..e5e87f845 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h @@ -34,12 +34,20 @@ protected: FollowerReference i_target; }; +enum ChaseMovementMode +{ + CHASE_MODE_NORMAL, // chasing target + CHASE_MODE_BACKPEDAL, // collision movement + CHASE_MODE_DISTANCING, // running away from melee + CHASE_MODE_FANNING, // mob collision movement +}; + template class ChaseMovementGenerator : public MovementGeneratorMedium>, public TargetedMovementGeneratorBase { public: ChaseMovementGenerator(Unit* target, Optional range = {}, Optional angle = {}) - : TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {} + : TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle), m_currentMode(CHASE_MODE_NORMAL) {} ~ChaseMovementGenerator() { } MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; } @@ -58,6 +66,11 @@ public: bool EnableWalking() const { return false; } bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); } + void SetOffsetAndAngle(std::optional dist, std::optional angle); + void SetNewTarget(Unit* target); + + void DistanceYourself(T* owner, float distance); + bool DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target = false); private: TimeTrackerSmall i_leashExtensionTimer; std::unique_ptr i_path; @@ -65,10 +78,12 @@ private: bool i_recalculateTravel; Optional _lastTargetPosition; - Optional const _range; - Optional const _angle; + Optional _range; + Optional _angle; bool _movingTowards = true; bool _mutualChase = true; + + ChaseMovementMode m_currentMode; }; template diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index daddbe9dd..5b2aeb110 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1126,7 +1126,7 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar break; } - ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions; + std::shared_ptr condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions; // handle emergency case - try to use other provided targets if no conditions provided if (targetType.GetCheckType() == TARGET_CHECK_ENTRY && (!condList || condList->empty())) @@ -1221,7 +1221,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge std::list targets; SpellTargetObjectTypes objectType = targetType.GetObjectType(); SpellTargetCheckTypes selectionType = targetType.GetCheckType(); - ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions; + std::shared_ptr condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions; float coneAngle = M_PI / 2; float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod; @@ -2119,7 +2119,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex) } } -uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList) +uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, std::shared_ptr condList) { // this function selects which containers need to be searched for spell target uint32 retMask = GRID_MAP_TYPE_MASK_ALL; @@ -2164,7 +2164,9 @@ void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* refere Cell::VisitObjects(pos->GetPositionX(), pos->GetPositionY(), referer->GetMap(), searcher, radius); } -WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList) +WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, + SpellTargetCheckTypes selectionType, + std::shared_ptr condList) { WorldObject* target = nullptr; uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); @@ -2176,7 +2178,11 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList) +void Spell::SearchAreaTargets(std::list &targets, float range, + Position const *position, Unit *referer, + SpellTargetObjectTypes objectType, + SpellTargetCheckTypes selectionType, + std::shared_ptr condList) { uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); if (!containerTypeMask) @@ -2186,7 +2192,11 @@ void Spell::SearchAreaTargets(std::list& targets, float range, Pos SearchTargets > (searcher, containerTypeMask, m_caster, position, range); } -void Spell::SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories /*selectCategory*/, ConditionList* condList, bool isChainHeal) +void Spell::SearchChainTargets( + std::list &targets, uint32 chainTargets, WorldObject *target, + SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, + SpellTargetSelectionCategories /*selectCategory*/, + std::shared_ptr condList, bool isChainHeal) { // max dist for jump target selection float jumpRadius = 0.0f; @@ -2223,7 +2233,8 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, + objectType, selectType, condList); tempTargets.remove(target); // remove targets which are always invalid for chain spells @@ -8984,7 +8995,7 @@ namespace Acore { WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionList* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), + SpellTargetCheckTypes selectionType, std::shared_ptr condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo), _targetSelectionType(selectionType), _condList(condList) { if (condList) @@ -9067,7 +9078,7 @@ namespace Acore } WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionList* condList) + SpellTargetCheckTypes selectionType, std::shared_ptr condList) : WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(caster) { } @@ -9084,7 +9095,7 @@ namespace Acore } WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList) + Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList) : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position) { } @@ -9104,7 +9115,7 @@ namespace Acore } WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList) + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList) : WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneAngle(coneAngle) { } @@ -9130,7 +9141,7 @@ namespace Acore } WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList) + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList) : WorldObjectSpellAreaTargetCheck(range, position, caster, caster, spellInfo, selectionType, condList) { } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index bb8f2cb7b..50ba2a4bc 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -445,12 +445,25 @@ public: void SelectEffectTypeImplicitTargets(uint8 effIndex); - uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList); + uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, + std::shared_ptr condList); template void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius); - WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = nullptr); - void SearchAreaTargets(std::list& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList); - void SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories selectCategory, ConditionList* condList, bool isChainHeal); + WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, + SpellTargetCheckTypes selectionType, + std::shared_ptr condList = nullptr); + void SearchAreaTargets(std::list &targets, float range, + Position const *position, Unit *referer, + SpellTargetObjectTypes objectType, + SpellTargetCheckTypes selectionType, + std::shared_ptr condList); + void SearchChainTargets(std::list &targets, + uint32 chainTargets, WorldObject *target, + SpellTargetObjectTypes objectType, + SpellTargetCheckTypes selectType, + SpellTargetSelectionCategories selectCategory, + std::shared_ptr condList, + bool isChainHeal); SpellCastResult prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = nullptr); void cancel(bool bySelf = false); @@ -801,10 +814,10 @@ namespace Acore SpellInfo const* _spellInfo; SpellTargetCheckTypes _targetSelectionType; ConditionSourceInfo* _condSrcInfo; - ConditionList* _condList; + std::shared_ptr _condList; WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionList* condList); + SpellTargetCheckTypes selectionType, std::shared_ptr condList); ~WorldObjectSpellTargetCheck(); bool operator()(WorldObject* target); }; @@ -814,7 +827,7 @@ namespace Acore float _range; Position const* _position; WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionList* condList); + SpellTargetCheckTypes selectionType, std::shared_ptr condList); bool operator()(WorldObject* target); }; @@ -823,7 +836,7 @@ namespace Acore float _range; Position const* _position; WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster, - Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList); + Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList); bool operator()(WorldObject* target); }; @@ -831,14 +844,14 @@ namespace Acore { float _coneAngle; WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList); + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList); bool operator()(WorldObject* target); }; struct WorldObjectSpellTrajTargetCheck : public WorldObjectSpellAreaTargetCheck { WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster, - SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList); + SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr condList); bool operator()(WorldObject* target); }; } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 64a25a2b4..58c20660b 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -863,11 +863,6 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry) _requireCooldownInfo = false; } -SpellInfo::~SpellInfo() -{ - _UnloadImplicitTargetConditionLists(); -} - uint32 SpellInfo::GetCategory() const { return CategoryEntry ? CategoryEntry->Id : 0; @@ -2873,23 +2868,6 @@ bool SpellInfo::_IsPositiveTarget(uint32 targetA, uint32 targetB) return true; } -void SpellInfo::_UnloadImplicitTargetConditionLists() -{ - // find the same instances of ConditionList and delete them. - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - ConditionList* cur = Effects[i].ImplicitTargetConditions; - if (!cur) - continue; - for (uint8 j = i; j < MAX_SPELL_EFFECTS; ++j) - { - if (Effects[j].ImplicitTargetConditions == cur) - Effects[j].ImplicitTargetConditions = nullptr; - } - delete cur; - } -} - bool SpellInfo::CheckElixirStacking(Unit const* caster) const { if (!caster) diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 534ad254a..cabdb3158 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -39,6 +39,7 @@ struct SpellRadiusEntry; struct SpellEntry; struct SpellCastTimesEntry; struct Condition; +typedef std::list ConditionList; enum SpellCastTargetFlags { @@ -270,12 +271,12 @@ public: uint32 ItemType; uint32 TriggerSpell; flag96 SpellClassMask; - std::list* ImplicitTargetConditions; + std::shared_ptr ImplicitTargetConditions; SpellEffectInfo() : _spellInfo(nullptr), _effIndex(0), Effect(0), ApplyAuraName(0), Amplitude(0), DieSides(0), RealPointsPerLevel(0), BasePoints(0), PointsPerComboPoint(0), ValueMultiplier(0), DamageMultiplier(0), BonusMultiplier(0), MiscValue(0), MiscValueB(0), Mechanic(MECHANIC_NONE), RadiusEntry(nullptr), ChainTarget(0), - ItemType(0), TriggerSpell(0), ImplicitTargetConditions(nullptr) {} + ItemType(0), TriggerSpell(0), ImplicitTargetConditions() {} SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex); bool IsEffect() const; @@ -403,7 +404,6 @@ public: bool _requireCooldownInfo; SpellInfo(SpellEntry const* spellEntry); - ~SpellInfo(); uint32 GetCategory() const; bool HasEffect(SpellEffects effect) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 534388fd4..cc770509a 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2766,15 +2766,6 @@ void SpellMgr::UnloadSpellInfoStore() mSpellInfoMap.clear(); } -void SpellMgr::UnloadSpellInfoImplicitTargetConditionLists() -{ - for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) - { - if (mSpellInfoMap[i]) - mSpellInfoMap[i]->_UnloadImplicitTargetConditionLists(); - } -} - void SpellMgr::LoadSpellSpecificAndAuraState() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 3a9700b6d..ca5ee6857 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -786,7 +786,6 @@ public: void LoadSpellInfoStore(); void LoadSpellCooldownOverrides(); void UnloadSpellInfoStore(); - void UnloadSpellInfoImplicitTargetConditionLists(); void LoadSpellInfoCustomAttributes(); void LoadSpellInfoCorrections(); void LoadSpellSpecificAndAuraState(); diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 88e3ce5a0..dcfd61342 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -887,6 +887,7 @@ static LocationXYZO ThrallSpawn[] = // Valimathras Trashspawn { 1325.059f, 332.652f, -65.027f, 2.186f }, { 1270.474f, 350.982f, -65.027f, 0.034f }, + { 1805.753f, 285.499f, 70.399f, 4.691f } }; #define GOSSIP_WRYNN "Reporting for duty, your majesty! Let the assault begin!" @@ -2382,7 +2383,8 @@ public: switch (summoned->GetEntry()) { case NPC_BLIGHT_ABBERATION: - summoned->AI()->AttackStart(me); + summoned->SetHomePosition(me->GetPosition()); + summoned->AddThreat(me, 100.0f); break; case NPC_WARSONG_BATTLEGUARD: summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true); @@ -2424,6 +2426,10 @@ public: me->AddThreat(summoned, 100.0f); summoned->AI()->AttackStart(me); break; + case NPC_KHANOK: + summoned->SetHomePosition(me->GetPosition()); + summoned->AddThreat(me, 100.0f); + summoned->AI()->AttackStart(me); default: break; } @@ -2580,10 +2586,8 @@ public: // Bossspawn 1 if (Creature* temp = me->SummonCreature(NPC_BLIGHT_ABBERATION, ThrallSpawn[28].x, ThrallSpawn[28].y, ThrallSpawn[28].z, ThrallSpawn[28].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS)) { - temp->GetMotionMaster()->MoveJump(ThrallSpawn[62].x, ThrallSpawn[62].y, ThrallSpawn[62].z, 10.0f, 20.0f, 0); - temp->AddThreat(me, 100.0f); me->AddThreat(temp, 100.0f); - temp->AI()->AttackStart(me); + me->AI()->AttackStart(temp); } break; case 6: @@ -2747,7 +2751,11 @@ public: break; // NPC_KHANOK - Inner Sunktum Spawn Middle case 17: - me->SummonCreature(NPC_KHANOK, ThrallSpawn[68].x, ThrallSpawn[68].y, ThrallSpawn[68].z, TEMPSUMMON_DEAD_DESPAWN); + if (Creature* temp = me->SummonCreature(NPC_KHANOK, ThrallSpawn[68].x, ThrallSpawn[68].y, ThrallSpawn[68].z, TEMPSUMMON_DEAD_DESPAWN)) + { + me->AddThreat(temp, 100.0f); + me->AI()->AttackStart(temp); + } break; case 18: if (Creature* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[69].x, ThrallSpawn[69].y, ThrallSpawn[69].z, ThrallSpawn[69].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 240 * IN_MILLISECONDS)) @@ -3167,9 +3175,8 @@ public: me->GetCreatureListWithEntryInGrid(HostileList, NPC_DOCTOR_H, 1000.0f); me->GetCreatureListWithEntryInGrid(HostileList, NPC_CHEMIST_H, 1000.0f); me->GetCreatureListWithEntryInGrid(HostileList, NPC_BLIGHT_SLINGER, 1000.0f); - if (!HostileList.empty()) - for (std::list::iterator itr = HostileList.begin(); itr != HostileList.end(); itr++) - (*itr)->DespawnOrUnsummon(); + for (auto& creature : HostileList) + creature->DespawnOrUnsummon(); for (uint8 i = 0; i < 7; ++i) me->SummonGameObject(GO_HORDE_BANNER, ThrallSpawn[i + 37].x, ThrallSpawn[i + 37].y, ThrallSpawn[i + 37].z, ThrallSpawn[i + 37].o, 0.0f, 0.0f, 0.0f, 0.0f, 120 * IN_MILLISECONDS); SpawnWave(6); @@ -3620,9 +3627,11 @@ public: valimathras->RemoveAura(SPELL_AURA_OF_VARIMATHRAS); valimathras->RemoveAura(SPELL_OPENING_LEGION_PORTALS); valimathras->AI()->Talk(SAY_VALIMATHRAS_ATTACK); + valimathras->SetHomePosition(me->GetPosition()); valimathras->AddThreat(me, 100.0f); me->AddThreat(valimathras, 100.0f); valimathras->AI()->AttackStart(me); + me->AI()->AttackStart(valimathras); } bStepping = false; JumpToNextStep(0 * IN_MILLISECONDS); @@ -3945,7 +3954,6 @@ public: { me->SetCorpseDelay(1); me->SetRespawnTime(1); - _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20s); _events.ScheduleEvent(EVENT_BLACK_ARROW, 15s); _events.ScheduleEvent(EVENT_SHOOT, 5s); _events.ScheduleEvent(EVENT_MULTI_SHOT, 6s); @@ -3974,10 +3982,6 @@ public: { switch (eventId) { - case EVENT_SUMMON_SKELETON: - DoCast(me, SPELL_SUMMON_SKELETON); - _events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20s, 30s); - break; case EVENT_BLACK_ARROW: if (Unit* victim = me->GetVictim()) DoCast(victim, SPELL_BLACK_ARROW); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index bb9e3e079..29269f537 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -3147,6 +3147,7 @@ enum BrewfestMountTransformation SPELL_MOUNT_KODO_60 = 49378, SPELL_BREWFEST_MOUNT_TRANSFORM = 49357, SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE = 52845, + SPELL_FRESH_DWARVEN_HOPS = 66050, }; class spell_item_brewfest_mount_transformation : public SpellScript @@ -3169,25 +3170,26 @@ class spell_item_brewfest_mount_transformation : public SpellScript Player* caster = GetCaster()->ToPlayer(); if (!caster) - { return; - } if (caster->HasMountedAura()) { + float speed = caster->GetSpeedRate(MOVE_RUN); + caster->RemoveAurasByType(SPELL_AURA_MOUNTED); + uint32 spell_id; switch (GetSpellInfo()->Id) { case SPELL_BREWFEST_MOUNT_TRANSFORM: - if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) + if (speed >= 2.0f) spell_id = caster->GetTeamId() == TEAM_ALLIANCE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; else spell_id = caster->GetTeamId() == TEAM_ALLIANCE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; break; case SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE: - if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f) + if (speed >= 2.0f) spell_id = caster->GetTeamId() == TEAM_HORDE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100; else spell_id = caster->GetTeamId() == TEAM_HORDE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60; @@ -3205,6 +3207,48 @@ class spell_item_brewfest_mount_transformation : public SpellScript } }; +class spell_item_brewfest_hops : public AuraScript +{ + PrepareAuraScript(spell_item_brewfest_hops); + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo( + { + SPELL_BREWFEST_MOUNT_TRANSFORM, + SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE, + }); + } + + bool Load() override + { + _spell_id = GetSpellInfo()->Id == SPELL_FRESH_DWARVEN_HOPS ? SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE : SPELL_BREWFEST_MOUNT_TRANSFORM; + return true; + } + + void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude) + { + isPeriodic = true; + amplitude = 3 * IN_MILLISECONDS; + } + + void Update(AuraEffect* /*effect*/) + { + Unit* caster = GetCaster(); + if (!caster || caster->HasAnyAuras(SPELL_MOUNT_RAM_100, SPELL_MOUNT_RAM_60, SPELL_MOUNT_KODO_100, SPELL_MOUNT_KODO_60)) + return; + caster->CastSpell(caster, _spell_id, true); + } + + void Register() override + { + DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_item_brewfest_hops::CalcPeriodic, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_item_brewfest_hops::Update, EFFECT_0, SPELL_AURA_DUMMY); + } +private: + uint32 _spell_id; +}; + enum NitroBoots { SPELL_NITRO_BOOTS_SUCCESS = 54861, @@ -4338,4 +4382,5 @@ void AddSC_item_spell_scripts() RegisterSpellScript(spell_item_spell_reflectors); RegisterSpellScript(spell_item_multiphase_goggles); RegisterSpellScript(spell_item_bloodsail_admiral_hat); + RegisterSpellScript(spell_item_brewfest_hops); }