mirror of
https://github.com/silviu20092/mod-reforging
synced 2025-11-29 16:38:15 +08:00
Initialize repo
This commit is contained in:
42
README.md
Normal file
42
README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
#  AzerothCore
|
||||
|
||||
# Reforging System for AzerothCore
|
||||
|
||||
## Overview
|
||||
|
||||
First, thank you Rochet2 for inspiration. His reforging repo for TrinityCore can be found here: https://github.com/Rochet2/TrinityCore/tree/reforging_3.3.5/src/server/scripts/Custom/Reforging
|
||||
|
||||
Adds the possiblity to **reforge** items on 3.3.5a client. Reforging allows players to **decrease** a stat by a certain percentage (default is 40%) and **add** a new stat based on the difference. The allowed stats and the percentage is **configurable** via the mod .conf file.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. Due to the nature of **WOTLK** client, the new **STATS** will only be visible to the owner. **This is only visual, stats will be there nonetheless!**
|
||||
2. Upgrades will be lost (of course) when trading, sending mail, depositing to guild bank, deposit to auction.
|
||||
3. Heirlooms can't be reforged.
|
||||
4. Items with random properties (like "of the Bear", "of Intellect") can't be reforged.
|
||||
|
||||
## How to install
|
||||
|
||||
1. Clone this repository somewhere on your device.
|
||||
2. Copy mod-reforging to your AzerothCore repo modules folder.
|
||||
3. Copy reforging.patch to your AzerothCore repo (root level).
|
||||
4. Open a git command prompt in your AzerothCore repo root and use "patch -p1 < reforging.patch" (no quotes). Ignore any warnings about whitespace if any.
|
||||
5. Re-run cmake to generate the solution.
|
||||
6. Re-build your project.
|
||||
7. You should have mod_reforging.conf.dist copied in configs/modules after building, copy this to your server's binaries folder.
|
||||
8. Start the server, .sql files should automatically be imported in DB, if not, apply them manually.
|
||||
|
||||
## Ingame usage
|
||||
|
||||
Use .npc add 200004 to spawn the Master Reforger NPC. The rest is self explanatory.
|
||||
|
||||
## Some photos
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Credits
|
||||
- silviu20092
|
||||
8
mod-reforging/.editorconfig
Normal file
8
mod-reforging/.editorconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 80
|
||||
105
mod-reforging/.gitattributes
vendored
Normal file
105
mod-reforging/.gitattributes
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
## AUTO-DETECT
|
||||
## Handle line endings automatically for files detected as
|
||||
## text and leave all files detected as binary untouched.
|
||||
## This will handle all files NOT defined below.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Text
|
||||
*.conf text
|
||||
*.conf.dist text
|
||||
*.cmake text
|
||||
|
||||
## Scripts
|
||||
*.sh text
|
||||
*.fish text
|
||||
*.lua text
|
||||
|
||||
## SQL
|
||||
*.sql text
|
||||
|
||||
## C++
|
||||
*.c text
|
||||
*.cc text
|
||||
*.cxx text
|
||||
*.cpp text
|
||||
*.c++ text
|
||||
*.hpp text
|
||||
*.h text
|
||||
*.h++ text
|
||||
*.hh text
|
||||
|
||||
|
||||
## For documentation
|
||||
|
||||
# Documents
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
## DOCUMENTATION
|
||||
*.markdown text
|
||||
*.md text
|
||||
*.mdwn text
|
||||
*.mdown text
|
||||
*.mkd text
|
||||
*.mkdn text
|
||||
*.mdtxt text
|
||||
*.mdtext text
|
||||
*.txt text
|
||||
AUTHORS text
|
||||
CHANGELOG text
|
||||
CHANGES text
|
||||
CONTRIBUTING text
|
||||
COPYING text
|
||||
copyright text
|
||||
*COPYRIGHT* text
|
||||
INSTALL text
|
||||
license text
|
||||
LICENSE text
|
||||
NEWS text
|
||||
readme text
|
||||
*README* text
|
||||
TODO text
|
||||
|
||||
## GRAPHICS
|
||||
*.ai binary
|
||||
*.bmp binary
|
||||
*.eps binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.jng binary
|
||||
*.jp2 binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.jpx binary
|
||||
*.jxr binary
|
||||
*.pdf binary
|
||||
*.png binary
|
||||
*.psb binary
|
||||
*.psd binary
|
||||
*.svg text
|
||||
*.svgz binary
|
||||
*.tif binary
|
||||
*.tiff binary
|
||||
*.wbmp binary
|
||||
*.webp binary
|
||||
|
||||
|
||||
## ARCHIVES
|
||||
*.7z binary
|
||||
*.gz binary
|
||||
*.jar binary
|
||||
*.rar binary
|
||||
*.tar binary
|
||||
*.zip binary
|
||||
|
||||
## EXECUTABLES
|
||||
*.exe binary
|
||||
*.pyc binary
|
||||
72
mod-reforging/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
72
mod-reforging/.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
name: Bug report
|
||||
description: Create a bug report to help us improve.
|
||||
title: "Bug: "
|
||||
body:
|
||||
- type: textarea
|
||||
id: current
|
||||
attributes:
|
||||
label: Current Behaviour
|
||||
description: |
|
||||
Description of the problem or issue here.
|
||||
Include entries of affected creatures / items / quests / spells etc.
|
||||
If this is a crash, post the crashlog (upload to https://gist.github.com/) and include the link here.
|
||||
Never upload files! Use GIST for text and YouTube for videos!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected Behaviour
|
||||
description: |
|
||||
Tell us what should happen instead.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce the problem
|
||||
description: |
|
||||
What does someone else need to do to encounter the same bug?
|
||||
placeholder: |
|
||||
1. Step 1
|
||||
2. Step 2
|
||||
3. Step 3
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: extra
|
||||
attributes:
|
||||
label: Extra Notes
|
||||
description: |
|
||||
Do you have any extra notes that can help solve the issue that does not fit any other field?
|
||||
placeholder: |
|
||||
None
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: commit
|
||||
attributes:
|
||||
label: AC rev. hash/commit
|
||||
description: |
|
||||
Copy the result of the `.server debug` command (if you need to run it from the client get a prat addon)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
description: |
|
||||
The Operating System the Server is running on.
|
||||
i.e. Windows 11 x64, Debian 10 x64, macOS 12, Ubuntu 20.04
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: custom
|
||||
attributes:
|
||||
label: Custom changes or Modules
|
||||
description: |
|
||||
List which custom changes or modules you have applied, i.e. Eluna module, etc.
|
||||
placeholder: |
|
||||
None
|
||||
validations:
|
||||
required: false
|
||||
33
mod-reforging/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
33
mod-reforging/.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for this project
|
||||
title: "Feature: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking your time to fill out a feature request. Remember to fill out all fields including the title above.
|
||||
An issue that is not properly filled out will be closed.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe your feature request or suggestion in detail
|
||||
description: |
|
||||
A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Describe a possible solution to your feature or suggestion in detail
|
||||
description: |
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: |
|
||||
Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
11
mod-reforging/.github/workflows/core-build.yml
vendored
Normal file
11
mod-reforging/.github/workflows/core-build.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: core-build
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
uses: azerothcore/reusable-workflows/.github/workflows/core_build_modules.yml@main
|
||||
with:
|
||||
module_repo: ${{ github.event.repository.name }}
|
||||
19
mod-reforging/.github/workflows/core_codestyle.yml
vendored
Normal file
19
mod-reforging/.github/workflows/core_codestyle.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Codestyle Checks
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
check-codestyle:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
name: Check Codestyling
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Check Codestyling
|
||||
run: source ./apps/ci/ci-codestyle.sh
|
||||
48
mod-reforging/.gitignore
vendored
Normal file
48
mod-reforging/.gitignore
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
!.gitignore
|
||||
|
||||
#
|
||||
#Generic
|
||||
#
|
||||
|
||||
.directory
|
||||
.mailmap
|
||||
*.orig
|
||||
*.rej
|
||||
*.*~
|
||||
.hg/
|
||||
*.kdev*
|
||||
.DS_Store
|
||||
CMakeLists.txt.user
|
||||
*.bak
|
||||
*.patch
|
||||
*.diff
|
||||
*.REMOTE.*
|
||||
*.BACKUP.*
|
||||
*.BASE.*
|
||||
*.LOCAL.*
|
||||
|
||||
#
|
||||
# IDE & other softwares
|
||||
#
|
||||
/.settings/
|
||||
/.externalToolBuilders/*
|
||||
# exclude in all levels
|
||||
nbproject/
|
||||
.sync.ffs_db
|
||||
*.kate-swp
|
||||
|
||||
#
|
||||
# Eclipse
|
||||
#
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
tmp/
|
||||
*.tmp
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.project
|
||||
.cproject
|
||||
21
mod-reforging/LICENSE
Normal file
21
mod-reforging/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 AzerothCore
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
0
mod-reforging/apps/.gitkeep
Normal file
0
mod-reforging/apps/.gitkeep
Normal file
0
mod-reforging/apps/ci/.gitkeep
Normal file
0
mod-reforging/apps/ci/.gitkeep
Normal file
40
mod-reforging/apps/ci/ci-codestyle.sh
Normal file
40
mod-reforging/apps/ci/ci-codestyle.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Codestyle check script:"
|
||||
echo
|
||||
|
||||
declare -A singleLineRegexChecks=(
|
||||
["LOG_.+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above"
|
||||
["[[:blank:]]$"]="Remove whitespace at the end of the lines above"
|
||||
["\t"]="Replace tabs with 4 spaces in the lines above"
|
||||
)
|
||||
|
||||
for check in ${!singleLineRegexChecks[@]}; do
|
||||
echo " Checking RegEx: '${check}'"
|
||||
|
||||
if grep -P -r -I -n ${check} src; then
|
||||
echo
|
||||
echo "${singleLineRegexChecks[$check]}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
declare -A multiLineRegexChecks=(
|
||||
["LOG_[^;]+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above"
|
||||
["\n\n\n"]="Multiple blank lines detected, keep only one. Check the files above"
|
||||
)
|
||||
|
||||
for check in ${!multiLineRegexChecks[@]}; do
|
||||
echo " Checking RegEx: '${check}'"
|
||||
|
||||
if grep -Pzo -r -I ${check} src; then
|
||||
echo
|
||||
echo
|
||||
echo "${multiLineRegexChecks[$check]}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Everything looks good"
|
||||
0
mod-reforging/conf/.gitkeep
Normal file
0
mod-reforging/conf/.gitkeep
Normal file
35
mod-reforging/conf/mod_reforging.conf.dist
Normal file
35
mod-reforging/conf/mod_reforging.conf.dist
Normal file
@@ -0,0 +1,35 @@
|
||||
#
|
||||
# Credits: silviu20092
|
||||
#
|
||||
|
||||
[worldserver]
|
||||
|
||||
########################################
|
||||
# Mod Reforging configuration
|
||||
########################################
|
||||
#
|
||||
# Reforging.Enable
|
||||
# Description: Enable Reforging module
|
||||
# Default: 0 - Disabled
|
||||
# 1 - Enabled
|
||||
#
|
||||
|
||||
Reforging.Enable = 1
|
||||
|
||||
#
|
||||
# Reforging.ReforgeableStats
|
||||
# Description: Stats that can be reforged. These are usually secondary stats like spirit, hit rating, etc. These correspond
|
||||
# to ItemModType enum in ItemTemplate.h. Choose a MAXIMUM of 15 stats.
|
||||
# Default: 6,13,14,31,32,36,37 (Spirit, Dodge Rating, Parry Rating, Hit Rating, Crit Rating, Haste Rating, Expertise Rating)
|
||||
#
|
||||
|
||||
Reforging.ReforgeableStats = 6,13,14,31,32,36,37
|
||||
|
||||
#
|
||||
# Reforging.Percentage
|
||||
# Description: A number between 10 and 90 which calculates how much from the reforged stat goes into another stat.
|
||||
# Example: if you reforge 130 Spirit and this percentage is 40, then the new stat will be 52 (40% of 130)
|
||||
# Default: 40 - 40% of reforged stat goes to another (that's what Blizzard used back when reforging was a thing)
|
||||
#
|
||||
|
||||
Reforging.Percentage = 40
|
||||
0
mod-reforging/data/.gitkeep
Normal file
0
mod-reforging/data/.gitkeep
Normal file
0
mod-reforging/data/sql/db-auth/base/.gitkeep
Normal file
0
mod-reforging/data/sql/db-auth/base/.gitkeep
Normal file
0
mod-reforging/data/sql/db-auth/updates/.gitkeep
Normal file
0
mod-reforging/data/sql/db-auth/updates/.gitkeep
Normal file
0
mod-reforging/data/sql/db-characters/base/.gitkeep
Normal file
0
mod-reforging/data/sql/db-characters/base/.gitkeep
Normal file
@@ -0,0 +1,9 @@
|
||||
DROP TABLE IF EXISTS `character_reforging`;
|
||||
CREATE TABLE `character_reforging`(
|
||||
`guid` int unsigned not null,
|
||||
`item_guid` int unsigned not null,
|
||||
`stat_decrease` int unsigned not null,
|
||||
`stat_increase` int unsigned not null,
|
||||
`stat_value` int unsigned not null,
|
||||
PRIMARY KEY (`item_guid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
0
mod-reforging/data/sql/db-world/base/.gitkeep
Normal file
0
mod-reforging/data/sql/db-world/base/.gitkeep
Normal file
0
mod-reforging/data/sql/db-world/updates/.gitkeep
Normal file
0
mod-reforging/data/sql/db-world/updates/.gitkeep
Normal file
@@ -0,0 +1,7 @@
|
||||
SET @Entry = 200004;
|
||||
SET @Name = "Master";
|
||||
SET @Subname = "Reforger";
|
||||
DELETE FROM `creature_template` WHERE `entry` = @Entry;
|
||||
|
||||
INSERT INTO `creature_template` (`entry`, `modelid1`, `modelid2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `scale`, `rank`, `dmgschool`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `AIName`, `MovementType`, `HoverHeight`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`) VALUES
|
||||
(@Entry, 19646, 0, @Name, @Subname, null, 0, 80, 80, 2, 35, 1, 1, 0, 0, 2000, 0, 1, 2147483648, 7, 138936390, 0, 0, 0, '', 0, 1, 0, 0, 1, 0, 0, 'npc_reforger');
|
||||
0
mod-reforging/include.sh
Normal file
0
mod-reforging/include.sh
Normal file
25
mod-reforging/pull_request_template.md
Normal file
25
mod-reforging/pull_request_template.md
Normal file
@@ -0,0 +1,25 @@
|
||||
<!-- First of all, THANK YOU for your contribution. -->
|
||||
|
||||
## Changes Proposed:
|
||||
-
|
||||
-
|
||||
|
||||
## Issues Addressed:
|
||||
<!-- If your fix has a relating issue, link it below -->
|
||||
- Closes
|
||||
|
||||
## SOURCE:
|
||||
<!-- If you can, include a source that can strengthen your claim -->
|
||||
|
||||
## Tests Performed:
|
||||
<!-- Does it build without errors? Did you test in-game? What did you test? On which OS did you test? Describe any other tests performed -->
|
||||
-
|
||||
-
|
||||
|
||||
|
||||
## How to Test the Changes:
|
||||
<!-- Describe in a detailed step-by-step order how to test the changes -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
23
mod-reforging/src/mod_reforging_itemscript.cpp
Normal file
23
mod-reforging/src/mod_reforging_itemscript.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Credits: silviu20092
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "item_reforge.h"
|
||||
|
||||
class mod_reforging_itemscript : public AllItemScript
|
||||
{
|
||||
public:
|
||||
mod_reforging_itemscript() : AllItemScript("mod_reforging_itemscript") {}
|
||||
|
||||
bool CanItemRemove(Player* player, Item* item) override
|
||||
{
|
||||
sItemReforge->RemoveReforge(player, item, true);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_mod_reforging_itemscript()
|
||||
{
|
||||
new mod_reforging_itemscript();
|
||||
}
|
||||
17
mod-reforging/src/mod_reforging_loader.cpp
Normal file
17
mod-reforging/src/mod_reforging_loader.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Credits: silviu20092
|
||||
*/
|
||||
|
||||
void AddSC_mod_reforging_worldscript();
|
||||
void AddSC_npc_reforger();
|
||||
void AddSC_mod_reforging_playerscript();
|
||||
void AddSC_mod_reforging_itemscript();
|
||||
|
||||
void Addmod_reforgingScripts()
|
||||
{
|
||||
AddSC_mod_reforging_worldscript();
|
||||
AddSC_npc_reforger();
|
||||
AddSC_mod_reforging_playerscript();
|
||||
AddSC_mod_reforging_itemscript();
|
||||
}
|
||||
|
||||
54
mod-reforging/src/mod_reforging_playerscript.cpp
Normal file
54
mod-reforging/src/mod_reforging_playerscript.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Credits: silviu20092
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Player.h"
|
||||
#include "item_reforge.h"
|
||||
|
||||
class mod_reforging_playerscript : public PlayerScript
|
||||
{
|
||||
private:
|
||||
class SendReforgePackets : public BasicEvent
|
||||
{
|
||||
public:
|
||||
SendReforgePackets(Player* player) : player(player)
|
||||
{
|
||||
player->m_Events.AddEvent(this, player->m_Events.CalculateTime(DELAY_MS));
|
||||
}
|
||||
|
||||
bool Execute(uint64 /*e_time*/, uint32 /*p_time*/)
|
||||
{
|
||||
sItemReforge->SendItemPackets(player);
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
static constexpr uint64 DELAY_MS = 3000;
|
||||
|
||||
Player* player;
|
||||
};
|
||||
public:
|
||||
mod_reforging_playerscript() : PlayerScript("mod_reforging_playerscript") {}
|
||||
|
||||
void OnAfterMoveItemFromInventory(Player* player, Item* it, uint8 /*bag*/, uint8 /*slot*/, bool /*update*/) override
|
||||
{
|
||||
sItemReforge->RemoveReforge(player, it, true);
|
||||
}
|
||||
|
||||
void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid) override
|
||||
{
|
||||
trans->Append("DELETE FROM character_reforging WHERE guid = {}", guid);
|
||||
sItemReforge->HandleCharacterRemove(guid);
|
||||
}
|
||||
|
||||
void OnLogin(Player* player) override
|
||||
{
|
||||
new SendReforgePackets(player);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_mod_reforging_playerscript()
|
||||
{
|
||||
new mod_reforging_playerscript();
|
||||
}
|
||||
36
mod-reforging/src/mod_reforging_worldscript.cpp
Normal file
36
mod-reforging/src/mod_reforging_worldscript.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Credits: silviu20092
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "Config.h"
|
||||
#include "item_reforge.h"
|
||||
|
||||
class mod_reforging_worldscript : public WorldScript
|
||||
{
|
||||
public:
|
||||
mod_reforging_worldscript() : WorldScript("mod_reforging_worldscript") {}
|
||||
|
||||
void OnAfterConfigLoad(bool reload) override
|
||||
{
|
||||
if (reload)
|
||||
sItemReforge->HandleReload(false);
|
||||
|
||||
sItemReforge->SetEnabled(sConfigMgr->GetOption<bool>("Reforging.Enable", true));
|
||||
sItemReforge->SetReforgeableStats(sConfigMgr->GetOption<std::string>("Reforging.ReforgeableStats", ItemReforge::DefaultReforgeableStats));
|
||||
sItemReforge->SetPercentage(sConfigMgr->GetOption<float>("Reforging.Percentage", ItemReforge::PERCENTAGE_DEFAULT));
|
||||
|
||||
if (reload)
|
||||
sItemReforge->HandleReload(true);
|
||||
}
|
||||
|
||||
void OnBeforeWorldInitialized() override
|
||||
{
|
||||
sItemReforge->LoadFromDB();
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_mod_reforging_worldscript()
|
||||
{
|
||||
new mod_reforging_worldscript();
|
||||
}
|
||||
321
mod-reforging/src/npc_reforger.cpp
Normal file
321
mod-reforging/src/npc_reforger.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* Credits: silviu20092
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedGossip.h"
|
||||
#include "Creature.h"
|
||||
#include "Player.h"
|
||||
#include "StringConvert.h"
|
||||
#include "item_reforge.h"
|
||||
|
||||
class npc_reforger : public CreatureScript
|
||||
{
|
||||
private:
|
||||
std::unordered_map<uint32, ObjectGuid> itemMap;
|
||||
|
||||
bool CloseGossip(Player* player, bool retVal = true)
|
||||
{
|
||||
CloseGossipMenuFor(player);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool AddEquipmentSlotMenu(Player* player, Creature* creature)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
|
||||
const std::vector<uint32>& reforgeableStats = sItemReforge->GetReforgeableStats();
|
||||
std::ostringstream oss;
|
||||
oss << "Reforgeable stats: ";
|
||||
bool hasStats = false;
|
||||
for (uint32 i = 0; i < reforgeableStats.size(); i++)
|
||||
{
|
||||
hasStats = true;
|
||||
oss << sItemReforge->StatTypeToString(reforgeableStats[i]);
|
||||
if (i < reforgeableStats.size() - 1)
|
||||
oss << ", ";
|
||||
}
|
||||
if (!hasStats)
|
||||
oss << ItemReforge::TextRed("NONE");
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, oss.str(), GOSSIP_SENDER_MAIN + 1, EQUIPMENT_SLOT_END);
|
||||
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
||||
{
|
||||
Item* item = sItemReforge->GetItemInSlot(player, slot);
|
||||
std::ostringstream oss;
|
||||
oss << sItemReforge->GetSlotIcon(slot);
|
||||
oss << sItemReforge->GetSlotName(slot);
|
||||
|
||||
if (item == nullptr)
|
||||
oss << " [" << ItemReforge::TextRed("NO ITEM") << "]";
|
||||
else
|
||||
{
|
||||
if (sItemReforge->IsAlreadyReforged(item))
|
||||
oss << " [" << ItemReforge::TextRed("ALREADY REFORGED") << "]";
|
||||
else if (!sItemReforge->IsReforgeable(player, item))
|
||||
oss << " [" << ItemReforge::TextRed("NOT REFORGEABLE") << "]";
|
||||
else
|
||||
oss << " [" << ItemReforge::TextGreen("REFORGEABLE") << "]";
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, oss.str(), GOSSIP_SENDER_MAIN + 1, slot);
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Go Back", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
|
||||
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanAdvanceWithReforging(Player* player, const Item* item) const
|
||||
{
|
||||
if (item == nullptr)
|
||||
{
|
||||
ItemReforge::SendMessageA(player, "There is no equipped item in that slot.");
|
||||
return false;
|
||||
}
|
||||
else if (sItemReforge->IsAlreadyReforged(item))
|
||||
{
|
||||
ItemReforge::SendMessageA(player, "This item was already reforged.");
|
||||
return false;
|
||||
}
|
||||
else if (!sItemReforge->IsReforgeable(player, item))
|
||||
{
|
||||
ItemReforge::SendMessageA(player, "This item is not reforgeable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddReforgingMenu(Player* player, Creature* creature)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
|
||||
ObjectGuid itemGuid = itemMap[player->GetGUID().GetCounter()];
|
||||
Item* item = player->GetItemByGuid(itemGuid);
|
||||
if (!CanAdvanceWithReforging(player, item))
|
||||
return CloseGossip(player, false);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, ItemReforge::ItemLinkForUI(item, player), GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF + 100);
|
||||
|
||||
std::vector<_ItemStat> itemStats = sItemReforge->LoadItemStatInfo(item, true);
|
||||
for (const _ItemStat& stat : itemStats)
|
||||
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, "Reforge " + sItemReforge->StatTypeToString(stat.ItemStatType), GOSSIP_SENDER_MAIN + 2, stat.ItemStatType);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Go Back", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddReforgingStatsMenu(Player* player, Creature* creature, uint32 stat)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
|
||||
ObjectGuid itemGuid = itemMap[player->GetGUID().GetCounter()];
|
||||
Item* item = player->GetItemByGuid(itemGuid);
|
||||
if (!CanAdvanceWithReforging(player, item))
|
||||
return CloseGossip(player, false);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, ItemReforge::ItemLinkForUI(item, player), GOSSIP_SENDER_MAIN + 2, stat);
|
||||
|
||||
const std::vector<uint32>& reforgeableStats = sItemReforge->GetReforgeableStats();
|
||||
std::vector<_ItemStat> itemStats = sItemReforge->LoadItemStatInfo(item);
|
||||
const _ItemStat* toReforgeStat = sItemReforge->FindItemStat(itemStats, stat);
|
||||
if (toReforgeStat == nullptr)
|
||||
return CloseGossip(player, false);
|
||||
|
||||
uint32 taken = sItemReforge->CalculateReforgePct(toReforgeStat->ItemStatValue);
|
||||
uint32 newVal = toReforgeStat->ItemStatValue - taken;
|
||||
std::ostringstream oss;
|
||||
oss << "Will take " << ItemReforge::TextRed(Acore::ToString((uint32)sItemReforge->GetPercentage()) + "%") << " from " << sItemReforge->StatTypeToString(stat);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, oss.str(), GOSSIP_SENDER_MAIN + 2, stat);
|
||||
|
||||
oss.str("");
|
||||
oss << sItemReforge->StatTypeToString(stat) << " value after reforge: ";
|
||||
oss << ItemReforge::TextRed(Acore::ToString(newVal)) << " (-" << Acore::ToString(taken) << ")";
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, oss.str(), GOSSIP_SENDER_MAIN + 2, stat);
|
||||
|
||||
for (const uint32& rstat : reforgeableStats)
|
||||
{
|
||||
if (sItemReforge->FindItemStat(itemStats, rstat) != nullptr)
|
||||
continue;
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, ItemReforge::TextGreen("+" + Acore::ToString(taken) + " " + sItemReforge->StatTypeToString(rstat)), GOSSIP_SENDER_MAIN + 10 + stat, rstat, "Are you sure you want to reforge this item?", 0, false);
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Go Back", GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF + 100);
|
||||
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddRemoveReforgeMenu(Player* player, Creature* creature)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
|
||||
for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
|
||||
{
|
||||
Item* item = sItemReforge->GetItemInSlot(player, slot);
|
||||
std::ostringstream oss;
|
||||
oss << sItemReforge->GetSlotIcon(slot);
|
||||
oss << sItemReforge->GetSlotName(slot);
|
||||
|
||||
if (item == nullptr)
|
||||
oss << " [" << ItemReforge::TextRed("NO ITEM") << "]";
|
||||
else
|
||||
{
|
||||
if (sItemReforge->IsAlreadyReforged(item))
|
||||
oss << " [" << ItemReforge::TextGreen("REFORGED") << "]";
|
||||
else
|
||||
oss << " [" << ItemReforge::TextRed("NOT REFORGED") << "]";
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, oss.str(), GOSSIP_SENDER_MAIN + 3, slot);
|
||||
}
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Go Back", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
|
||||
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AddRemoveReforgeStatsMenu(Player* player, Creature* creature)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
|
||||
ObjectGuid itemGuid = itemMap[player->GetGUID().GetCounter()];
|
||||
Item* item = player->GetItemByGuid(itemGuid);
|
||||
if (!sItemReforge->CanRemoveReforge(item))
|
||||
return CloseGossip(player, false);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_MONEY_BAG, ItemReforge::ItemLinkForUI(item, player), GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
|
||||
|
||||
const ItemReforge::ReforgingData* reforging = sItemReforge->GetReforgingData(item);
|
||||
if (reforging == nullptr)
|
||||
return CloseGossip(player, false);
|
||||
|
||||
std::vector<_ItemStat> itemStats = sItemReforge->LoadItemStatInfo(item);
|
||||
const _ItemStat* decreasedStat = sItemReforge->FindItemStat(itemStats, reforging->stat_decrease);
|
||||
if (decreasedStat == nullptr)
|
||||
return CloseGossip(player, false);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Will restore " << sItemReforge->StatTypeToString(decreasedStat->ItemStatType) << " to " << ItemReforge::TextGreen(Acore::ToString(decreasedStat->ItemStatValue));
|
||||
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, oss.str(), GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
|
||||
|
||||
oss.str("");
|
||||
oss << ItemReforge::TextRed("-" + Acore::ToString(reforging->stat_value) + " " + sItemReforge->StatTypeToString(reforging->stat_increase));
|
||||
AddGossipItemFor(player, GOSSIP_ICON_INTERACT_1, oss.str(), GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_BATTLE, ItemReforge::TextRed("[RESTORE]"), GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF + 1, "Are you sure?", 0, false);
|
||||
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Go Back", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
|
||||
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
npc_reforger() : CreatureScript("npc_reforger") {}
|
||||
|
||||
bool OnGossipHello(Player* player, Creature* creature) override
|
||||
{
|
||||
if (!sItemReforge->GetEnabled())
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "|cffb50505NOT AVAILABLE|r", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
|
||||
else
|
||||
{
|
||||
AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Select equipment slot to reforge", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
|
||||
AddGossipItemFor(player, GOSSIP_ICON_BATTLE, "Remove reforge from items", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
|
||||
}
|
||||
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Nevermind", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
|
||||
SendGossipMenuFor(player, DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
|
||||
{
|
||||
if (!sItemReforge->GetEnabled())
|
||||
return CloseGossip(player);
|
||||
|
||||
if (sender == GOSSIP_SENDER_MAIN)
|
||||
{
|
||||
if (action == GOSSIP_ACTION_INFO_DEF)
|
||||
{
|
||||
ClearGossipMenuFor(player);
|
||||
return OnGossipHello(player, creature);
|
||||
}
|
||||
else if (action == GOSSIP_ACTION_INFO_DEF + 1)
|
||||
return AddEquipmentSlotMenu(player, creature);
|
||||
else if (action == GOSSIP_ACTION_INFO_DEF + 2)
|
||||
return CloseGossip(player);
|
||||
else if (action == GOSSIP_ACTION_INFO_DEF + 3)
|
||||
return AddRemoveReforgeMenu(player, creature);
|
||||
}
|
||||
else if (sender == GOSSIP_SENDER_MAIN + 1)
|
||||
{
|
||||
uint8 slot = (uint8)action;
|
||||
if (slot == EQUIPMENT_SLOT_END)
|
||||
return AddEquipmentSlotMenu(player, creature);
|
||||
|
||||
Item* item = sItemReforge->GetItemInSlot(player, slot);
|
||||
if (!CanAdvanceWithReforging(player, item))
|
||||
return AddEquipmentSlotMenu(player, creature);
|
||||
else
|
||||
{
|
||||
itemMap[player->GetGUID().GetCounter()] = item->GetGUID();
|
||||
return AddReforgingMenu(player, creature);
|
||||
}
|
||||
}
|
||||
else if (sender == GOSSIP_SENDER_MAIN + 2)
|
||||
{
|
||||
if (action == GOSSIP_ACTION_INFO_DEF + 100)
|
||||
return AddReforgingMenu(player, creature);
|
||||
else
|
||||
return AddReforgingStatsMenu(player, creature, action);
|
||||
}
|
||||
else if (sender == GOSSIP_SENDER_MAIN + 3)
|
||||
{
|
||||
uint8 slot = (uint8)action;
|
||||
Item* item = sItemReforge->GetItemInSlot(player, slot);
|
||||
if (!sItemReforge->CanRemoveReforge(item))
|
||||
return AddRemoveReforgeMenu(player, creature);
|
||||
else
|
||||
{
|
||||
itemMap[player->GetGUID().GetCounter()] = item->GetGUID();
|
||||
return AddRemoveReforgeStatsMenu(player, creature);
|
||||
}
|
||||
}
|
||||
else if (sender == GOSSIP_SENDER_MAIN + 4)
|
||||
{
|
||||
if (action == GOSSIP_ACTION_INFO_DEF)
|
||||
return AddRemoveReforgeStatsMenu(player, creature);
|
||||
else
|
||||
{
|
||||
if (sItemReforge->RemoveReforge(player, itemMap[player->GetGUID().GetCounter()]))
|
||||
sItemReforge->VisualFeedback(player);
|
||||
|
||||
return CloseGossip(player);
|
||||
}
|
||||
}
|
||||
else if (sender >= GOSSIP_SENDER_MAIN + 10)
|
||||
{
|
||||
uint32 decreaseStat = sender - (GOSSIP_SENDER_MAIN + 10);
|
||||
uint32 increaseStat = action;
|
||||
if (!sItemReforge->Reforge(player, itemMap[player->GetGUID().GetCounter()], decreaseStat, increaseStat))
|
||||
ItemReforge::SendMessageA(player, "Could not reforge item, try again.");
|
||||
else
|
||||
sItemReforge->VisualFeedback(player);
|
||||
|
||||
return CloseGossip(player);
|
||||
}
|
||||
|
||||
return CloseGossip(player, false);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_npc_reforger()
|
||||
{
|
||||
new npc_reforger();
|
||||
}
|
||||
BIN
pics/pic1.jpg
Normal file
BIN
pics/pic1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 687 KiB |
BIN
pics/pic2.jpg
Normal file
BIN
pics/pic2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 732 KiB |
BIN
pics/pic3.jpg
Normal file
BIN
pics/pic3.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 706 KiB |
BIN
pics/pic4.jpg
Normal file
BIN
pics/pic4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 724 KiB |
BIN
pics/pic5.jpg
Normal file
BIN
pics/pic5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 714 KiB |
908
reforging.patch
Normal file
908
reforging.patch
Normal file
@@ -0,0 +1,908 @@
|
||||
diff --git a/src/server/game/Entities/Item/item_reforge.cpp b/src/server/game/Entities/Item/item_reforge.cpp
|
||||
new file mode 100644
|
||||
index 000000000..cdbb5bd3d
|
||||
--- /dev/null
|
||||
+++ b/src/server/game/Entities/Item/item_reforge.cpp
|
||||
@@ -0,0 +1,739 @@
|
||||
+/*
|
||||
+ * Credits: silviu20092
|
||||
+ */
|
||||
+
|
||||
+#include "DatabaseEnv.h"
|
||||
+#include "Player.h"
|
||||
+#include "Chat.h"
|
||||
+#include "Tokenize.h"
|
||||
+#include "StringConvert.h"
|
||||
+#include "item_reforge.h"
|
||||
+
|
||||
+ItemReforge::ItemReforge()
|
||||
+{
|
||||
+ enabled = true;
|
||||
+ percentage = PERCENTAGE_DEFAULT;
|
||||
+}
|
||||
+
|
||||
+ItemReforge::~ItemReforge() {}
|
||||
+
|
||||
+/*static*/ ItemReforge* ItemReforge::instance()
|
||||
+{
|
||||
+ static ItemReforge instance;
|
||||
+ return &instance;
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::SetEnabled(bool value)
|
||||
+{
|
||||
+ enabled = value;
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::GetEnabled() const
|
||||
+{
|
||||
+ return enabled;
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::SetReforgeableStats(const std::string& stats)
|
||||
+{
|
||||
+ reforgeableStats.clear();
|
||||
+ std::vector<std::string_view> tokenized = Acore::Tokenize(stats, ',', false);
|
||||
+ if (tokenized.size() <= MAX_REFORGEABLE_STATS)
|
||||
+ {
|
||||
+ std::transform(tokenized.begin(), tokenized.end(), std::back_inserter(reforgeableStats),
|
||||
+ [](const std::string_view& str) { return *Acore::StringTo<uint32>(str); });
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::IsReforgeableStat(uint32 stat) const
|
||||
+{
|
||||
+ std::vector<uint32>::const_iterator citer = std::find_if(reforgeableStats.begin(), reforgeableStats.end(), [&](const uint32& s) { return s == stat; });
|
||||
+ return citer != reforgeableStats.end();
|
||||
+}
|
||||
+
|
||||
+const std::vector<uint32>& ItemReforge::GetReforgeableStats() const
|
||||
+{
|
||||
+ return reforgeableStats;
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::SetPercentage(float value)
|
||||
+{
|
||||
+ if (value < PERCENTAGE_MIN || value > PERCENTAGE_MAX)
|
||||
+ percentage = PERCENTAGE_DEFAULT;
|
||||
+ else
|
||||
+ percentage = value;
|
||||
+}
|
||||
+
|
||||
+float ItemReforge::GetPercentage() const
|
||||
+{
|
||||
+ return percentage;
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::CleanupDB() const
|
||||
+{
|
||||
+ CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
+ CharacterDatabase.DirectExecute("DELETE FROM character_reforging WHERE guid NOT IN (SELECT guid FROM characters)");
|
||||
+ CharacterDatabase.DirectExecute("DELETE FROM character_reforging WHERE item_guid NOT IN (SELECT guid FROM item_instance)");
|
||||
+ CharacterDatabase.DirectCommitTransaction(trans);
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::LoadFromDB()
|
||||
+{
|
||||
+ reforgingDataMap.clear();
|
||||
+
|
||||
+ CleanupDB();
|
||||
+
|
||||
+ QueryResult result = CharacterDatabase.Query("SELECT guid, item_guid, stat_decrease, stat_increase, stat_value FROM character_reforging");
|
||||
+ if (!result)
|
||||
+ return;
|
||||
+
|
||||
+ do
|
||||
+ {
|
||||
+ Field* fields = result->Fetch();
|
||||
+
|
||||
+ ReforgingData reforgingData;
|
||||
+ reforgingData.guid = fields[0].Get<uint32>();
|
||||
+ reforgingData.item_guid = fields[1].Get<uint32>();
|
||||
+ reforgingData.stat_decrease = fields[2].Get<uint32>();
|
||||
+ reforgingData.stat_increase = fields[3].Get<uint32>();
|
||||
+ reforgingData.stat_value = fields[4].Get<uint32>();
|
||||
+ reforgingDataMap[reforgingData.item_guid] = reforgingData;
|
||||
+ } while (result->NextRow());
|
||||
+}
|
||||
+
|
||||
+std::string ItemReforge::GetSlotIcon(uint8 slot, uint32 width, uint32 height, int x, int y) const
|
||||
+{
|
||||
+ std::ostringstream ss;
|
||||
+ ss << "|TInterface/PaperDoll/";
|
||||
+ switch (slot)
|
||||
+ {
|
||||
+ case EQUIPMENT_SLOT_HEAD:
|
||||
+ ss << "UI-PaperDoll-Slot-Head";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_NECK:
|
||||
+ ss << "UI-PaperDoll-Slot-Neck";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_SHOULDERS:
|
||||
+ ss << "UI-PaperDoll-Slot-Shoulder";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_BODY:
|
||||
+ ss << "UI-PaperDoll-Slot-Shirt";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_CHEST:
|
||||
+ ss << "UI-PaperDoll-Slot-Chest";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_WAIST:
|
||||
+ ss << "UI-PaperDoll-Slot-Waist";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_LEGS:
|
||||
+ ss << "UI-PaperDoll-Slot-Legs";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_FEET:
|
||||
+ ss << "UI-PaperDoll-Slot-Feet";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_WRISTS:
|
||||
+ ss << "UI-PaperDoll-Slot-Wrists";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_HANDS:
|
||||
+ ss << "UI-PaperDoll-Slot-Hands";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_FINGER1:
|
||||
+ case EQUIPMENT_SLOT_FINGER2:
|
||||
+ ss << "UI-PaperDoll-Slot-Finger";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_TRINKET1:
|
||||
+ case EQUIPMENT_SLOT_TRINKET2:
|
||||
+ ss << "UI-PaperDoll-Slot-Trinket";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_BACK:
|
||||
+ ss << "UI-PaperDoll-Slot-Chest";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_MAINHAND:
|
||||
+ ss << "UI-PaperDoll-Slot-MainHand";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_OFFHAND:
|
||||
+ ss << "UI-PaperDoll-Slot-SecondaryHand";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_RANGED:
|
||||
+ ss << "UI-PaperDoll-Slot-Ranged";
|
||||
+ break;
|
||||
+ case EQUIPMENT_SLOT_TABARD:
|
||||
+ ss << "UI-PaperDoll-Slot-Tabard";
|
||||
+ break;
|
||||
+ default:
|
||||
+ ss << "UI-Backpack-EmptySlot";
|
||||
+ break;
|
||||
+ }
|
||||
+ ss << ":" << width << ":" << height << ":" << x << ":" << y << "|t";
|
||||
+ return ss.str();
|
||||
+}
|
||||
+
|
||||
+std::string ItemReforge::GetSlotName(uint8 slot) const
|
||||
+{
|
||||
+ switch (slot)
|
||||
+ {
|
||||
+ case EQUIPMENT_SLOT_HEAD:
|
||||
+ return "Head";
|
||||
+ case EQUIPMENT_SLOT_NECK:
|
||||
+ return "Neck";
|
||||
+ case EQUIPMENT_SLOT_SHOULDERS:
|
||||
+ return "Shoulders";
|
||||
+ case EQUIPMENT_SLOT_BODY:
|
||||
+ return "Shirt";
|
||||
+ case EQUIPMENT_SLOT_CHEST:
|
||||
+ return "Chest";
|
||||
+ case EQUIPMENT_SLOT_WAIST:
|
||||
+ return "Waist";
|
||||
+ case EQUIPMENT_SLOT_LEGS:
|
||||
+ return "Legs";
|
||||
+ case EQUIPMENT_SLOT_FEET:
|
||||
+ return "Feet";
|
||||
+ case EQUIPMENT_SLOT_WRISTS:
|
||||
+ return "Wrists";
|
||||
+ case EQUIPMENT_SLOT_HANDS:
|
||||
+ return "Hands";
|
||||
+ case EQUIPMENT_SLOT_FINGER1:
|
||||
+ return "Finger 1";
|
||||
+ case EQUIPMENT_SLOT_FINGER2:
|
||||
+ return "Finger 2";
|
||||
+ case EQUIPMENT_SLOT_TRINKET1:
|
||||
+ return "Trinket 1";
|
||||
+ case EQUIPMENT_SLOT_TRINKET2:
|
||||
+ return "Trinket 2";
|
||||
+ case EQUIPMENT_SLOT_BACK:
|
||||
+ return "Back";
|
||||
+ case EQUIPMENT_SLOT_MAINHAND:
|
||||
+ return "Main Hand";
|
||||
+ case EQUIPMENT_SLOT_OFFHAND:
|
||||
+ return "Off Hand";
|
||||
+ case EQUIPMENT_SLOT_RANGED:
|
||||
+ return "Ranged";
|
||||
+ case EQUIPMENT_SLOT_TABARD:
|
||||
+ return "Tabard";
|
||||
+ default:
|
||||
+ return "Unknown";
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+std::string ItemReforge::StatTypeToString(uint32 statType) const
|
||||
+{
|
||||
+ static std::unordered_map<uint32, std::string> statTypeToStrMap = {
|
||||
+ {ITEM_MOD_MANA, "Mana"}, {ITEM_MOD_HEALTH, "Health"}, {ITEM_MOD_AGILITY, "Agility"},
|
||||
+ {ITEM_MOD_STRENGTH, "Strength"}, {ITEM_MOD_INTELLECT, "Intellect"}, {ITEM_MOD_SPIRIT, "Spirit"},
|
||||
+ {ITEM_MOD_STAMINA, "Stamina"}, {ITEM_MOD_DEFENSE_SKILL_RATING, "Defense Rating"}, {ITEM_MOD_DODGE_RATING, "Dodge Rating"},
|
||||
+ {ITEM_MOD_PARRY_RATING, "Parry Rating"}, {ITEM_MOD_BLOCK_RATING, "Block Rating"}, {ITEM_MOD_HIT_MELEE_RATING, "Melee Hit Rating"},
|
||||
+ {ITEM_MOD_HIT_RANGED_RATING, "Ranged Hit Rating"}, {ITEM_MOD_HIT_SPELL_RATING, "Spell Hit Rating"}, {ITEM_MOD_CRIT_MELEE_RATING, "Melee Crit Rating"},
|
||||
+ {ITEM_MOD_CRIT_RANGED_RATING, "Ranged Crit Rating"}, {ITEM_MOD_CRIT_SPELL_RATING, "Spell Crit Rating"}, {ITEM_MOD_HIT_TAKEN_MELEE_RATING, "Melee Hit Taken Rating"},
|
||||
+ {ITEM_MOD_HIT_TAKEN_RANGED_RATING, "Ranged Hit Taken Rating"}, {ITEM_MOD_HIT_TAKEN_SPELL_RATING, "Spell Hit Taken Rating"}, {ITEM_MOD_CRIT_TAKEN_MELEE_RATING, "Melee Crit Taken Rating"},
|
||||
+ {ITEM_MOD_CRIT_TAKEN_RANGED_RATING, "Ranged Crit Taken Rating"}, {ITEM_MOD_CRIT_TAKEN_SPELL_RATING, "Spell Crit Taken Rating"}, {ITEM_MOD_HASTE_MELEE_RATING, "Melee Haste Rating"},
|
||||
+ {ITEM_MOD_HASTE_RANGED_RATING, "Ranged Haste Rating"}, {ITEM_MOD_HASTE_SPELL_RATING, "Spell Haste Rating"}, {ITEM_MOD_HIT_RATING, "Hit Rating"},
|
||||
+ {ITEM_MOD_CRIT_RATING, "Crit Rating"}, {ITEM_MOD_HIT_TAKEN_RATING, "Hit Taken Rating"}, {ITEM_MOD_CRIT_TAKEN_RATING, "Crit Taken Rating"},
|
||||
+ {ITEM_MOD_RESILIENCE_RATING, "Resilience Rating"}, {ITEM_MOD_HASTE_RATING, "Haste Rating"}, {ITEM_MOD_EXPERTISE_RATING, "Expertise"},
|
||||
+ {ITEM_MOD_ATTACK_POWER, "Attack Power"}, {ITEM_MOD_RANGED_ATTACK_POWER, "Ranged Attack Power"}, {ITEM_MOD_MANA_REGENERATION, "Mana Regen"},
|
||||
+ {ITEM_MOD_ARMOR_PENETRATION_RATING, "Armor Penetration"}, {ITEM_MOD_SPELL_POWER, "Spell Power"}, {ITEM_MOD_HEALTH_REGEN, "HP Regen"},
|
||||
+ {ITEM_MOD_SPELL_PENETRATION, "Spell Penetration"}, {ITEM_MOD_BLOCK_VALUE, "Block Value"}
|
||||
+ };
|
||||
+
|
||||
+ if (statTypeToStrMap.find(statType) != statTypeToStrMap.end())
|
||||
+ return statTypeToStrMap.at(statType);
|
||||
+
|
||||
+ return "unknown";
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::IsReforgeable(const Player* player, const Item* item) const
|
||||
+{
|
||||
+ if (!item || !item->IsEquipped())
|
||||
+ return false;
|
||||
+
|
||||
+ if (item->GetOwnerGUID() != player->GetGUID())
|
||||
+ return false;
|
||||
+
|
||||
+ if (reforgeableStats.empty())
|
||||
+ return false;
|
||||
+
|
||||
+ const ItemTemplate* proto = item->GetTemplate();
|
||||
+ if (!proto->StatsCount || proto->StatsCount >= MAX_ITEM_PROTO_STATS)
|
||||
+ return false;
|
||||
+
|
||||
+ if (proto->Quality > ITEM_QUALITY_LEGENDARY)
|
||||
+ return false;
|
||||
+
|
||||
+ if (IsAlreadyReforged(item))
|
||||
+ return false;
|
||||
+
|
||||
+ for (uint32 i = 0; i < proto->StatsCount; i++)
|
||||
+ {
|
||||
+ if (!IsReforgeableStat(proto->ItemStat[i].ItemStatType))
|
||||
+ continue;
|
||||
+ if (CalculateReforgePct(proto->ItemStat[i].ItemStatValue) >= 1)
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::IsAlreadyReforged(const Item* item) const
|
||||
+{
|
||||
+ return reforgingDataMap.find(item->GetGUID().GetCounter()) != reforgingDataMap.end();
|
||||
+}
|
||||
+
|
||||
+Item* ItemReforge::GetItemInSlot(const Player* player, uint8 slot) const
|
||||
+{
|
||||
+ return player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
+}
|
||||
+
|
||||
+uint32 ItemReforge::CalculateReforgePct(int32 value) const
|
||||
+{
|
||||
+ if (value <= 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ return (uint32)(std::floorf((float)value * (GetPercentage() / 100.0f)));
|
||||
+}
|
||||
+
|
||||
+std::vector<_ItemStat> ItemReforge::LoadItemStatInfo(const Item* item, bool onlyReforgeable) const
|
||||
+{
|
||||
+ std::vector<_ItemStat> statInfo;
|
||||
+ ItemTemplate const* proto = item->GetTemplate();
|
||||
+
|
||||
+ for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
|
||||
+ {
|
||||
+ if (i >= proto->StatsCount)
|
||||
+ continue;
|
||||
+
|
||||
+ uint32 statType = proto->ItemStat[i].ItemStatType;
|
||||
+ int32 statValue = proto->ItemStat[i].ItemStatValue;
|
||||
+ if (statValue <= 0)
|
||||
+ continue;
|
||||
+
|
||||
+ if (onlyReforgeable && !IsReforgeableStat(statType))
|
||||
+ continue;
|
||||
+
|
||||
+ _ItemStat stat;
|
||||
+ stat.ItemStatType = statType;
|
||||
+ stat.ItemStatValue = statValue;
|
||||
+ statInfo.push_back(stat);
|
||||
+ }
|
||||
+
|
||||
+ return statInfo;
|
||||
+}
|
||||
+
|
||||
+const _ItemStat* ItemReforge::FindItemStat(const std::vector<_ItemStat>& stats, uint32 statType) const
|
||||
+{
|
||||
+ std::vector<_ItemStat>::const_iterator citer = std::find_if(stats.begin(), stats.end(), [&](const _ItemStat& stat) { return stat.ItemStatType == statType; });
|
||||
+ if (citer != stats.end())
|
||||
+ return &*citer;
|
||||
+
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::Reforge(Player* player, ObjectGuid itemGuid, uint32 statDecrease, uint32 statIncrease)
|
||||
+{
|
||||
+ Item* item = player->GetItemByGuid(itemGuid);
|
||||
+ if (!IsReforgeable(player, item))
|
||||
+ return false;
|
||||
+
|
||||
+ std::vector<_ItemStat> itemStats = LoadItemStatInfo(item);
|
||||
+ const _ItemStat* decreasedStat = FindItemStat(itemStats, statDecrease);
|
||||
+ if (decreasedStat == nullptr)
|
||||
+ return false;
|
||||
+
|
||||
+ if (FindItemStat(itemStats, statIncrease) != nullptr)
|
||||
+ return false;
|
||||
+
|
||||
+ player->_ApplyItemMods(item, item->GetSlot(), false);
|
||||
+
|
||||
+ uint32 value = CalculateReforgePct(decreasedStat->ItemStatValue);
|
||||
+ ReforgingData reforgingData;
|
||||
+ reforgingData.guid = player->GetGUID().GetCounter();
|
||||
+ reforgingData.item_guid = item->GetGUID().GetCounter();
|
||||
+ reforgingData.stat_decrease = statDecrease;
|
||||
+ reforgingData.stat_increase = statIncrease;
|
||||
+ reforgingData.stat_value = value;
|
||||
+ reforgingDataMap[reforgingData.item_guid] = reforgingData;
|
||||
+
|
||||
+ player->_ApplyItemMods(item, item->GetSlot(), true);
|
||||
+
|
||||
+ CharacterDatabase.Execute("INSERT INTO character_reforging (guid, item_guid, stat_decrease, stat_increase, stat_value) VALUES ({}, {}, {}, {}, {})",
|
||||
+ reforgingData.guid, reforgingData.item_guid, statDecrease, statIncrease, value);
|
||||
+
|
||||
+ SendItemPacket(player, item);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+const ItemReforge::ReforgingData* ItemReforge::GetReforgingData(const Item* item) const
|
||||
+{
|
||||
+ if (!GetEnabled())
|
||||
+ return nullptr;
|
||||
+
|
||||
+ if (IsAlreadyReforged(item))
|
||||
+ return &reforgingDataMap.at(item->GetGUID().GetCounter());
|
||||
+
|
||||
+ return nullptr;
|
||||
+}
|
||||
+
|
||||
+std::vector<Item*> ItemReforge::GetPlayerItems(const Player* player, bool inBankAlso) const
|
||||
+{
|
||||
+ std::vector<Item*> items;
|
||||
+ for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
|
||||
+ if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
+ items.push_back(item);
|
||||
+
|
||||
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
|
||||
+ if (Bag* bag = player->GetBagByPos(i))
|
||||
+ for (uint32 j = 0; j < bag->GetBagSize(); j++)
|
||||
+ if (Item* item = player->GetItemByPos(i, j))
|
||||
+ items.push_back(item);
|
||||
+
|
||||
+ for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
|
||||
+ if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
+ items.push_back(item);
|
||||
+
|
||||
+ if (inBankAlso)
|
||||
+ {
|
||||
+ for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
|
||||
+ if (Item* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
|
||||
+ items.push_back(item);
|
||||
+
|
||||
+ for (uint8 i = BANK_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
|
||||
+ if (Bag* bag = player->GetBagByPos(i))
|
||||
+ for (uint32 j = 0; j < bag->GetBagSize(); j++)
|
||||
+ if (Item* item = player->GetItemByPos(i, j))
|
||||
+ items.push_back(item);
|
||||
+ }
|
||||
+
|
||||
+ return items;
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::CanRemoveReforge(const Item* item) const
|
||||
+{
|
||||
+ if (!item || !item->IsEquipped())
|
||||
+ return false;
|
||||
+
|
||||
+ return IsAlreadyReforged(item);
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::RemoveReforge(Player* player, ObjectGuid itemGuid)
|
||||
+{
|
||||
+ return RemoveReforge(player, player->GetItemByGuid(itemGuid));
|
||||
+}
|
||||
+
|
||||
+bool ItemReforge::RemoveReforge(Player* player, Item* item, bool force)
|
||||
+{
|
||||
+ if (!force && !CanRemoveReforge(item))
|
||||
+ return false;
|
||||
+
|
||||
+ player->_ApplyItemMods(item, item->GetSlot(), false);
|
||||
+ reforgingDataMap.erase(item->GetGUID().GetCounter());
|
||||
+ player->_ApplyItemMods(item, item->GetSlot(), true);
|
||||
+
|
||||
+ CharacterDatabase.Execute("DELETE FROM character_reforging WHERE item_guid = {}", item->GetGUID().GetCounter());
|
||||
+
|
||||
+ SendItemPacket(player, item);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::VisualFeedback(Player* player)
|
||||
+{
|
||||
+ player->CastSpell(player, VISUAL_FEEDBACK_SPELL_ID, true);
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::HandleCharacterRemove(uint32 guid)
|
||||
+{
|
||||
+ for (auto it = reforgingDataMap.begin(); it != reforgingDataMap.end(); )
|
||||
+ {
|
||||
+ if (it->second.guid == guid)
|
||||
+ it = reforgingDataMap.erase(it);
|
||||
+ else
|
||||
+ ++it;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::SendItemPacket(Player* player, const Item* item) const
|
||||
+{
|
||||
+ ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item->GetEntry());
|
||||
+ std::string Name = pProto->Name1;
|
||||
+ std::string Description = pProto->Description;
|
||||
+
|
||||
+ int loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
|
||||
+ if (loc_idx >= 0)
|
||||
+ {
|
||||
+ if (ItemLocale const* il = sObjectMgr->GetItemLocale(pProto->ItemId))
|
||||
+ {
|
||||
+ ObjectMgr::GetLocaleString(il->Name, loc_idx, Name);
|
||||
+ ObjectMgr::GetLocaleString(il->Description, loc_idx, Description);
|
||||
+ }
|
||||
+ }
|
||||
+ // guess size
|
||||
+ WorldPacket queryData(SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
|
||||
+ queryData << pProto->ItemId;
|
||||
+ queryData << pProto->Class;
|
||||
+ queryData << pProto->SubClass;
|
||||
+ queryData << pProto->SoundOverrideSubclass;
|
||||
+ queryData << Name;
|
||||
+ queryData << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
|
||||
+ queryData << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
|
||||
+ queryData << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
|
||||
+ queryData << pProto->DisplayInfoID;
|
||||
+ queryData << pProto->Quality;
|
||||
+ queryData << pProto->Flags;
|
||||
+ queryData << pProto->Flags2;
|
||||
+ queryData << pProto->BuyPrice;
|
||||
+ queryData << pProto->SellPrice;
|
||||
+ queryData << pProto->InventoryType;
|
||||
+ queryData << pProto->AllowableClass;
|
||||
+ queryData << pProto->AllowableRace;
|
||||
+ queryData << pProto->ItemLevel;
|
||||
+ queryData << pProto->RequiredLevel;
|
||||
+ queryData << pProto->RequiredSkill;
|
||||
+ queryData << pProto->RequiredSkillRank;
|
||||
+ queryData << pProto->RequiredSpell;
|
||||
+ queryData << pProto->RequiredHonorRank;
|
||||
+ queryData << pProto->RequiredCityRank;
|
||||
+ queryData << pProto->RequiredReputationFaction;
|
||||
+ queryData << pProto->RequiredReputationRank;
|
||||
+ queryData << int32(pProto->MaxCount);
|
||||
+ queryData << int32(pProto->Stackable);
|
||||
+ queryData << pProto->ContainerSlots;
|
||||
+ const ReforgingData* reforgingData = GetReforgingData(item);
|
||||
+ if (reforgingData == nullptr)
|
||||
+ {
|
||||
+ queryData << pProto->StatsCount;
|
||||
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
||||
+ {
|
||||
+ queryData << pProto->ItemStat[i].ItemStatType;
|
||||
+ queryData << pProto->ItemStat[i].ItemStatValue;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ queryData << pProto->StatsCount + 1;
|
||||
+ for (uint32 i = 0; i < pProto->StatsCount; ++i)
|
||||
+ {
|
||||
+ uint32 statType = pProto->ItemStat[i].ItemStatType;
|
||||
+ queryData << statType;
|
||||
+ if (reforgingData->stat_decrease == statType)
|
||||
+ queryData << pProto->ItemStat[i].ItemStatValue - reforgingData->stat_value;
|
||||
+ else
|
||||
+ queryData << pProto->ItemStat[i].ItemStatValue;
|
||||
+ }
|
||||
+
|
||||
+ queryData << reforgingData->stat_increase;
|
||||
+ queryData << (int32)reforgingData->stat_value;
|
||||
+ }
|
||||
+
|
||||
+ queryData << pProto->ScalingStatDistribution; // scaling stats distribution
|
||||
+ queryData << pProto->ScalingStatValue; // some kind of flags used to determine stat values column
|
||||
+ for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i)
|
||||
+ {
|
||||
+ queryData << pProto->Damage[i].DamageMin;
|
||||
+ queryData << pProto->Damage[i].DamageMax;
|
||||
+ queryData << pProto->Damage[i].DamageType;
|
||||
+ }
|
||||
+
|
||||
+ // resistances (7)
|
||||
+ queryData << pProto->Armor;
|
||||
+ queryData << pProto->HolyRes;
|
||||
+ queryData << pProto->FireRes;
|
||||
+ queryData << pProto->NatureRes;
|
||||
+ queryData << pProto->FrostRes;
|
||||
+ queryData << pProto->ShadowRes;
|
||||
+ queryData << pProto->ArcaneRes;
|
||||
+
|
||||
+ queryData << pProto->Delay;
|
||||
+ queryData << pProto->AmmoType;
|
||||
+ queryData << pProto->RangedModRange;
|
||||
+
|
||||
+ for (int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s)
|
||||
+ {
|
||||
+ // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
|
||||
+ // use `item_template` or if not set then only use spell cooldowns
|
||||
+ SpellInfo const* spell = sSpellMgr->GetSpellInfo(pProto->Spells[s].SpellId);
|
||||
+ if (spell)
|
||||
+ {
|
||||
+ bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
|
||||
+
|
||||
+ queryData << pProto->Spells[s].SpellId;
|
||||
+ queryData << pProto->Spells[s].SpellTrigger;
|
||||
+ queryData << int32(pProto->Spells[s].SpellCharges);
|
||||
+
|
||||
+ if (db_data)
|
||||
+ {
|
||||
+ queryData << uint32(pProto->Spells[s].SpellCooldown);
|
||||
+ queryData << uint32(pProto->Spells[s].SpellCategory);
|
||||
+ queryData << uint32(pProto->Spells[s].SpellCategoryCooldown);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ queryData << uint32(spell->RecoveryTime);
|
||||
+ queryData << uint32(spell->GetCategory());
|
||||
+ queryData << uint32(spell->CategoryRecoveryTime);
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ queryData << uint32(0);
|
||||
+ queryData << uint32(0);
|
||||
+ queryData << uint32(0);
|
||||
+ queryData << uint32(-1);
|
||||
+ queryData << uint32(0);
|
||||
+ queryData << uint32(-1);
|
||||
+ }
|
||||
+ }
|
||||
+ queryData << pProto->Bonding;
|
||||
+ queryData << Description;
|
||||
+ queryData << pProto->PageText;
|
||||
+ queryData << pProto->LanguageID;
|
||||
+ queryData << pProto->PageMaterial;
|
||||
+ queryData << pProto->StartQuest;
|
||||
+ queryData << pProto->LockID;
|
||||
+ queryData << int32(pProto->Material);
|
||||
+ queryData << pProto->Sheath;
|
||||
+ queryData << pProto->RandomProperty;
|
||||
+ queryData << pProto->RandomSuffix;
|
||||
+ queryData << pProto->Block;
|
||||
+ queryData << pProto->ItemSet;
|
||||
+ queryData << pProto->MaxDurability;
|
||||
+ queryData << pProto->Area;
|
||||
+ queryData << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
|
||||
+ queryData << pProto->BagFamily;
|
||||
+ queryData << pProto->TotemCategory;
|
||||
+ for (int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s)
|
||||
+ {
|
||||
+ queryData << pProto->Socket[s].Color;
|
||||
+ queryData << pProto->Socket[s].Content;
|
||||
+ }
|
||||
+ queryData << pProto->socketBonus;
|
||||
+ queryData << pProto->GemProperties;
|
||||
+ queryData << pProto->RequiredDisenchantSkill;
|
||||
+ queryData << pProto->ArmorDamageModifier;
|
||||
+ queryData << pProto->Duration; // added in 2.4.2.8209, duration (seconds)
|
||||
+ queryData << pProto->ItemLimitCategory; // WotLK, ItemLimitCategory
|
||||
+ queryData << pProto->HolidayId; // Holiday.dbc?
|
||||
+ player->GetSession()->SendPacket(&queryData);
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::SendItemPackets(Player* player) const
|
||||
+{
|
||||
+ std::vector<Item*> items = GetPlayerItems(player, true);
|
||||
+ std::vector<Item*>::const_iterator itr = items.begin();
|
||||
+ for (itr; itr != items.end(); ++itr)
|
||||
+ SendItemPacket(player, *itr);
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::HandleReload(Player* player, bool apply) const
|
||||
+{
|
||||
+ std::vector<Item*> playerItems = GetPlayerItems(player, true);
|
||||
+ std::vector<Item*>::iterator iter = playerItems.begin();
|
||||
+ for (iter; iter != playerItems.end(); ++iter)
|
||||
+ {
|
||||
+ Item* item = *iter;
|
||||
+ if (apply)
|
||||
+ SendItemPacket(player, item);
|
||||
+
|
||||
+ if (!item->IsEquipped())
|
||||
+ continue;
|
||||
+
|
||||
+ player->_ApplyItemMods(item, item->GetSlot(), apply);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void ItemReforge::HandleReload(bool apply) const
|
||||
+{
|
||||
+ const SessionMap& sessions = sWorld->GetAllSessions();
|
||||
+ SessionMap::const_iterator itr;
|
||||
+ for (itr = sessions.begin(); itr != sessions.end(); ++itr)
|
||||
+ if (itr->second && itr->second->GetPlayer() && itr->second->GetPlayer()->IsInWorld())
|
||||
+ HandleReload(itr->second->GetPlayer(), apply);
|
||||
+}
|
||||
+
|
||||
+/*static*/ void ItemReforge::SendMessage(Player* player, const std::string& message)
|
||||
+{
|
||||
+ ChatHandler(player->GetSession()).SendSysMessage(message);
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::TextWithColor(const std::string& text, const std::string& color)
|
||||
+{
|
||||
+ return "|cff" + color + text + "|r";
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::TextRed(const std::string& text)
|
||||
+{
|
||||
+ return TextWithColor(text, RED_COLOR);
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::TextGreen(const std::string& text)
|
||||
+{
|
||||
+ return TextWithColor(text, GREEN_COLOR);
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::ItemIcon(const ItemTemplate* proto, uint32 width, uint32 height, int x, int y)
|
||||
+{
|
||||
+ std::ostringstream ss;
|
||||
+ ss << "|TInterface";
|
||||
+ const ItemDisplayInfoEntry* dispInfo = nullptr;
|
||||
+ if (proto)
|
||||
+ {
|
||||
+ dispInfo = sItemDisplayInfoStore.LookupEntry(proto->DisplayInfoID);
|
||||
+ if (dispInfo)
|
||||
+ ss << "/ICONS/" << dispInfo->inventoryIcon;
|
||||
+ }
|
||||
+ if (!dispInfo)
|
||||
+ ss << "/InventoryItems/WoWUnknownItem01";
|
||||
+ ss << ":" << width << ":" << height << ":" << x << ":" << y << "|t";
|
||||
+ return ss.str();
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::ItemNameWithLocale(const Player* player, const ItemTemplate* itemTemplate, int32 randomPropertyId)
|
||||
+{
|
||||
+ LocaleConstant loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
|
||||
+ std::string name = itemTemplate->Name1;
|
||||
+ if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemTemplate->ItemId))
|
||||
+ ObjectMgr::GetLocaleString(il->Name, loc_idx, name);
|
||||
+
|
||||
+ std::array<char const*, 16> const* suffix = nullptr;
|
||||
+ if (randomPropertyId < 0)
|
||||
+ {
|
||||
+ if (const ItemRandomSuffixEntry* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-randomPropertyId))
|
||||
+ suffix = &itemRandEntry->Name;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (const ItemRandomPropertiesEntry* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(randomPropertyId))
|
||||
+ suffix = &itemRandEntry->Name;
|
||||
+ }
|
||||
+ if (suffix)
|
||||
+ {
|
||||
+ std::string_view test((*suffix)[(name != itemTemplate->Name1) ? loc_idx : DEFAULT_LOCALE]);
|
||||
+ if (!test.empty())
|
||||
+ {
|
||||
+ name += ' ';
|
||||
+ name += test;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return name;
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::ItemLink(const Player* player, const ItemTemplate* itemTemplate, int32 randomPropertyId)
|
||||
+{
|
||||
+ std::stringstream oss;
|
||||
+ oss << "|c";
|
||||
+ oss << std::hex << ItemQualityColors[itemTemplate->Quality] << std::dec;
|
||||
+ oss << "|Hitem:";
|
||||
+ oss << itemTemplate->ItemId;
|
||||
+ oss << ":0:0:0:0:0:0:0:0:0|h[";
|
||||
+ oss << ItemNameWithLocale(player, itemTemplate, randomPropertyId);
|
||||
+ oss << "]|h|r";
|
||||
+
|
||||
+ return oss.str();
|
||||
+}
|
||||
+
|
||||
+/*static*/ std::string ItemReforge::ItemLinkForUI(const Item* item, const Player* player)
|
||||
+{
|
||||
+ const ItemTemplate* proto = item->GetTemplate();
|
||||
+ std::ostringstream oss;
|
||||
+ oss << ItemIcon(proto);
|
||||
+ oss << ItemLink(player, proto, item->GetItemRandomPropertyId());
|
||||
+ return oss.str();
|
||||
+}
|
||||
diff --git a/src/server/game/Entities/Item/item_reforge.h b/src/server/game/Entities/Item/item_reforge.h
|
||||
new file mode 100644
|
||||
index 000000000..609fb090f
|
||||
--- /dev/null
|
||||
+++ b/src/server/game/Entities/Item/item_reforge.h
|
||||
@@ -0,0 +1,94 @@
|
||||
+/*
|
||||
+ * Credits: silviu20092
|
||||
+ */
|
||||
+
|
||||
+#ifndef _ITEM_REFORGE_H_
|
||||
+#define _ITEM_REFORGE_H_
|
||||
+
|
||||
+#include "Define.h"
|
||||
+
|
||||
+class ItemReforge
|
||||
+{
|
||||
+public:
|
||||
+ struct ReforgingData
|
||||
+ {
|
||||
+ uint32 guid;
|
||||
+ uint32 item_guid;
|
||||
+ uint32 stat_decrease;
|
||||
+ uint32 stat_increase;
|
||||
+ uint32 stat_value;
|
||||
+ };
|
||||
+private:
|
||||
+ static constexpr float PERCENTAGE_MIN = 10.0f;
|
||||
+ static constexpr float PERCENTAGE_MAX = 90.0f;
|
||||
+ static constexpr const char* RED_COLOR = "b50505";
|
||||
+ static constexpr const char* GREEN_COLOR = "056e3a";
|
||||
+ static constexpr uint32 MAX_REFORGEABLE_STATS = 15;
|
||||
+
|
||||
+ bool enabled;
|
||||
+ std::vector<uint32> reforgeableStats;
|
||||
+ float percentage;
|
||||
+
|
||||
+ ItemReforge();
|
||||
+ ~ItemReforge();
|
||||
+
|
||||
+ typedef std::unordered_map<uint32, ReforgingData> ReforgingDataContainer;
|
||||
+
|
||||
+ ReforgingDataContainer reforgingDataMap;
|
||||
+
|
||||
+ void CleanupDB() const;
|
||||
+
|
||||
+ static std::string TextWithColor(const std::string& text, const std::string& color);
|
||||
+public:
|
||||
+ static constexpr const char* DefaultReforgeableStats = "6,13,14,31,32,36,37";
|
||||
+ static constexpr float PERCENTAGE_DEFAULT = 40.0f;
|
||||
+ static constexpr int VISUAL_FEEDBACK_SPELL_ID = 46331;
|
||||
+
|
||||
+ static ItemReforge* instance();
|
||||
+
|
||||
+ void SetEnabled(bool value);
|
||||
+ bool GetEnabled() const;
|
||||
+ void SetReforgeableStats(const std::string& stats);
|
||||
+ bool IsReforgeableStat(uint32 stat) const;
|
||||
+ const std::vector<uint32>& GetReforgeableStats() const;
|
||||
+ void SetPercentage(float value);
|
||||
+ float GetPercentage() const;
|
||||
+
|
||||
+ void LoadFromDB();
|
||||
+
|
||||
+ std::string GetSlotIcon(uint8 slot, uint32 width = 30, uint32 height = 30, int x = 0, int y = 0) const;
|
||||
+ std::string GetSlotName(uint8 slot) const;
|
||||
+ std::string StatTypeToString(uint32 statType) const;
|
||||
+
|
||||
+ bool IsReforgeable(const Player* player, const Item* item) const;
|
||||
+ bool IsAlreadyReforged(const Item* item) const;
|
||||
+ Item* GetItemInSlot(const Player* player, uint8 slot) const;
|
||||
+ uint32 CalculateReforgePct(int32 value) const;
|
||||
+ std::vector<_ItemStat> LoadItemStatInfo(const Item* item, bool onlyReforgeable = false) const;
|
||||
+ const _ItemStat* FindItemStat(const std::vector<_ItemStat>& stats, uint32 statType) const;
|
||||
+
|
||||
+ bool Reforge(Player* player, ObjectGuid itemGuid, uint32 statDecrease, uint32 statIncrease);
|
||||
+ void SendItemPacket(Player* player, const Item* item) const;
|
||||
+ void SendItemPackets(Player* player) const;
|
||||
+ void HandleReload(Player* player, bool apply) const;
|
||||
+ void HandleReload(bool apply) const;
|
||||
+ const ReforgingData* GetReforgingData(const Item* item) const;
|
||||
+ std::vector<Item*> GetPlayerItems(const Player* player, bool inBankAlso) const;
|
||||
+ bool CanRemoveReforge(const Item* item) const;
|
||||
+ bool RemoveReforge(Player* player, ObjectGuid itemGuid);
|
||||
+ bool RemoveReforge(Player* player, Item* item, bool force = false);
|
||||
+ void VisualFeedback(Player* player);
|
||||
+ void HandleCharacterRemove(uint32 guid);
|
||||
+
|
||||
+ static void SendMessage(Player* player, const std::string& message);
|
||||
+ static std::string TextRed(const std::string& text);
|
||||
+ static std::string TextGreen(const std::string& text);
|
||||
+ static std::string ItemIcon(const ItemTemplate* proto, uint32 width = 30, uint32 height = 30, int x = 0, int y = 0);
|
||||
+ static std::string ItemNameWithLocale(const Player* player, const ItemTemplate* itemTemplate, int32 randomPropertyId);
|
||||
+ static std::string ItemLink(const Player* player, const ItemTemplate* itemTemplate, int32 randomPropertyId);
|
||||
+ static std::string ItemLinkForUI(const Item* item, const Player* player);
|
||||
+};
|
||||
+
|
||||
+#define sItemReforge ItemReforge::instance()
|
||||
+
|
||||
+#endif
|
||||
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
|
||||
index 10140ff94..a5c3f34c1 100644
|
||||
--- a/src/server/game/Entities/Player/Player.cpp
|
||||
+++ b/src/server/game/Entities/Player/Player.cpp
|
||||
@@ -91,6 +91,8 @@
|
||||
// see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766
|
||||
#include "GridNotifiersImpl.h"
|
||||
|
||||
+#include "item_reforge.h"
|
||||
+
|
||||
enum CharacterFlags
|
||||
{
|
||||
CHARACTER_FLAG_NONE = 0x00000000,
|
||||
@@ -6553,6 +6555,15 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
|
||||
if (only_level_scale && !ssv)
|
||||
return;
|
||||
|
||||
+ uint32 statCount = proto->StatsCount;
|
||||
+ const ItemReforge::ReforgingData* reforging = nullptr;
|
||||
+ if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
+ {
|
||||
+ reforging = sItemReforge->GetReforgingData(item);
|
||||
+ if (reforging != nullptr)
|
||||
+ statCount++;
|
||||
+ }
|
||||
+
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
|
||||
{
|
||||
uint32 statType = 0;
|
||||
@@ -6579,11 +6590,30 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply
|
||||
}
|
||||
else
|
||||
{
|
||||
- if (i >= proto->StatsCount)
|
||||
+ if (i >= statCount)
|
||||
continue;
|
||||
|
||||
- statType = proto->ItemStat[i].ItemStatType;
|
||||
- val = proto->ItemStat[i].ItemStatValue;
|
||||
+ if (reforging == nullptr)
|
||||
+ {
|
||||
+ statType = proto->ItemStat[i].ItemStatType;
|
||||
+ val = proto->ItemStat[i].ItemStatValue;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (i == statCount - 1)
|
||||
+ {
|
||||
+ statType = reforging->stat_increase;
|
||||
+ val = reforging->stat_value;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ statType = proto->ItemStat[i].ItemStatType;
|
||||
+ val = proto->ItemStat[i].ItemStatValue;
|
||||
+
|
||||
+ if (statType == reforging->stat_decrease)
|
||||
+ val -= reforging->stat_value;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
if (val == 0)
|
||||
Reference in New Issue
Block a user