Compare commits
13 Commits
f262839c7c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4eb59b7245 | |||
| 737d7c756d | |||
| 58dcb622f3 | |||
| faf8813b11 | |||
| e1c6d2a0b7 | |||
| a5a10166d9 | |||
| 4d4463ca0b | |||
| 38dffee5f6 | |||
| 4d8a2a20e1 | |||
| 7a32675298 | |||
| e7f0ce052b | |||
| b49efcea0d | |||
| 6516b32fc6 |
@@ -108,3 +108,4 @@ local.properties
|
|||||||
# !modules/yourmodule
|
# !modules/yourmodule
|
||||||
#
|
#
|
||||||
# ==================
|
# ==================
|
||||||
|
.claude
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
-- MySQL dump 10.13 Distrib 8.4.3, for Win64 (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: localhost Database: acore_characters
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 8.4.3
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `mod_gamemodes_characters`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `mod_gamemodes_characters`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!50503 SET character_set_client = utf8mb4 */;
|
||||||
|
CREATE TABLE `mod_gamemodes_characters` (
|
||||||
|
`guid` int unsigned NOT NULL,
|
||||||
|
`game_mode` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '0 = NORMAL, 1 = TRAITOR',
|
||||||
|
PRIMARY KEY (`guid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Character Game Modes';
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mod_gamemodes_characters`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mod_gamemodes_characters` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mod_gamemodes_characters` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mod_gamemodes_characters` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
+1715993
File diff suppressed because it is too large
Load Diff
@@ -235,6 +235,15 @@ import_aoe_loot_sql() {
|
|||||||
import_sql_file "acore_world" "$aoe_loot_sql_file"
|
import_sql_file "acore_world" "$aoe_loot_sql_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure_server_motd() {
|
||||||
|
log "configuring MoonWell login message"
|
||||||
|
mysql_exec "acore_auth" "
|
||||||
|
REPLACE INTO motd (realmid, text)
|
||||||
|
VALUES (-1, 'Добро пожаловать на сервер MoonWell! Приятной игры!');
|
||||||
|
DELETE FROM motd_localized WHERE realmid = -1;
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
import_encounter_journal_sql() {
|
import_encounter_journal_sql() {
|
||||||
local schema_file="$ROOT_DIR/modules/mod-encounter-journal/data/sql/db-world/base/01_encounter_journal_schema.sql"
|
local schema_file="$ROOT_DIR/modules/mod-encounter-journal/data/sql/db-world/base/01_encounter_journal_schema.sql"
|
||||||
local sample_file="$ROOT_DIR/modules/mod-encounter-journal/data/sql/db-world/base/02_encounter_journal_sample.sql"
|
local sample_file="$ROOT_DIR/modules/mod-encounter-journal/data/sql/db-world/base/02_encounter_journal_sample.sql"
|
||||||
@@ -307,5 +316,6 @@ import_mythicplus_sql
|
|||||||
import_aoe_loot_sql
|
import_aoe_loot_sql
|
||||||
import_encounter_journal_sql
|
import_encounter_journal_sql
|
||||||
import_store_sql
|
import_store_sql
|
||||||
|
configure_server_motd
|
||||||
|
|
||||||
log "custom SQL bootstrap completed"
|
log "custom SQL bootstrap completed"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
local CONFIG = {
|
local CONFIG = {
|
||||||
maxCategories = 11,
|
maxCategories = 11,
|
||||||
strings = {
|
strings = {
|
||||||
categoryAccessDenied = "You do not have access to this category!",
|
categoryAccessDenied = "У вас нет доступа к этой категории!",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ function SHOP_UI.MainFrame_Create()
|
|||||||
shopFrame.Title:SetFont("Fonts\\FRIZQT__.TTF", 14)
|
shopFrame.Title:SetFont("Fonts\\FRIZQT__.TTF", 14)
|
||||||
shopFrame.Title:SetShadowOffset(1, -1)
|
shopFrame.Title:SetShadowOffset(1, -1)
|
||||||
shopFrame.Title:SetPoint("TOP", shopFrame, "TOP", 0, -3)
|
shopFrame.Title:SetPoint("TOP", shopFrame, "TOP", 0, -3)
|
||||||
shopFrame.Title:SetText("|cffedd100Shop|r")
|
shopFrame.Title:SetText("|cffedd100Лавка Лунной Жрицы|r")
|
||||||
|
|
||||||
-- create navigation button placeholders, pass parent as arg
|
-- create navigation button placeholders, pass parent as arg
|
||||||
SHOP_UI.NavButtons_Create(shopFrame)
|
SHOP_UI.NavButtons_Create(shopFrame)
|
||||||
@@ -333,9 +333,9 @@ function SHOP_UI.OnPurchaseConfirm(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
StaticPopupDialogs["CONFIRM_STORE_PURCHASE"] = {
|
StaticPopupDialogs["CONFIRM_STORE_PURCHASE"] = {
|
||||||
text = "Are you sure you want to purchase %s?",
|
text = "Вы уверены, что хотите купить %s?",
|
||||||
button1 = "Yes",
|
button1 = "Да",
|
||||||
button2 = "No",
|
button2 = "Нет",
|
||||||
OnAccept = function(self, data)
|
OnAccept = function(self, data)
|
||||||
SHOP_UI.OnPurchaseConfirm(data)
|
SHOP_UI.OnPurchaseConfirm(data)
|
||||||
end,
|
end,
|
||||||
@@ -454,7 +454,7 @@ function SHOP_UI.ServiceBoxes_Create(parent)
|
|||||||
service.buyButton.ButtonText = service.buyButton:CreateFontString()
|
service.buyButton.ButtonText = service.buyButton:CreateFontString()
|
||||||
service.buyButton.ButtonText:SetFont("Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
|
service.buyButton.ButtonText:SetFont("Fonts\\FRIZQT__.TTF", 11, "OUTLINE")
|
||||||
service.buyButton.ButtonText:SetPoint("CENTER", service.buyButton, 0, 0)
|
service.buyButton.ButtonText:SetPoint("CENTER", service.buyButton, 0, 0)
|
||||||
service.buyButton.ButtonText:SetText("Buy now!")
|
service.buyButton.ButtonText:SetText("Купить")
|
||||||
|
|
||||||
service.buyButton:SetScript(
|
service.buyButton:SetScript(
|
||||||
"OnClick",
|
"OnClick",
|
||||||
@@ -635,7 +635,7 @@ function SHOP_UI.ServiceBoxes_Update()
|
|||||||
|
|
||||||
-- calculate discount percentage
|
-- calculate discount percentage
|
||||||
local discountPct = math.floor(((service.Price - service.Discount) - service.Price) / service.Price * 100)
|
local discountPct = math.floor(((service.Price - service.Discount) - service.Price) / service.Price * 100)
|
||||||
service.BannerText:SetFormattedText("|cffffffffOn sale: %i%%|r", discountPct)
|
service.BannerText:SetFormattedText("|cffffffffСкидка: %i%%|r", discountPct)
|
||||||
|
|
||||||
-- if service is discounted, then show all the discount frames and override the price text. otherwise hide.
|
-- if service is discounted, then show all the discount frames and override the price text. otherwise hide.
|
||||||
if service.Discount > 1 then
|
if service.Discount > 1 then
|
||||||
@@ -1193,7 +1193,7 @@ local function ModifyGameMenuFrame()
|
|||||||
storeButton.Text:SetFont("Fonts\\FRIZQT__.TTF", 13, "OUTLINE")
|
storeButton.Text:SetFont("Fonts\\FRIZQT__.TTF", 13, "OUTLINE")
|
||||||
storeButton.Text:SetShadowOffset(1, -1)
|
storeButton.Text:SetShadowOffset(1, -1)
|
||||||
storeButton.Text:SetPoint("CENTER", storeButton, "CENTER", 0, 1)
|
storeButton.Text:SetPoint("CENTER", storeButton, "CENTER", 0, 1)
|
||||||
storeButton.Text:SetText("|cffdbe005Store");
|
storeButton.Text:SetText("|cffdbe005Лавка Лунной Жрицы");
|
||||||
|
|
||||||
-- on click open the shop frame and hide the escape menu
|
-- on click open the shop frame and hide the escape menu
|
||||||
storeButton:SetScript("OnClick", function()
|
storeButton:SetScript("OnClick", function()
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ local CONFIG = {
|
|||||||
mailSenderGUID = 1, -- GUID of the character shown as sender of purchase mails
|
mailSenderGUID = 1, -- GUID of the character shown as sender of purchase mails
|
||||||
strings = {
|
strings = {
|
||||||
-- Currency name is appended to the end of this string
|
-- Currency name is appended to the end of this string
|
||||||
insufficientFunds = "You don't have enough",
|
insufficientFunds = "Недостаточно средств на балансе",
|
||||||
-- Type is appended, ie. title, mount, pet etc.
|
-- Type is appended, ie. title, mount, pet etc.
|
||||||
alreadyKnown = "You already have this",
|
alreadyKnownMount = "Этот транспорт уже изучен",
|
||||||
tooHighLevel = "Your level is too high",
|
alreadyKnownPet = "Этот питомец уже изучен",
|
||||||
mailBody = "Thank you for your purchase!",
|
alreadyKnownTitle = "Этот титул уже получен",
|
||||||
|
tooHighLevel = "Уровень персонажа слишком высок",
|
||||||
|
mailBody = "Благодарим за покупку в Лавке Лунной Жрицы!",
|
||||||
-- The service name is prefixed to this message
|
-- The service name is prefixed to this message
|
||||||
successfulPurchase = "successfully purchased!"
|
successfulPurchase = "Покупка успешно совершена!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,11 +24,24 @@ local CONFIG = {
|
|||||||
local AIO = AIO or require("AIO") and require("Store_DataStruct")
|
local AIO = AIO or require("AIO") and require("Store_DataStruct")
|
||||||
|
|
||||||
local CURRENCY_TYPES = {
|
local CURRENCY_TYPES = {
|
||||||
[1] = "GOLD",
|
|
||||||
[2] = "ITEM_TOKEN",
|
|
||||||
[3] = "SERVER_HANDLED"
|
[3] = "SERVER_HANDLED"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local purchaseReferenceSequence = 0
|
||||||
|
|
||||||
|
local function GetAccountBalance(player)
|
||||||
|
local query = AuthDBQuery(
|
||||||
|
"SELECT CAST(`balance` AS DOUBLE) FROM `account_balances` WHERE `account_id` = " ..
|
||||||
|
player:GetAccountId() .. " LIMIT 1;"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not query then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return query:GetDouble(0)
|
||||||
|
end
|
||||||
|
|
||||||
local SHOP_UI = {
|
local SHOP_UI = {
|
||||||
serviceHandlers = {
|
serviceHandlers = {
|
||||||
[1] = "ItemHandler", -- Okay
|
[1] = "ItemHandler", -- Okay
|
||||||
@@ -55,17 +70,8 @@ function StoreHandler.UpdateCurrencies(player)
|
|||||||
local val = 0
|
local val = 0
|
||||||
local currencyTypeText = CURRENCY_TYPES[currency[KEYS.currency.currencyType]]
|
local currencyTypeText = CURRENCY_TYPES[currency[KEYS.currency.currencyType]]
|
||||||
|
|
||||||
-- Handle the different currency types
|
|
||||||
if(currencyTypeText == "GOLD") then
|
|
||||||
val = math.floor(player:GetCoinage() / 10000)
|
|
||||||
end
|
|
||||||
|
|
||||||
if(currencyTypeText == "ITEM_TOKEN") then
|
|
||||||
val = player:GetItemCount(currency[KEYS.currency.data])
|
|
||||||
end
|
|
||||||
|
|
||||||
if(currencyTypeText == "SERVER_HANDLED") then
|
if(currencyTypeText == "SERVER_HANDLED") then
|
||||||
-- Add your custom handling here for retreiving your server handled currencies
|
val = GetAccountBalance(player)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If value is larger than 10k then truncate to make sure it fits within the shop frame
|
-- If value is larger than 10k then truncate to make sure it fits within the shop frame
|
||||||
@@ -73,7 +79,9 @@ function StoreHandler.UpdateCurrencies(player)
|
|||||||
val = "9999+"
|
val = "9999+"
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(tmp, val)
|
-- Preserve the database currency id. A positional array silently breaks as
|
||||||
|
-- soon as ids have gaps or are reordered by MySQL.
|
||||||
|
tmp[currencyId] = val
|
||||||
end
|
end
|
||||||
AIO.Handle(player, "STORE_CLIENT", "UpdateCurrencies", tmp)
|
AIO.Handle(player, "STORE_CLIENT", "UpdateCurrencies", tmp)
|
||||||
end
|
end
|
||||||
@@ -99,46 +107,45 @@ function StoreHandler.Purchase(player, serviceId)
|
|||||||
player:PlayDirectSound(120, player)
|
player:PlayDirectSound(120, player)
|
||||||
|
|
||||||
-- Send success toast
|
-- Send success toast
|
||||||
player:SendAreaTriggerMessage(services[serviceId][KEYS.service.name] .. " "..CONFIG.strings.successfulPurchase)
|
player:SendAreaTriggerMessage(CONFIG.strings.successfulPurchase)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Helper functions
|
-- Helper functions
|
||||||
function SHOP_UI.DeductCurrency(player, currencyId, amount)
|
function SHOP_UI.DeductCurrency(player, currencyId, amount, serviceId)
|
||||||
local currency = GetCurrencyData()
|
local currency = GetCurrencyData()
|
||||||
|
if not currency[currencyId] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local currencyType = currency[currencyId][KEYS.currency.currencyType]
|
local currencyType = currency[currencyId][KEYS.currency.currencyType]
|
||||||
local currencyName = currency[currencyId][KEYS.currency.name]
|
if CURRENCY_TYPES[currencyType] ~= "SERVER_HANDLED" then
|
||||||
local currencyData = currency[currencyId][KEYS.currency.data]
|
|
||||||
|
|
||||||
-- Gold handling
|
|
||||||
if(CURRENCY_TYPES[currencyType] == "GOLD") then
|
|
||||||
if(player:GetCoinage() < amount * 10000) then
|
|
||||||
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.insufficientFunds.." "..currencyName.."|r")
|
|
||||||
player:PlayDirectSound(GetSoundEffect("notEnoughMoney", player:GetRace(), player:GetGender()), player)
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
player:SetCoinage(player:GetCoinage() - (amount * 10000))
|
purchaseReferenceSequence = purchaseReferenceSequence + 1
|
||||||
end
|
local reference = string.format(
|
||||||
|
"game-store:%u:%u:%u:%u:%u",
|
||||||
|
player:GetAccountId(),
|
||||||
|
player:GetGUIDLow(),
|
||||||
|
serviceId or 0,
|
||||||
|
os.time(),
|
||||||
|
purchaseReferenceSequence
|
||||||
|
)
|
||||||
|
local result = AuthDBQuery(
|
||||||
|
"SELECT `store_deduct_balance`(" .. player:GetAccountId() .. ", " .. amount .. ", '" .. reference .. "', " ..
|
||||||
|
player:GetGUIDLow() .. ", " .. (serviceId or 0) .. ") AS `balance_after`;"
|
||||||
|
)
|
||||||
|
local balanceAfter = result and result:GetDouble(0) or -1
|
||||||
|
|
||||||
-- Token handling
|
if balanceAfter < 0 then
|
||||||
if(CURRENCY_TYPES[currencyType] == "ITEM_TOKEN") then
|
player:SendAreaTriggerMessage("|cFFFF0000" .. CONFIG.strings.insufficientFunds .. "|r")
|
||||||
if not(player:HasItem(currencyData, amount)) then
|
|
||||||
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.insufficientFunds.." "..currencyName.."|r")
|
|
||||||
player:PlayDirectSound(GetSoundEffect("notEnoughMoney", player:GetRace(), player:GetGender()), player)
|
player:PlayDirectSound(GetSoundEffect("notEnoughMoney", player:GetRace(), player:GetGender()), player)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
player:RemoveItem(currencyData, amount)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Other special handlingm you have to add your own integration here.
|
|
||||||
if(CURRENCY_TYPES[currencyType] == "SERVER_HANDLED") then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -152,7 +159,7 @@ function SHOP_UI.ItemHandler(player, data)
|
|||||||
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -169,7 +176,7 @@ function SHOP_UI.ItemHandler(player, data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Send reward mail
|
-- Send reward mail
|
||||||
SendMail("Purchase of: "..data[KEYS.service.name], CONFIG.strings.mailBody, player:GetGUIDLow(), CONFIG.mailSenderGUID, 62, 0, 0, 0, unpack(items))
|
SendMail("Покупка: "..data[KEYS.service.name], CONFIG.strings.mailBody, player:GetGUIDLow(), CONFIG.mailSenderGUID, 62, 0, 0, 0, unpack(items))
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -178,7 +185,7 @@ function SHOP_UI.GoldHandler(player, data)
|
|||||||
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -206,13 +213,13 @@ function SHOP_UI.MountHandler(player, data)
|
|||||||
|
|
||||||
-- check if player already has the spells learned
|
-- check if player already has the spells learned
|
||||||
if(knownCount == rewardCount) then
|
if(knownCount == rewardCount) then
|
||||||
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnown.." mount|r")
|
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnownMount.."|r")
|
||||||
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -244,13 +251,13 @@ function SHOP_UI.PetHandler(player, data)
|
|||||||
|
|
||||||
-- check if player already has the spells learned
|
-- check if player already has the spells learned
|
||||||
if(knownCount == rewardCount) then
|
if(knownCount == rewardCount) then
|
||||||
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnown.." pet|r")
|
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnownPet.."|r")
|
||||||
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -271,7 +278,7 @@ function SHOP_UI.BuffHandler(player, data)
|
|||||||
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -292,7 +299,7 @@ function SHOP_UI.ServiceHandler(player, data)
|
|||||||
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
local currency, amount = data[KEYS.service.currency], data[KEYS.service.price] - data[KEYS.service.discount]
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -319,7 +326,7 @@ function SHOP_UI.LevelHandler(player, data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
@@ -348,13 +355,13 @@ function SHOP_UI.TitleHandler(player, data)
|
|||||||
|
|
||||||
-- Check whether or not the player already has the specified title
|
-- Check whether or not the player already has the specified title
|
||||||
if(player:HasTitle(data[KEYS.service.reward_1])) then
|
if(player:HasTitle(data[KEYS.service.reward_1])) then
|
||||||
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnown.." title|r")
|
player:SendAreaTriggerMessage("|cFFFF0000"..CONFIG.strings.alreadyKnownTitle.."|r")
|
||||||
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
player:PlayDirectSound(GetSoundEffect("cantLearn", player:GetRace(), player:GetGender()), player)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Deduct currency
|
-- Deduct currency
|
||||||
local deducted = SHOP_UI.DeductCurrency(player, currency, amount)
|
local deducted = SHOP_UI.DeductCurrency(player, currency, amount, data.ID)
|
||||||
|
|
||||||
-- If currency was not deducted from the player, abort and send message
|
-- If currency was not deducted from the player, abort and send message
|
||||||
if not(deducted) then
|
if not(deducted) then
|
||||||
|
|||||||
@@ -1,7 +1,2 @@
|
|||||||
local PLAYER_EVENT_ON_LOGIN = 3
|
-- Login greeting is managed by the native AzerothCore MOTD in
|
||||||
|
-- import-custom-sql.sh. Keep this file as a harmless Eluna loading example.
|
||||||
local function OnLogin(event, player)
|
|
||||||
player:SendBroadcastMessage("Eluna greetings you")
|
|
||||||
end
|
|
||||||
|
|
||||||
RegisterPlayerEvent(PLAYER_EVENT_ON_LOGIN, OnLogin)
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- Game-mode UI synchronization is client-native since MoonWellClient 2.0.
|
||||||
|
--
|
||||||
|
-- The authoritative mode still comes from the server in SMSG_CHAR_ENUM as
|
||||||
|
-- CHARACTER_FLAG_UNK31 (0x40000000). WarcraftXL retains that bit while WoW
|
||||||
|
-- switches from GlueXML to the in-world FrameXML Lua state. Keeping this file
|
||||||
|
-- as a no-op avoids stale deployment scripts failing on a missing path while
|
||||||
|
-- removing the timing-sensitive AIO login event completely.
|
||||||
@@ -1207,6 +1207,9 @@ public:
|
|||||||
|
|
||||||
void OnHeal(Unit* healer, Unit* receiver, uint32& gain) override
|
void OnHeal(Unit* healer, Unit* receiver, uint32& gain) override
|
||||||
{
|
{
|
||||||
|
if (!healer)
|
||||||
|
return;
|
||||||
|
|
||||||
if (healer->IsPlayer())
|
if (healer->IsPlayer())
|
||||||
sEluna->OnPlayerHeal(healer->ToPlayer(), receiver, gain);
|
sEluna->OnPlayerHeal(healer->ToPlayer(), receiver, gain);
|
||||||
|
|
||||||
@@ -1216,6 +1219,9 @@ public:
|
|||||||
|
|
||||||
void OnDamage(Unit* attacker, Unit* receiver, uint32& damage) override
|
void OnDamage(Unit* attacker, Unit* receiver, uint32& damage) override
|
||||||
{
|
{
|
||||||
|
if (!attacker)
|
||||||
|
return;
|
||||||
|
|
||||||
if (attacker->IsPlayer())
|
if (attacker->IsPlayer())
|
||||||
sEluna->OnPlayerDamage(attacker->ToPlayer(), receiver, damage);
|
sEluna->OnPlayerDamage(attacker->ToPlayer(), receiver, damage);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#
|
||||||
|
# Game Modes module configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# GameModes settings
|
||||||
|
########################################
|
||||||
|
|
||||||
|
#
|
||||||
|
# GameModes.Enable
|
||||||
|
# Description: Enable game modes system
|
||||||
|
# Default: 1 - (Enabled)
|
||||||
|
# 0 - (Disabled)
|
||||||
|
#
|
||||||
|
|
||||||
|
GameModes.Enable = 1
|
||||||
|
|
||||||
|
#
|
||||||
|
# GameModes.Traitor.Enable
|
||||||
|
# Description: Enable Traitor game mode
|
||||||
|
# Default: 1 - (Enabled)
|
||||||
|
# 0 - (Disabled)
|
||||||
|
#
|
||||||
|
|
||||||
|
GameModes.Traitor.Enable = 1
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
DROP TABLE IF EXISTS `mod_gamemodes_characters`;
|
||||||
|
CREATE TABLE `mod_gamemodes_characters` (
|
||||||
|
`guid` INT UNSIGNED NOT NULL,
|
||||||
|
`game_mode` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0 = NORMAL, 1 = TRAITOR',
|
||||||
|
PRIMARY KEY (`guid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#include "GameModes.h"
|
||||||
|
#include "DatabaseEnv.h"
|
||||||
|
#include "Log.h"
|
||||||
|
|
||||||
|
GameModeMgr* GameModeMgr::Instance()
|
||||||
|
{
|
||||||
|
static GameModeMgr instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameModeMgr::LoadFromDB()
|
||||||
|
{
|
||||||
|
uint32 oldMSTime = getMSTime();
|
||||||
|
_gameModes.clear();
|
||||||
|
|
||||||
|
QueryResult result = CharacterDatabase.Query("SELECT `guid`, `game_mode` FROM `mod_gamemodes_characters`");
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
LOG_INFO("module", ">> Loaded 0 character game modes. Table `mod_gamemodes_characters` is empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 count = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = result->Fetch();
|
||||||
|
ObjectGuid::LowType guid = fields[0].Get<uint32>();
|
||||||
|
uint8 mode = fields[1].Get<uint8>();
|
||||||
|
|
||||||
|
if (mode >= GAMEMODE_MAX)
|
||||||
|
{
|
||||||
|
LOG_ERROR("module", "mod-gamemodes: Invalid game mode {} for character {}, skipping.", mode, guid);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameModes[guid] = static_cast<GameMode>(mode);
|
||||||
|
++count;
|
||||||
|
} while (result->NextRow());
|
||||||
|
|
||||||
|
LOG_INFO("module", ">> Loaded {} character game modes in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameModeMgr::SaveGameMode(ObjectGuid::LowType guid, GameMode mode)
|
||||||
|
{
|
||||||
|
_gameModes[guid] = mode;
|
||||||
|
|
||||||
|
CharacterDatabase.Execute("REPLACE INTO `mod_gamemodes_characters` (`guid`, `game_mode`) VALUES ({}, {})",
|
||||||
|
guid, static_cast<uint8>(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameModeMgr::DeleteGameMode(ObjectGuid::LowType guid)
|
||||||
|
{
|
||||||
|
_gameModes.erase(guid);
|
||||||
|
|
||||||
|
CharacterDatabase.Execute("DELETE FROM `mod_gamemodes_characters` WHERE `guid` = {}", guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameMode GameModeMgr::GetGameMode(ObjectGuid::LowType guid) const
|
||||||
|
{
|
||||||
|
auto it = _gameModes.find(guid);
|
||||||
|
if (it != _gameModes.end())
|
||||||
|
return it->second;
|
||||||
|
return GAMEMODE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameModeMgr::HasGameMode(ObjectGuid::LowType guid, GameMode mode) const
|
||||||
|
{
|
||||||
|
return GetGameMode(guid) == mode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef MOD_GAMEMODES_H
|
||||||
|
#define MOD_GAMEMODES_H
|
||||||
|
|
||||||
|
#include "Player.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
enum GameMode : uint8
|
||||||
|
{
|
||||||
|
GAMEMODE_NORMAL = 0,
|
||||||
|
GAMEMODE_TRAITOR = 1,
|
||||||
|
GAMEMODE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
// Custom bit in SMSG_CHAR_ENUM character flags.
|
||||||
|
// Chosen as a high unused bit so the packet layout stays unchanged.
|
||||||
|
constexpr uint32 GAMEMODE_CHARACTER_FLAG_TRAITOR = 0x40000000;
|
||||||
|
|
||||||
|
class GameModeMgr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static GameModeMgr* Instance();
|
||||||
|
|
||||||
|
void LoadFromDB();
|
||||||
|
void SaveGameMode(ObjectGuid::LowType guid, GameMode mode);
|
||||||
|
void DeleteGameMode(ObjectGuid::LowType guid);
|
||||||
|
|
||||||
|
GameMode GetGameMode(ObjectGuid::LowType guid) const;
|
||||||
|
bool HasGameMode(ObjectGuid::LowType guid, GameMode mode) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameModeMgr() = default;
|
||||||
|
std::unordered_map<ObjectGuid::LowType, GameMode> _gameModes;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define sGameModeMgr GameModeMgr::Instance()
|
||||||
|
|
||||||
|
#endif // MOD_GAMEMODES_H
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
void AddSC_TraitorMode();
|
||||||
|
|
||||||
|
void Addmod_gamemodesScripts()
|
||||||
|
{
|
||||||
|
AddSC_TraitorMode();
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
#include "TraitorMode.h"
|
||||||
|
#include "GameModes.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "DBCStores.h"
|
||||||
|
#include "DatabaseEnv.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include "ObjectMgr.h"
|
||||||
|
#include "ReputationMgr.h"
|
||||||
|
#include "ScriptMgr.h"
|
||||||
|
#include "WorldPacket.h"
|
||||||
|
#include "WorldSession.h"
|
||||||
|
|
||||||
|
namespace TraitorMode
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void NormalizeFactionState(Player* player, FactionEntry const* factionEntry, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
if (!factionEntry || factionEntry->reputationListID < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FactionState* factionState = const_cast<FactionState*>(player->GetReputationMgr().GetState(factionEntry));
|
||||||
|
if (!factionState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
factionState->Flags &= ~(FACTION_FLAG_VISIBLE | FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED |
|
||||||
|
FACTION_FLAG_PEACE_FORCED | FACTION_FLAG_INACTIVE);
|
||||||
|
|
||||||
|
if (visible)
|
||||||
|
factionState->Flags |= FACTION_FLAG_VISIBLE;
|
||||||
|
else
|
||||||
|
factionState->Flags |= FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED;
|
||||||
|
|
||||||
|
if (atWar)
|
||||||
|
factionState->Flags |= FACTION_FLAG_AT_WAR;
|
||||||
|
else
|
||||||
|
factionState->Flags &= ~FACTION_FLAG_AT_WAR;
|
||||||
|
|
||||||
|
factionState->needSend = true;
|
||||||
|
factionState->needSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFactionStanding(Player* player, uint32 factionId, int32 targetDisplayed, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
|
||||||
|
if (!factionEntry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// ReputationMgr::GetBaseReputation now always uses the player's actual race
|
||||||
|
// mask, so SetOneFactionReputation is safe to call directly.
|
||||||
|
player->GetReputationMgr().SetOneFactionReputation(factionEntry, static_cast<float>(targetDisplayed), false);
|
||||||
|
NormalizeFactionState(player, factionEntry, visible, atWar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NormalizeTraitorReputationStates(Player* player)
|
||||||
|
{
|
||||||
|
TeamId originalTeam = player->GetTeamId(true);
|
||||||
|
uint32 const* newFactions = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? HORDE_REPUTATION_FACTIONS
|
||||||
|
: ALLIANCE_REPUTATION_FACTIONS;
|
||||||
|
uint32 const* oldFactions = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? ALLIANCE_REPUTATION_FACTIONS
|
||||||
|
: HORDE_REPUTATION_FACTIONS;
|
||||||
|
|
||||||
|
auto normalizeFactionAndParent = [player](uint32 factionId, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId))
|
||||||
|
{
|
||||||
|
NormalizeFactionState(player, factionEntry, visible, atWar);
|
||||||
|
|
||||||
|
if (factionEntry->team)
|
||||||
|
NormalizeFactionState(player, sFactionStore.LookupEntry(factionEntry->team), visible, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < FACTION_COUNT; ++i)
|
||||||
|
{
|
||||||
|
normalizeFactionAndParent(newFactions[i], true, false);
|
||||||
|
normalizeFactionAndParent(oldFactions[i], false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendFactionVisiblePacket(Player* player, FactionEntry const* factionEntry)
|
||||||
|
{
|
||||||
|
if (!factionEntry || factionEntry->reputationListID < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
|
||||||
|
data << uint32(factionEntry->reputationListID);
|
||||||
|
player->SendDirectMessage(&data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendTraitorFactionVisibility(Player* player)
|
||||||
|
{
|
||||||
|
TeamId originalTeam = player->GetTeamId(true);
|
||||||
|
uint32 const* newFactions = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? HORDE_REPUTATION_FACTIONS
|
||||||
|
: ALLIANCE_REPUTATION_FACTIONS;
|
||||||
|
auto sendFactionAndParent = [player](uint32 factionId)
|
||||||
|
{
|
||||||
|
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId))
|
||||||
|
{
|
||||||
|
SendFactionVisiblePacket(player, factionEntry);
|
||||||
|
|
||||||
|
if (factionEntry->team)
|
||||||
|
SendFactionVisiblePacket(player, sFactionStore.LookupEntry(factionEntry->team));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < FACTION_COUNT; ++i)
|
||||||
|
sendFactionAndParent(newFactions[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyFactionSwap(Player* player)
|
||||||
|
{
|
||||||
|
TeamId originalTeam = player->GetTeamId(true);
|
||||||
|
TeamId newTeam = (originalTeam == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE;
|
||||||
|
|
||||||
|
player->setTeamId(newTeam);
|
||||||
|
|
||||||
|
// Derive faction template from the canonical race for the new team (same
|
||||||
|
// logic as SetFactionForRace), so we never rely on hardcoded DBC IDs.
|
||||||
|
uint8 representativeRace = (newTeam == TEAM_HORDE) ? HORDE_DEFAULT_RACE : ALLIANCE_DEFAULT_RACE;
|
||||||
|
if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(representativeRace))
|
||||||
|
player->SetFaction(rEntry->FactionID);
|
||||||
|
|
||||||
|
LOG_INFO("module", "mod-gamemodes: ApplyFactionSwap for {} (GUID: {}), original team = {}, new team = {}, factionTemplate = {}",
|
||||||
|
player->GetName(), player->GetGUID().GetCounter(),
|
||||||
|
(originalTeam == TEAM_ALLIANCE ? "Alliance" : "Horde"),
|
||||||
|
(newTeam == TEAM_ALLIANCE ? "Alliance" : "Horde"),
|
||||||
|
player->GetFaction());
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitTraitorReputations(Player* player)
|
||||||
|
{
|
||||||
|
TeamId originalTeam = player->GetTeamId(true);
|
||||||
|
|
||||||
|
// New faction reputations -> Neutral (standing = 0)
|
||||||
|
const uint32* newFactions = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? HORDE_REPUTATION_FACTIONS
|
||||||
|
: ALLIANCE_REPUTATION_FACTIONS;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < FACTION_COUNT; ++i)
|
||||||
|
SetFactionStanding(player, newFactions[i], 0, true, false);
|
||||||
|
|
||||||
|
// Old faction reputations -> Hostile (standing = -6000)
|
||||||
|
const uint32* oldFactions = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? ALLIANCE_REPUTATION_FACTIONS
|
||||||
|
: HORDE_REPUTATION_FACTIONS;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < FACTION_COUNT; ++i)
|
||||||
|
SetFactionStanding(player, oldFactions[i], -6000, false, true);
|
||||||
|
|
||||||
|
LOG_INFO("module", "mod-gamemodes: InitTraitorReputations for {} (GUID: {})",
|
||||||
|
player->GetName(), player->GetGUID().GetCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TeleportToNewFactionStart(Player* player)
|
||||||
|
{
|
||||||
|
TeamId originalTeam = player->GetTeamId(true);
|
||||||
|
uint8 spawnRace = (originalTeam == TEAM_ALLIANCE)
|
||||||
|
? HORDE_DEFAULT_RACE
|
||||||
|
: ALLIANCE_DEFAULT_RACE;
|
||||||
|
|
||||||
|
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(spawnRace, player->getClass());
|
||||||
|
if (!info)
|
||||||
|
{
|
||||||
|
// Fallback: try warrior (class 1) for that race
|
||||||
|
info = sObjectMgr->GetPlayerInfo(spawnRace, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info)
|
||||||
|
{
|
||||||
|
LOG_ERROR("module", "mod-gamemodes: No spawn info for race {} class {}", spawnRace, player->getClass());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player->TeleportTo(info->mapId, info->positionX, info->positionY, info->positionZ, info->orientation);
|
||||||
|
|
||||||
|
LOG_INFO("module", "mod-gamemodes: Teleported {} to faction start (map {}, {:.1f}, {:.1f}, {:.1f})",
|
||||||
|
player->GetName(), info->mapId, info->positionX, info->positionY, info->positionZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TraitorModePlayer : public PlayerScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TraitorModePlayer() : PlayerScript("TraitorModePlayer",
|
||||||
|
{PLAYERHOOK_ON_CREATE, PLAYERHOOK_ON_LOGIN, PLAYERHOOK_ON_REPUTATION_LOAD_FROM_DB, PLAYERHOOK_ON_UPDATE_FACTION}) {}
|
||||||
|
|
||||||
|
// OnPlayerCreate fires on a temporary Player object AFTER SaveToDB.
|
||||||
|
// Changes to faction/reputation here are lost. We only save the game mode to DB.
|
||||||
|
void OnPlayerCreate(Player* player) override
|
||||||
|
{
|
||||||
|
LOG_INFO("module", "mod-gamemodes: OnPlayerCreate for {} (GUID: {}), OutfitId = {}",
|
||||||
|
player->GetName(), player->GetGUID().GetCounter(), player->GetOutfitId());
|
||||||
|
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Enable", true))
|
||||||
|
return;
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Traitor.Enable", true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (player->GetOutfitId() != GAMEMODE_TRAITOR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sGameModeMgr->SaveGameMode(player->GetGUID().GetCounter(), GAMEMODE_TRAITOR);
|
||||||
|
CharacterDatabase.Execute("UPDATE `characters` SET `cinematic` = 1 WHERE `guid` = {}", player->GetGUID().GetCounter());
|
||||||
|
LOG_INFO("module", "mod-gamemodes: Saved TRAITOR mode for {} (GUID: {})",
|
||||||
|
player->GetName(), player->GetGUID().GetCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reputation load completes before initial packets are sent to the client.
|
||||||
|
// Traitor mode must rewrite faction/reputation state here, otherwise the client
|
||||||
|
// builds the reputation window from the original race defaults.
|
||||||
|
void OnPlayerReputationLoadFromDB(Player* player) override
|
||||||
|
{
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Enable", true))
|
||||||
|
return;
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Traitor.Enable", true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sGameModeMgr->HasGameMode(player->GetGUID().GetCounter(), GAMEMODE_TRAITOR))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TraitorMode::ApplyFactionSwap(player);
|
||||||
|
|
||||||
|
if (player->HasAtLoginFlag(AT_LOGIN_FIRST))
|
||||||
|
TraitorMode::InitTraitorReputations(player);
|
||||||
|
TraitorMode::NormalizeTraitorReputationStates(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlayerLogin(Player* player) override
|
||||||
|
{
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Enable", true))
|
||||||
|
return;
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Traitor.Enable", true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sGameModeMgr->HasGameMode(player->GetGUID().GetCounter(), GAMEMODE_TRAITOR))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TraitorMode::ApplyFactionSwap(player);
|
||||||
|
TraitorMode::NormalizeTraitorReputationStates(player);
|
||||||
|
player->GetReputationMgr().SendInitialReputations();
|
||||||
|
TraitorMode::SendTraitorFactionVisibility(player);
|
||||||
|
player->SetPlayerFlag(PLAYER_FLAGS_TRAITOR);
|
||||||
|
|
||||||
|
if (player->HasAtLoginFlag(AT_LOGIN_FIRST))
|
||||||
|
TraitorMode::TeleportToNewFactionStart(player);
|
||||||
|
|
||||||
|
LOG_INFO("module", "mod-gamemodes: OnPlayerLogin END for {} — TeamId = {}, FactionTemplate = {}, OriginalTeam = {}",
|
||||||
|
player->GetName(),
|
||||||
|
static_cast<uint32>(player->GetTeamId()),
|
||||||
|
player->GetFaction(),
|
||||||
|
static_cast<uint32>(player->GetTeamId(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPlayerUpdateFaction fires every time SetFactionForRace is called
|
||||||
|
// (login, race change, etc). Re-apply the swap so it's never lost.
|
||||||
|
void OnPlayerUpdateFaction(Player* player) override
|
||||||
|
{
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Enable", true))
|
||||||
|
return;
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Traitor.Enable", true))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!sGameModeMgr->HasGameMode(player->GetGUID().GetCounter(), GAMEMODE_TRAITOR))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TraitorMode::ApplyFactionSwap(player);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TraitorModeWorld : public WorldScript
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TraitorModeWorld() : WorldScript("TraitorModeWorld",
|
||||||
|
{WORLDHOOK_ON_LOAD_CUSTOM_DATABASE_TABLE}) {}
|
||||||
|
|
||||||
|
void OnLoadCustomDatabaseTable() override
|
||||||
|
{
|
||||||
|
sGameModeMgr->LoadFromDB();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void AddSC_TraitorMode()
|
||||||
|
{
|
||||||
|
new TraitorModePlayer();
|
||||||
|
new TraitorModeWorld();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#ifndef MOD_GAMEMODES_TRAITOR_H
|
||||||
|
#define MOD_GAMEMODES_TRAITOR_H
|
||||||
|
|
||||||
|
#include "Player.h"
|
||||||
|
|
||||||
|
namespace TraitorMode
|
||||||
|
{
|
||||||
|
// Reputation faction IDs: capital cities + PvP battleground factions
|
||||||
|
constexpr uint32 ALLIANCE_REPUTATION_FACTIONS[] = {
|
||||||
|
72, // Stormwind
|
||||||
|
47, // Ironforge
|
||||||
|
69, // Darnassus
|
||||||
|
54, // Gnomeregan Exiles
|
||||||
|
930, // Exodar
|
||||||
|
890, // Silverwing Sentinels (WSG)
|
||||||
|
730, // Stormpike Guard (AV)
|
||||||
|
509, // League of Arathor (AB)
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint32 HORDE_REPUTATION_FACTIONS[] = {
|
||||||
|
76, // Orgrimmar
|
||||||
|
81, // Thunder Bluff
|
||||||
|
68, // Undercity
|
||||||
|
530, // Darkspear Trolls
|
||||||
|
911, // Silvermoon City
|
||||||
|
889, // Warsong Outriders (WSG)
|
||||||
|
729, // Frostwolf Clan (AV)
|
||||||
|
510, // The Defilers (AB)
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr uint32 FACTION_COUNT = 8;
|
||||||
|
|
||||||
|
// Default spawn points for each faction (race 2 = Orc for Horde, race 1 = Human for Alliance)
|
||||||
|
constexpr uint8 HORDE_DEFAULT_RACE = 2; // Orc
|
||||||
|
constexpr uint8 ALLIANCE_DEFAULT_RACE = 1; // Human
|
||||||
|
|
||||||
|
// Apply faction swap on login/faction update
|
||||||
|
void ApplyFactionSwap(Player* player);
|
||||||
|
|
||||||
|
// Set new faction reputations to Neutral (standing 0)
|
||||||
|
void InitTraitorReputations(Player* player);
|
||||||
|
|
||||||
|
// Teleport traitor to the opposite faction's starting zone
|
||||||
|
void TeleportToNewFactionStart(Player* player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSC_TraitorMode();
|
||||||
|
|
||||||
|
#endif // MOD_GAMEMODES_TRAITOR_H
|
||||||
@@ -47,10 +47,6 @@ public:
|
|||||||
|
|
||||||
sIndividualProgression->CheckAdjustments(player);
|
sIndividualProgression->CheckAdjustments(player);
|
||||||
|
|
||||||
if (sIndividualProgression->enabled)
|
|
||||||
{
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage("|cff00ff00Individual Progression: |cffccccccenabled|r");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerSetMaxLevel(Player* player, uint32& maxPlayerLevel) override
|
void OnPlayerSetMaxLevel(Player* player, uint32& maxPlayerLevel) override
|
||||||
|
|||||||
@@ -4772,7 +4772,10 @@ uint32 PlayerbotAI::AutoScaleActivity(uint32 mod)
|
|||||||
return static_cast<uint32>(mod * (1 - lagProgress));
|
return static_cast<uint32>(mod * (1 - lagProgress));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsOpposing(Player* player) { return IsOpposing(player->getRace(), bot->getRace()); }
|
bool PlayerbotAI::IsOpposing(Player* player)
|
||||||
|
{
|
||||||
|
return player && bot && player->GetTeamId() != bot->GetTeamId();
|
||||||
|
}
|
||||||
|
|
||||||
bool PlayerbotAI::IsOpposing(uint8 race1, uint8 race2)
|
bool PlayerbotAI::IsOpposing(uint8 race1, uint8 race2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,27 +98,6 @@ public:
|
|||||||
PlayerbotsMgr::instance().AddPlayerbotData(player, false);
|
PlayerbotsMgr::instance().AddPlayerbotData(player, false);
|
||||||
sRandomPlayerbotMgr.OnPlayerLogin(player);
|
sRandomPlayerbotMgr.OnPlayerLogin(player);
|
||||||
|
|
||||||
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
|
|
||||||
// especially if you are distributing a repack or hosting a public server
|
|
||||||
// e.g. you can replace the URL with your own repository,
|
|
||||||
// but it should be publicly accessible and include all modifications you've made
|
|
||||||
if (sPlayerbotAIConfig.enabled)
|
|
||||||
{
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage(
|
|
||||||
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
|
|
||||||
"|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sPlayerbotAIConfig.enabled || sPlayerbotAIConfig.randomBotAutologin)
|
|
||||||
{
|
|
||||||
std::string roundedTime =
|
|
||||||
std::to_string(std::ceil((sPlayerbotAIConfig.maxRandomBots * 0.11 / 60) * 10) / 10.0);
|
|
||||||
roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2);
|
|
||||||
|
|
||||||
ChatHandler(player->GetSession()).SendSysMessage(
|
|
||||||
"|cff00ff00Playerbots:|r bot initialization at server startup takes about '"
|
|
||||||
+ roundedTime + "' minutes.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-4
@@ -82,6 +82,9 @@ FORCE_PLAYERBOTS="${FORCE_PLAYERBOTS:-${ACORE_PLAYERBOTS_FORCE:-0}}"
|
|||||||
: "${ACORE_QUEST_PARTY_ENABLE:=1}"
|
: "${ACORE_QUEST_PARTY_ENABLE:=1}"
|
||||||
: "${ACORE_QUEST_PARTY_MESSAGE:=0}"
|
: "${ACORE_QUEST_PARTY_MESSAGE:=0}"
|
||||||
|
|
||||||
|
: "${ACORE_GAMEMODES_ENABLE:=1}"
|
||||||
|
: "${ACORE_GAMEMODES_TRAITOR_ENABLE:=1}"
|
||||||
|
|
||||||
: "${ACORE_ELUNA_ENABLED:=true}"
|
: "${ACORE_ELUNA_ENABLED:=true}"
|
||||||
: "${ACORE_ELUNA_TRACEBACK:=false}"
|
: "${ACORE_ELUNA_TRACEBACK:=false}"
|
||||||
: "${ACORE_ELUNA_SCRIPT_PATH:=lua_scripts}"
|
: "${ACORE_ELUNA_SCRIPT_PATH:=lua_scripts}"
|
||||||
@@ -266,8 +269,9 @@ database_service_running() {
|
|||||||
docker compose ps --status running --services 2>/dev/null | grep -qx 'ac-database'
|
docker compose ps --status running --services 2>/dev/null | grep -qx 'ac-database'
|
||||||
}
|
}
|
||||||
|
|
||||||
import_world_sql_file() {
|
import_sql_file() {
|
||||||
local file_path="$1"
|
local file_path="$1"
|
||||||
|
local database="$2"
|
||||||
|
|
||||||
if [[ ! -f "$file_path" ]]; then
|
if [[ ! -f "$file_path" ]]; then
|
||||||
log "skip missing ${file_path#$ROOT_DIR/}"
|
log "skip missing ${file_path#$ROOT_DIR/}"
|
||||||
@@ -275,13 +279,21 @@ import_world_sql_file() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if (( DRY_RUN )); then
|
if (( DRY_RUN )); then
|
||||||
log "would import ${file_path#$ROOT_DIR/} into acore_world"
|
log "would import ${file_path#$ROOT_DIR/} into ${database}"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker compose exec -T ac-database bash -lc \
|
docker compose exec -T ac-database bash -lc \
|
||||||
"mysql --default-character-set=utf8mb4 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" acore_world" < "$file_path"
|
"mysql --default-character-set=utf8mb4 -uroot -p\"\$MYSQL_ROOT_PASSWORD\" ${database}" < "$file_path"
|
||||||
log "imported ${file_path#$ROOT_DIR/} into acore_world"
|
log "imported ${file_path#$ROOT_DIR/} into ${database}"
|
||||||
|
}
|
||||||
|
|
||||||
|
import_world_sql_file() {
|
||||||
|
import_sql_file "$1" "acore_world"
|
||||||
|
}
|
||||||
|
|
||||||
|
import_characters_sql_file() {
|
||||||
|
import_sql_file "$1" "acore_characters"
|
||||||
}
|
}
|
||||||
|
|
||||||
import_local_lua_module_sql() {
|
import_local_lua_module_sql() {
|
||||||
@@ -300,6 +312,15 @@ import_local_lua_module_sql() {
|
|||||||
import_world_sql_file "$start_zone_race_teleporters_sql"
|
import_world_sql_file "$start_zone_race_teleporters_sql"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import_gamemodes_sql() {
|
||||||
|
if ! database_service_running; then
|
||||||
|
log "skip gamemodes SQL: ac-database is not running"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
import_characters_sql_file "$ROOT_DIR/modules/mod-gamemodes/data/sql/db-characters/base/mod_gamemodes_characters.sql"
|
||||||
|
}
|
||||||
|
|
||||||
ensure_world_conf() {
|
ensure_world_conf() {
|
||||||
if [[ -f "$WORLD_CONF" ]]; then
|
if [[ -f "$WORLD_CONF" ]]; then
|
||||||
return
|
return
|
||||||
@@ -339,6 +360,8 @@ sync_module_dists() {
|
|||||||
"$MODULES_ETC_DIR/mod_learnspells.conf.dist"
|
"$MODULES_ETC_DIR/mod_learnspells.conf.dist"
|
||||||
sync_file "$ROOT_DIR/modules/mod-playerbots/conf/playerbots.conf.dist" \
|
sync_file "$ROOT_DIR/modules/mod-playerbots/conf/playerbots.conf.dist" \
|
||||||
"$MODULES_ETC_DIR/playerbots.conf.dist"
|
"$MODULES_ETC_DIR/playerbots.conf.dist"
|
||||||
|
sync_file "$ROOT_DIR/modules/mod-gamemodes/conf/mod_gamemodes.conf.dist" \
|
||||||
|
"$MODULES_ETC_DIR/mod_gamemodes.conf.dist"
|
||||||
sync_file "$ROOT_DIR/modules/mod-quest-loot-party/conf/mod-quest-loot-party.conf.dist" \
|
sync_file "$ROOT_DIR/modules/mod-quest-loot-party/conf/mod-quest-loot-party.conf.dist" \
|
||||||
"$MODULES_ETC_DIR/mod-quest-loot-party.conf.dist"
|
"$MODULES_ETC_DIR/mod-quest-loot-party.conf.dist"
|
||||||
}
|
}
|
||||||
@@ -349,6 +372,7 @@ write_autobalance_conf() {
|
|||||||
|
|
||||||
# Managed by ./setup-modules.sh
|
# Managed by ./setup-modules.sh
|
||||||
# Solo + small-group profile for dungeons and raids.
|
# Solo + small-group profile for dungeons and raids.
|
||||||
|
AutoBalanceAnnounce.enable = 0
|
||||||
AutoBalance.MinPlayers = ${ACORE_AUTOBALANCE_MIN_PLAYERS}
|
AutoBalance.MinPlayers = ${ACORE_AUTOBALANCE_MIN_PLAYERS}
|
||||||
AutoBalance.MinPlayers.Heroic = ${ACORE_AUTOBALANCE_MIN_PLAYERS_HEROIC}
|
AutoBalance.MinPlayers.Heroic = ${ACORE_AUTOBALANCE_MIN_PLAYERS_HEROIC}
|
||||||
AutoBalance.MinPlayers.Raid = ${ACORE_AUTOBALANCE_MIN_PLAYERS_RAID}
|
AutoBalance.MinPlayers.Raid = ${ACORE_AUTOBALANCE_MIN_PLAYERS_RAID}
|
||||||
@@ -482,6 +506,16 @@ playerbots_supported() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_gamemodes_conf() {
|
||||||
|
write_file "$MODULES_ETC_DIR/mod_gamemodes.conf.dist" <<EOF
|
||||||
|
[worldserver]
|
||||||
|
|
||||||
|
# Managed by ./setup-modules.sh
|
||||||
|
GameModes.Enable = ${ACORE_GAMEMODES_ENABLE}
|
||||||
|
GameModes.Traitor.Enable = ${ACORE_GAMEMODES_TRAITOR_ENABLE}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
write_playerbots_conf() {
|
write_playerbots_conf() {
|
||||||
if ! [[ -f "$ROOT_DIR/modules/mod-playerbots/conf/playerbots.conf.dist" ]]; then
|
if ! [[ -f "$ROOT_DIR/modules/mod-playerbots/conf/playerbots.conf.dist" ]]; then
|
||||||
log "skip playerbots: module config source not found"
|
log "skip playerbots: module config source not found"
|
||||||
@@ -570,6 +604,7 @@ cleanup_legacy_module_overrides() {
|
|||||||
"$MODULES_ETC_DIR/mod_eluna.conf"
|
"$MODULES_ETC_DIR/mod_eluna.conf"
|
||||||
"$MODULES_ETC_DIR/mod_learnspells.conf"
|
"$MODULES_ETC_DIR/mod_learnspells.conf"
|
||||||
"$MODULES_ETC_DIR/mod-quest-loot-party.conf"
|
"$MODULES_ETC_DIR/mod-quest-loot-party.conf"
|
||||||
|
"$MODULES_ETC_DIR/mod_gamemodes.conf"
|
||||||
"$MODULES_ETC_DIR/playerbots.conf"
|
"$MODULES_ETC_DIR/playerbots.conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -626,8 +661,10 @@ main() {
|
|||||||
write_quest_loot_party_conf
|
write_quest_loot_party_conf
|
||||||
write_eluna_conf
|
write_eluna_conf
|
||||||
write_learnspells_conf
|
write_learnspells_conf
|
||||||
|
write_gamemodes_conf
|
||||||
write_playerbots_conf
|
write_playerbots_conf
|
||||||
import_local_lua_module_sql
|
import_local_lua_module_sql
|
||||||
|
import_gamemodes_sql
|
||||||
|
|
||||||
log "module bootstrap completed"
|
log "module bootstrap completed"
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-23
@@ -27,16 +27,23 @@ CREATE TABLE IF NOT EXISTS `store_categories` (
|
|||||||
|
|
||||||
-- Dumping data for table store.store_categories: ~9 rows (approximately)
|
-- Dumping data for table store.store_categories: ~9 rows (approximately)
|
||||||
INSERT INTO `store_categories` (`id`, `name`, `icon`, `requiredRank`, `flags`, `enabled`) VALUES
|
INSERT INTO `store_categories` (`id`, `name`, `icon`, `requiredRank`, `flags`, `enabled`) VALUES
|
||||||
(1, 'Featured', 'inv_helmet_96', 0, 2, 1),
|
(1, 'Рекомендуем', 'inv_helmet_96', 0, 2, 1),
|
||||||
(2, 'Titles', 'inv_scroll_11', 0, 0, 1),
|
(2, 'Титулы', 'inv_scroll_11', 0, 0, 1),
|
||||||
(3, 'Items', 'ability_warrior_challange', 0, 0, 1),
|
(3, 'Предметы', 'ability_warrior_challange', 0, 0, 1),
|
||||||
(4, 'Mounts & Pets', 'inv_box_petcarrier_01', 0, 0, 1),
|
(4, 'Транспорт и питомцы', 'inv_box_petcarrier_01', 0, 0, 1),
|
||||||
(5, 'Boosts', 'spell_holy_surgeoflight', 0, 0, 1),
|
(5, 'Усиления', 'spell_holy_surgeoflight', 0, 0, 1),
|
||||||
(6, 'Services', 'vas_charactertransfer', 0, 0, 1),
|
(6, 'Услуги', 'vas_charactertransfer', 0, 0, 1),
|
||||||
(7, 'Buffs', 'spell_holy_holynova', 0, 0, 1),
|
(7, 'Эффекты', 'spell_holy_holynova', 0, 0, 1),
|
||||||
(8, 'Outlet', 'inv_misc_toy_07', 0, 1, 1),
|
(8, 'Распродажа', 'inv_misc_toy_07', 0, 1, 1),
|
||||||
(9, 'VIP', 'inv_misc_note_03', 4, 0, 1);
|
(9, 'VIP', 'inv_misc_note_03', 4, 0, 1);
|
||||||
|
|
||||||
|
UPDATE `store_categories` SET `name` = CASE `id`
|
||||||
|
WHEN 1 THEN 'Рекомендуем' WHEN 2 THEN 'Титулы' WHEN 3 THEN 'Предметы'
|
||||||
|
WHEN 4 THEN 'Транспорт и питомцы' WHEN 5 THEN 'Усиления' WHEN 6 THEN 'Услуги'
|
||||||
|
WHEN 7 THEN 'Эффекты' WHEN 8 THEN 'Распродажа' WHEN 9 THEN 'VIP'
|
||||||
|
ELSE `name` END
|
||||||
|
WHERE `id` BETWEEN 1 AND 9;
|
||||||
|
|
||||||
-- Dumping structure for table store.store_category_service_link
|
-- Dumping structure for table store.store_category_service_link
|
||||||
CREATE TABLE IF NOT EXISTS `store_category_service_link` (
|
CREATE TABLE IF NOT EXISTS `store_category_service_link` (
|
||||||
`category` int unsigned NOT NULL,
|
`category` int unsigned NOT NULL,
|
||||||
@@ -73,8 +80,15 @@ CREATE TABLE IF NOT EXISTS `store_currencies` (
|
|||||||
|
|
||||||
-- Dumping data for table store.store_currencies: ~2 rows (approximately)
|
-- Dumping data for table store.store_currencies: ~2 rows (approximately)
|
||||||
INSERT INTO `store_currencies` (`id`, `type`, `name`, `icon`, `data`, `tooltip`) VALUES
|
INSERT INTO `store_currencies` (`id`, `type`, `name`, `icon`, `data`, `tooltip`) VALUES
|
||||||
(1, 1, 'Gold', 'Gold', 0, 'This is normal gold.'),
|
(1, 3, 'Баланс', 'Gold', 0, 'Баланс учётной записи на сайте.');
|
||||||
(2, 2, 'Item Token', 'Token', 4540, 'This is an item currency.');
|
|
||||||
|
-- The website account balance is the only in-game store currency. These
|
||||||
|
-- statements also migrate databases bootstrapped by an older Store.sql.
|
||||||
|
UPDATE `store_currencies`
|
||||||
|
SET `type` = 3, `name` = 'Баланс', `icon` = 'Gold', `data` = 0,
|
||||||
|
`tooltip` = 'Баланс учётной записи на сайте.'
|
||||||
|
WHERE `id` = 1;
|
||||||
|
DELETE FROM `store_currencies` WHERE `id` <> 1;
|
||||||
|
|
||||||
-- Dumping structure for table store.store_logs
|
-- Dumping structure for table store.store_logs
|
||||||
CREATE TABLE IF NOT EXISTS `store_logs` (
|
CREATE TABLE IF NOT EXISTS `store_logs` (
|
||||||
@@ -159,19 +173,93 @@ CREATE TABLE IF NOT EXISTS `store_services` (
|
|||||||
|
|
||||||
-- Dumping data for table store.store_services: ~13 rows (approximately)
|
-- Dumping data for table store.store_services: ~13 rows (approximately)
|
||||||
INSERT INTO `store_services` (`id`, `type`, `name`, `tooltipName`, `tooltipType`, `tooltipText`, `icon`, `price`, `currency`, `hyperlinkId`, `creatureEntry`, `discountAmount`, `flags`, `reward_1`, `reward_2`, `reward_3`, `reward_4`, `reward_5`, `reward_6`, `reward_7`, `reward_8`, `rewardcount_1`, `rewardcount_2`, `rewardcount_3`, `rewardcount_4`, `rewardcount_5`, `rewardcount_6`, `rewardcount_7`, `rewardcount_8`, `new`, `enabled`) VALUES
|
INSERT INTO `store_services` (`id`, `type`, `name`, `tooltipName`, `tooltipType`, `tooltipText`, `icon`, `price`, `currency`, `hyperlinkId`, `creatureEntry`, `discountAmount`, `flags`, `reward_1`, `reward_2`, `reward_3`, `reward_4`, `reward_5`, `reward_6`, `reward_7`, `reward_8`, `rewardcount_1`, `rewardcount_2`, `rewardcount_3`, `rewardcount_4`, `rewardcount_5`, `rewardcount_6`, `rewardcount_7`, `rewardcount_8`, `new`, `enabled`) VALUES
|
||||||
(1, 8, 'Level Boost\r\n+10 Levels', 'Level Boost', '', 'Increase your characters levels by 10.', 'achievement_level_10', 10, 2, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(1, 8, 'Повышение уровня\r\n+10 уровней', 'Повышение уровня', '', 'Повышает уровень персонажа на 10.', 'achievement_level_10', 10, 1, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(2, 7, 'Faction Change', 'Faction Change', '', 'Allows you to change your characters faction. Available after relogging.', 'vas_factionchange', 5, 1, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(2, 7, 'Смена фракции', 'Смена фракции', '', 'Позволяет сменить фракцию персонажа. Станет доступно после повторного входа.', 'vas_factionchange', 5, 1, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(3, 7, 'Race Change', 'Race Change', '', 'Allows you to change your characters race. Available after relogging.', 'vas_racechange', 10, 1, 0, 0, 5, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(3, 7, 'Смена расы', 'Смена расы', '', 'Позволяет сменить расу персонажа. Станет доступно после повторного входа.', 'vas_racechange', 10, 1, 0, 0, 5, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(4, 7, 'Name Change', 'Name Change', '', 'Allows you to change your characters name. Available after relogging.', 'vas_namechange', 5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(4, 7, 'Смена имени', 'Смена имени', '', 'Позволяет сменить имя персонажа. Станет доступно после повторного входа.', 'vas_namechange', 5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(5, 1, 'Foam Sword\r\n(Two-Hand Sword)', '', 'item', '|cff00FFFFClick to preview!|r', 'inv_sword_22', 10, 1, 45061, 45061, 0, 1, 45061, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(5, 1, 'Игрушечный меч\r\n(двуручный)', '', 'item', '|cff00FFFFНажмите для предпросмотра|r', 'inv_sword_22', 10, 1, 45061, 45061, 0, 1, 45061, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(8, 3, 'Swift Spectral Tiger\r\n(Mount)', '', 'spell', '|cff00FFFFClick to preview!|r', 'ability_mount_spectraltiger', 30, 1, 42777, 24004, 0, 0, 42777, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1),
|
(8, 3, 'Стремительный призрачный тигр\r\n(транспорт)', '', 'spell', '|cff00FFFFНажмите для предпросмотра|r', 'ability_mount_spectraltiger', 30, 1, 42777, 24004, 0, 0, 42777, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1),
|
||||||
(12, 1, 'Epic Purple Shirt\r\n(Shirt)', '', 'item', '|cff00FFFFClick to preview!|r', 'inv_shirt_purple_01', 10, 1, 45037, 45037, 5, 1, 45037, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(12, 1, 'Эпическая лиловая рубашка', '', 'item', '|cff00FFFFНажмите для предпросмотра|r', 'inv_shirt_purple_01', 10, 1, 45037, 45037, 5, 1, 45037, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(13, 4, 'Prairie Chicken\r\n(Pet)', '', 'spell', '|cff00FFFFClick to preview!|r', 'spell_magic_polymorphchicken', 10, 1, 10686, 7392, 0, 0, 10686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(13, 4, 'Луговая курица\r\n(питомец)', '', 'spell', '|cff00FFFFНажмите для предпросмотра|r', 'spell_magic_polymorphchicken', 10, 1, 10686, 7392, 0, 0, 10686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(14, 8, 'Level Boost\r\n+20 Levels', 'Level Boost', '', 'Increase your characters levels by 20.', 'achievement_level_20', 20, 1, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(14, 8, 'Повышение уровня\r\n+20 уровней', 'Повышение уровня', '', 'Повышает уровень персонажа на 20.', 'achievement_level_20', 20, 1, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(15, 5, 'Buff\r\nBlessing of Might', 'Buff', '', 'Buffs you with Blessing of Might.', 'spell_holy_fistofjustice', 1, 1, 0, 0, 0, 0, 27140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(15, 5, 'Эффект\r\nБлагословение могущества', 'Эффект', '', 'Накладывает Благословение могущества.', 'spell_holy_fistofjustice', 1, 1, 0, 0, 0, 0, 27140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(16, 9, 'Title\r\nChampion of the Naaru', 'Title', '', 'Gives you the title Champion of the Naaru.', 'inv_mace_51', 10, 1, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1),
|
(16, 9, 'Титул\r\nЗащитник Наару', 'Титул', '', 'Открывает титул «Защитник Наару».', 'inv_mace_51', 10, 1, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1),
|
||||||
(17, 8, 'Level 60 Boost', 'Level Boost', '', 'Boosts your characters level to level 60!', 'achievement_level_60', 40, 1, 0, 0, 0, 1, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
(17, 8, 'Повышение до 60-го уровня', 'Повышение уровня', '', 'Повышает уровень персонажа до 60-го.', 'achievement_level_60', 40, 1, 0, 0, 0, 1, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
|
||||||
(18, 1, 'Tuxedo Set', 'Tuxedo Set', '', 'Express your great fashion sense with this full Tuxedo set!\r\n\r\nContains the following:\r\n\r\n1x Tuxedo Jacket\r\n1x Tuxedo Shirt\r\n1x Tuxedo Pants', 'inv_shirt_black_01', 50, 1, 0, 0, 0, 1, 10036, 10035, 10034, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1);
|
(18, 1, 'Комплект смокинга', 'Комплект смокинга', '', 'Стильный комплект смокинга.\r\n\r\nСодержит:\r\n\r\n1x Куртка-смокинг\r\n1x Рубашка под смокинг\r\n1x Брюки под смокинг', 'inv_shirt_black_01', 50, 1, 0, 0, 0, 1, 10036, 10035, 10034, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1);
|
||||||
|
|
||||||
|
-- Update visible texts in already initialized databases as well.
|
||||||
|
UPDATE `store_services` SET
|
||||||
|
`name` = CASE `id`
|
||||||
|
WHEN 1 THEN 'Повышение уровня\r\n+10 уровней' WHEN 2 THEN 'Смена фракции'
|
||||||
|
WHEN 3 THEN 'Смена расы' WHEN 4 THEN 'Смена имени'
|
||||||
|
WHEN 5 THEN 'Игрушечный меч\r\n(двуручный)'
|
||||||
|
WHEN 8 THEN 'Стремительный призрачный тигр\r\n(транспорт)'
|
||||||
|
WHEN 12 THEN 'Эпическая лиловая рубашка' WHEN 13 THEN 'Луговая курица\r\n(питомец)'
|
||||||
|
WHEN 14 THEN 'Повышение уровня\r\n+20 уровней'
|
||||||
|
WHEN 15 THEN 'Эффект\r\nБлагословение могущества'
|
||||||
|
WHEN 16 THEN 'Титул\r\nЗащитник Наару' WHEN 17 THEN 'Повышение до 60-го уровня'
|
||||||
|
WHEN 18 THEN 'Комплект смокинга' ELSE `name` END,
|
||||||
|
`tooltipName` = CASE `id`
|
||||||
|
WHEN 1 THEN 'Повышение уровня' WHEN 2 THEN 'Смена фракции' WHEN 3 THEN 'Смена расы'
|
||||||
|
WHEN 4 THEN 'Смена имени' WHEN 14 THEN 'Повышение уровня' WHEN 15 THEN 'Эффект'
|
||||||
|
WHEN 16 THEN 'Титул' WHEN 17 THEN 'Повышение уровня' WHEN 18 THEN 'Комплект смокинга'
|
||||||
|
ELSE `tooltipName` END,
|
||||||
|
`tooltipText` = CASE `id`
|
||||||
|
WHEN 1 THEN 'Повышает уровень персонажа на 10.'
|
||||||
|
WHEN 2 THEN 'Позволяет сменить фракцию персонажа. Станет доступно после повторного входа.'
|
||||||
|
WHEN 3 THEN 'Позволяет сменить расу персонажа. Станет доступно после повторного входа.'
|
||||||
|
WHEN 4 THEN 'Позволяет сменить имя персонажа. Станет доступно после повторного входа.'
|
||||||
|
WHEN 5 THEN '|cff00FFFFНажмите для предпросмотра|r'
|
||||||
|
WHEN 8 THEN '|cff00FFFFНажмите для предпросмотра|r'
|
||||||
|
WHEN 12 THEN '|cff00FFFFНажмите для предпросмотра|r'
|
||||||
|
WHEN 13 THEN '|cff00FFFFНажмите для предпросмотра|r'
|
||||||
|
WHEN 14 THEN 'Повышает уровень персонажа на 20.'
|
||||||
|
WHEN 15 THEN 'Накладывает Благословение могущества.'
|
||||||
|
WHEN 16 THEN 'Открывает титул «Защитник Наару».'
|
||||||
|
WHEN 17 THEN 'Повышает уровень персонажа до 60-го.'
|
||||||
|
WHEN 18 THEN 'Стильный комплект смокинга.\r\n\r\nСодержит:\r\n\r\n1x Куртка-смокинг\r\n1x Рубашка под смокинг\r\n1x Брюки под смокинг'
|
||||||
|
ELSE `tooltipText` END
|
||||||
|
WHERE `id` IN (1,2,3,4,5,8,12,13,14,15,16,17,18);
|
||||||
|
|
||||||
|
UPDATE `store_services` SET `currency` = 1;
|
||||||
|
|
||||||
|
-- Atomic bridge between the website balance and Eluna. It both reserves the
|
||||||
|
-- funds and writes the website-compatible ledger entry before a reward is sent.
|
||||||
|
DROP FUNCTION IF EXISTS `acore_auth`.`store_deduct_balance`;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE FUNCTION `acore_auth`.`store_deduct_balance`(
|
||||||
|
p_account_id INT UNSIGNED,
|
||||||
|
p_amount DECIMAL(14,2),
|
||||||
|
p_reference VARCHAR(191),
|
||||||
|
p_character_guid INT UNSIGNED,
|
||||||
|
p_service_id INT UNSIGNED
|
||||||
|
) RETURNS DECIMAL(14,2)
|
||||||
|
DETERMINISTIC
|
||||||
|
MODIFIES SQL DATA
|
||||||
|
BEGIN
|
||||||
|
DECLARE v_balance_after DECIMAL(14,2) DEFAULT -1;
|
||||||
|
|
||||||
|
UPDATE `acore_auth`.`account_balances`
|
||||||
|
SET `balance` = `balance` - p_amount, `updated_at` = CURRENT_TIMESTAMP
|
||||||
|
WHERE `account_id` = p_account_id
|
||||||
|
AND p_amount >= 0
|
||||||
|
AND `balance` >= p_amount;
|
||||||
|
|
||||||
|
IF ROW_COUNT() = 1 THEN
|
||||||
|
SELECT `balance` INTO v_balance_after
|
||||||
|
FROM `acore_auth`.`account_balances`
|
||||||
|
WHERE `account_id` = p_account_id;
|
||||||
|
|
||||||
|
INSERT INTO `acore_auth`.`balance_transactions`
|
||||||
|
(`account_id`, `type`, `amount`, `balance_after`, `reference`, `metadata`, `created_at`)
|
||||||
|
VALUES
|
||||||
|
(p_account_id, 'store_purchase', -p_amount, v_balance_after, p_reference,
|
||||||
|
JSON_OBJECT('character_guid', p_character_guid, 'service_id', p_service_id), CURRENT_TIMESTAMP);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN v_balance_after;
|
||||||
|
END//
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
||||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||||
|
|||||||
@@ -41,14 +41,18 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(CHAR_SEL_BANINFO_LIST, "SELECT bandate, unbandate, bannedby, banreason FROM character_banned WHERE guid = ? ORDER BY unbandate", CONNECTION_SYNCH);
|
PrepareStatement(CHAR_SEL_BANINFO_LIST, "SELECT bandate, unbandate, bannedby, banreason FROM character_banned WHERE guid = ? ORDER BY unbandate", CONNECTION_SYNCH);
|
||||||
PrepareStatement(CHAR_SEL_BANNED_NAME, "SELECT characters.name FROM characters, character_banned WHERE character_banned.guid = ? AND character_banned.guid = characters.guid", CONNECTION_SYNCH);
|
PrepareStatement(CHAR_SEL_BANNED_NAME, "SELECT characters.name FROM characters, character_banned WHERE character_banned.guid = ? AND character_banned.guid = characters.guid", CONNECTION_SYNCH);
|
||||||
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
|
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
|
||||||
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags "
|
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags, COALESCE(mgc.game_mode, 0) "
|
||||||
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
|
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
|
||||||
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
|
||||||
|
"LEFT JOIN mod_gamemodes_characters AS mgc ON c.guid = mgc.guid "
|
||||||
|
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
|
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
|
||||||
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
|
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
|
||||||
"cb.guid, c.extra_flags, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
|
"cb.guid, c.extra_flags, COALESCE(mgc.game_mode, 0), cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
|
||||||
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
|
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
|
||||||
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
|
||||||
|
"LEFT JOIN mod_gamemodes_characters AS mgc ON c.guid = mgc.guid "
|
||||||
|
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||||
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||||
@@ -66,12 +70,13 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC);
|
||||||
|
|
||||||
// Start LoginQueryHolder content
|
// Start LoginQueryHolder content
|
||||||
PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
|
PrepareStatement(CHAR_SEL_CHARACTER, "SELECT characters.guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
|
||||||
"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
|
"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
|
||||||
"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
|
"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
|
||||||
"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
|
"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
|
||||||
"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, "
|
"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, "
|
||||||
"knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date) FROM characters WHERE guid = ?", CONNECTION_ASYNC);
|
"knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date), COALESCE(mgc.game_mode, 0) "
|
||||||
|
"FROM characters LEFT JOIN mod_gamemodes_characters AS mgc ON characters.guid = mgc.guid WHERE characters.guid = ?", CONNECTION_ASYNC);
|
||||||
|
|
||||||
PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
|
PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
|
||||||
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
|
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
@@ -109,6 +114,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
|||||||
PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW, "SELECT quest FROM character_queststatus_rewarded WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW, "SELECT quest FROM character_queststatus_rewarded WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
|
||||||
|
PrepareStatement(CHAR_REP_CHAR_GAME_MODE, "REPLACE INTO mod_gamemodes_characters (guid, game_mode) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||||
|
PrepareStatement(CHAR_DEL_CHAR_GAME_MODE, "DELETE FROM mod_gamemodes_characters WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_SEL_BREW_OF_THE_MONTH, "SELECT lastEventId FROM character_brew_of_the_month WHERE guid = ?", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_SEL_BREW_OF_THE_MONTH, "SELECT lastEventId FROM character_brew_of_the_month WHERE guid = ?", CONNECTION_ASYNC);
|
||||||
PrepareStatement(CHAR_REP_BREW_OF_THE_MONTH, "REPLACE INTO character_brew_of_the_month (guid, lastEventId) VALUES (?, ?)", CONNECTION_ASYNC);
|
PrepareStatement(CHAR_REP_BREW_OF_THE_MONTH, "REPLACE INTO character_brew_of_the_month (guid, lastEventId) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ enum CharacterDatabaseStatements : uint32
|
|||||||
CHAR_SEL_CHARACTER_RANDOMBG,
|
CHAR_SEL_CHARACTER_RANDOMBG,
|
||||||
CHAR_SEL_CHARACTER_BANNED,
|
CHAR_SEL_CHARACTER_BANNED,
|
||||||
CHAR_SEL_CHARACTER_QUESTSTATUSREW,
|
CHAR_SEL_CHARACTER_QUESTSTATUSREW,
|
||||||
|
CHAR_REP_CHAR_GAME_MODE,
|
||||||
|
CHAR_DEL_CHAR_GAME_MODE,
|
||||||
CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES,
|
CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES,
|
||||||
CHAR_SEL_MAILITEMS,
|
CHAR_SEL_MAILITEMS,
|
||||||
CHAR_SEL_BREW_OF_THE_MONTH,
|
CHAR_SEL_BREW_OF_THE_MONTH,
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
|
|||||||
if (Player const* player = target->ToPlayer())
|
if (Player const* player = target->ToPlayer())
|
||||||
if (player->GetDeathTimer() != 0)
|
if (player->GetDeathTimer() != 0)
|
||||||
// flag set == must be same team, not set == different team
|
// flag set == must be same team, not set == different team
|
||||||
return (player->GetTeamId() == source->GetTeamId()) == (player_dead.own_team_flag != 0);
|
return (player->GetBgTeamId() == source->GetBgTeamId()) == (player_dead.own_team_flag != 0);
|
||||||
return false;
|
return false;
|
||||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
|
case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA:
|
||||||
return source->HasAuraEffect(aura.spell_id, aura.effect_idx);
|
return source->HasAuraEffect(aura.spell_id, aura.effect_idx);
|
||||||
@@ -389,7 +389,7 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
|
|||||||
if (!bg)
|
if (!bg)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 score = bg->GetTeamScore(source->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE);
|
uint32 score = bg->GetTeamScore(source->GetBgTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE);
|
||||||
return score >= bg_loss_team_score.min_score && score <= bg_loss_team_score.max_score;
|
return score >= bg_loss_team_score.min_score && score <= bg_loss_team_score.max_score;
|
||||||
}
|
}
|
||||||
case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT:
|
case ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT:
|
||||||
@@ -462,7 +462,7 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un
|
|||||||
|
|
||||||
uint32 winnnerScore = bg->GetTeamScore(winnerTeam);
|
uint32 winnnerScore = bg->GetTeamScore(winnerTeam);
|
||||||
uint32 loserScore = bg->GetTeamScore(TeamId(!uint32(winnerTeam)));
|
uint32 loserScore = bg->GetTeamScore(TeamId(!uint32(winnerTeam)));
|
||||||
return source->GetTeamId() == winnerTeam && winnnerScore == teams_scores.winner_score && loserScore == teams_scores.loser_score;
|
return source->GetBgTeamId() == winnerTeam && winnnerScore == teams_scores.winner_score && loserScore == teams_scores.loser_score;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -635,7 +635,7 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, PreparedQ
|
|||||||
|
|
||||||
// title achievement rewards are retroactive
|
// title achievement rewards are retroactive
|
||||||
if (AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement))
|
if (AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement))
|
||||||
if (uint32 titleId = reward->titleId[Player::TeamIdForRace(GetPlayer()->getRace())])
|
if (uint32 titleId = reward->titleId[uint8(GetPlayer()->GetTeamId())])
|
||||||
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
|
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
|
||||||
if (!GetPlayer()->HasTitle(titleEntry))
|
if (!GetPlayer()->HasTitle(titleEntry))
|
||||||
GetPlayer()->SetTitle(titleEntry);
|
GetPlayer()->SetTitle(titleEntry);
|
||||||
@@ -2448,8 +2448,8 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria,
|
|||||||
if (achievement->mapID != -1 && GetPlayer()->GetMapId() != uint32(achievement->mapID))
|
if (achievement->mapID != -1 && GetPlayer()->GetMapId() != uint32(achievement->mapID))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((achievement->requiredFaction == ACHIEVEMENT_FACTION_HORDE && GetPlayer()->GetTeamId(true) != TEAM_HORDE) ||
|
if ((achievement->requiredFaction == ACHIEVEMENT_FACTION_HORDE && GetPlayer()->GetTeamId() != TEAM_HORDE) ||
|
||||||
(achievement->requiredFaction == ACHIEVEMENT_FACTION_ALLIANCE && GetPlayer()->GetTeamId(true) != TEAM_ALLIANCE))
|
(achievement->requiredFaction == ACHIEVEMENT_FACTION_ALLIANCE && GetPlayer()->GetTeamId() != TEAM_ALLIANCE))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i)
|
for (uint32 i = 0; i < MAX_CRITERIA_REQUIREMENTS; ++i)
|
||||||
|
|||||||
@@ -28,6 +28,16 @@ namespace
|
|||||||
{
|
{
|
||||||
std::unordered_map<ObjectGuid, CharacterCacheEntry> _characterCacheStore;
|
std::unordered_map<ObjectGuid, CharacterCacheEntry> _characterCacheStore;
|
||||||
std::unordered_map<std::string, CharacterCacheEntry*> _characterCacheByNameStore;
|
std::unordered_map<std::string, CharacterCacheEntry*> _characterCacheByNameStore;
|
||||||
|
|
||||||
|
TeamId GetEffectiveCharacterTeam(uint8 race, uint32 playerFlags, uint8 gameMode)
|
||||||
|
{
|
||||||
|
TeamId teamId = Player::TeamIdForRace(race);
|
||||||
|
|
||||||
|
if (Player::ResolveGameModeFromStorage(playerFlags, gameMode) == PLAYER_GAMEMODE_TRAITOR)
|
||||||
|
return teamId == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE;
|
||||||
|
|
||||||
|
return teamId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterCache* CharacterCache::instance()
|
CharacterCache* CharacterCache::instance()
|
||||||
@@ -62,7 +72,11 @@ void CharacterCache::LoadCharacterCacheStorage()
|
|||||||
_characterCacheStore.clear();
|
_characterCacheStore.clear();
|
||||||
uint32 oldMSTime = getMSTime();
|
uint32 oldMSTime = getMSTime();
|
||||||
|
|
||||||
QueryResult result = CharacterDatabase.Query("SELECT guid, name, account, race, gender, class, level FROM characters");
|
QueryResult result = CharacterDatabase.Query(R"(
|
||||||
|
SELECT c.guid, c.name, c.account, c.race, c.gender, c.class, c.level, c.playerFlags, COALESCE(mgc.game_mode, 0)
|
||||||
|
FROM characters c
|
||||||
|
LEFT JOIN mod_gamemodes_characters mgc ON mgc.guid = c.guid
|
||||||
|
)");
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
LOG_INFO("server.loading", "No character name data loaded, empty query!");
|
LOG_INFO("server.loading", "No character name data loaded, empty query!");
|
||||||
@@ -73,7 +87,8 @@ void CharacterCache::LoadCharacterCacheStorage()
|
|||||||
{
|
{
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()) /*guid*/, fields[2].Get<uint32>() /*account*/, fields[1].Get<std::string>() /*name*/,
|
AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()) /*guid*/, fields[2].Get<uint32>() /*account*/, fields[1].Get<std::string>() /*name*/,
|
||||||
fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/);
|
fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/,
|
||||||
|
fields[7].Get<uint32>() /*playerFlags*/, fields[8].Get<uint8>() /*gameMode*/);
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
|
|
||||||
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail GROUP BY receiver");
|
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail GROUP BY receiver");
|
||||||
@@ -92,7 +107,12 @@ void CharacterCache::LoadCharacterCacheStorage()
|
|||||||
|
|
||||||
void CharacterCache::RefreshCacheEntry(uint32 lowGuid)
|
void CharacterCache::RefreshCacheEntry(uint32 lowGuid)
|
||||||
{
|
{
|
||||||
QueryResult result = CharacterDatabase.Query("SELECT guid, name, account, race, gender, class, level FROM characters WHERE guid = {}", lowGuid);
|
QueryResult result = CharacterDatabase.Query(R"(
|
||||||
|
SELECT c.guid, c.name, c.account, c.race, c.gender, c.class, c.level, c.playerFlags, COALESCE(mgc.game_mode, 0)
|
||||||
|
FROM characters c
|
||||||
|
LEFT JOIN mod_gamemodes_characters mgc ON mgc.guid = c.guid
|
||||||
|
WHERE c.guid = {}
|
||||||
|
)", lowGuid);
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -102,7 +122,9 @@ void CharacterCache::RefreshCacheEntry(uint32 lowGuid)
|
|||||||
{
|
{
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
DeleteCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(lowGuid), fields[1].Get<std::string>());
|
DeleteCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(lowGuid), fields[1].Get<std::string>());
|
||||||
AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()) /*guid*/, fields[2].Get<uint32>() /*account*/, fields[1].Get<std::string>() /*name*/, fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/);
|
AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()) /*guid*/, fields[2].Get<uint32>() /*account*/, fields[1].Get<std::string>() /*name*/,
|
||||||
|
fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/,
|
||||||
|
fields[7].Get<uint32>() /*playerFlags*/, fields[8].Get<uint8>() /*gameMode*/);
|
||||||
} while (result->NextRow());
|
} while (result->NextRow());
|
||||||
|
|
||||||
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail WHERE receiver = {} GROUP BY receiver", lowGuid);
|
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail WHERE receiver = {} GROUP BY receiver", lowGuid);
|
||||||
@@ -119,16 +141,18 @@ void CharacterCache::RefreshCacheEntry(uint32 lowGuid)
|
|||||||
/*
|
/*
|
||||||
Modifying functions
|
Modifying functions
|
||||||
*/
|
*/
|
||||||
void CharacterCache::AddCharacterCacheEntry(ObjectGuid const& guid, uint32 accountId, std::string const& name, uint8 gender, uint8 race, uint8 playerClass, uint8 level)
|
void CharacterCache::AddCharacterCacheEntry(ObjectGuid const& guid, uint32 accountId, std::string const& name, uint8 gender, uint8 race, uint8 playerClass, uint8 level, uint32 playerFlags, uint8 gameMode)
|
||||||
{
|
{
|
||||||
CharacterCacheEntry& data = _characterCacheStore[guid];
|
CharacterCacheEntry& data = _characterCacheStore[guid];
|
||||||
data.Guid = guid;
|
data.Guid = guid;
|
||||||
data.Name = name;
|
data.Name = name;
|
||||||
data.AccountId = accountId;
|
data.AccountId = accountId;
|
||||||
|
data.PlayerFlags = playerFlags;
|
||||||
data.Race = race;
|
data.Race = race;
|
||||||
data.Sex = gender;
|
data.Sex = gender;
|
||||||
data.Class = playerClass;
|
data.Class = playerClass;
|
||||||
data.Level = level;
|
data.Level = level;
|
||||||
|
data.GameMode = gameMode;
|
||||||
data.GuildId = 0; // Will be set in guild loading or guild setting
|
data.GuildId = 0; // Will be set in guild loading or guild setting
|
||||||
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
|
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
|
||||||
{
|
{
|
||||||
@@ -145,7 +169,7 @@ void CharacterCache::DeleteCharacterCacheEntry(ObjectGuid const& guid, std::stri
|
|||||||
_characterCacheByNameStore.erase(name);
|
_characterCacheByNameStore.erase(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterCache::UpdateCharacterData(ObjectGuid const& guid, std::string const& name, Optional<uint8> gender /*= {}*/, Optional<uint8> race /*= {}*/)
|
void CharacterCache::UpdateCharacterData(ObjectGuid const& guid, std::string const& name, Optional<uint8> gender /*= {}*/, Optional<uint8> race /*= {}*/, Optional<uint32> playerFlags /*= {}*/, Optional<uint8> gameMode /*= {}*/)
|
||||||
{
|
{
|
||||||
auto itr = _characterCacheStore.find(guid);
|
auto itr = _characterCacheStore.find(guid);
|
||||||
if (itr == _characterCacheStore.end())
|
if (itr == _characterCacheStore.end())
|
||||||
@@ -164,6 +188,16 @@ void CharacterCache::UpdateCharacterData(ObjectGuid const& guid, std::string con
|
|||||||
itr->second.Race = *race;
|
itr->second.Race = *race;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playerFlags)
|
||||||
|
{
|
||||||
|
itr->second.PlayerFlags = *playerFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameMode)
|
||||||
|
{
|
||||||
|
itr->second.GameMode = *gameMode;
|
||||||
|
}
|
||||||
|
|
||||||
//WorldPackets::Misc::InvalidatePlayer packet(guid);
|
//WorldPackets::Misc::InvalidatePlayer packet(guid);
|
||||||
//sWorld->SendGlobalMessage(packet.Write());
|
//sWorld->SendGlobalMessage(packet.Write());
|
||||||
|
|
||||||
@@ -311,7 +345,7 @@ uint32 CharacterCache::GetCharacterTeamByGuid(ObjectGuid guid) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Player::TeamIdForRace(itr->second.Race);
|
return GetEffectiveCharacterTeam(itr->second.Race, itr->second.PlayerFlags, itr->second.GameMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 CharacterCache::GetCharacterAccountIdByGuid(ObjectGuid guid) const
|
uint32 CharacterCache::GetCharacterAccountIdByGuid(ObjectGuid guid) const
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ struct CharacterCacheEntry
|
|||||||
ObjectGuid Guid;
|
ObjectGuid Guid;
|
||||||
std::string Name;
|
std::string Name;
|
||||||
uint32 AccountId;
|
uint32 AccountId;
|
||||||
|
uint32 PlayerFlags = 0;
|
||||||
uint8 Class;
|
uint8 Class;
|
||||||
uint8 Race;
|
uint8 Race;
|
||||||
uint8 Sex;
|
uint8 Sex;
|
||||||
uint8 Level;
|
uint8 Level;
|
||||||
|
uint8 GameMode = 0;
|
||||||
uint8 MailCount;
|
uint8 MailCount;
|
||||||
ObjectGuid::LowType GuildId;
|
ObjectGuid::LowType GuildId;
|
||||||
std::array<uint32, MAX_ARENA_SLOT> ArenaTeamId;
|
std::array<uint32, MAX_ARENA_SLOT> ArenaTeamId;
|
||||||
@@ -49,10 +51,10 @@ class AC_GAME_API CharacterCache
|
|||||||
void LoadCharacterCacheStorage();
|
void LoadCharacterCacheStorage();
|
||||||
void RefreshCacheEntry(uint32 lowGuid);
|
void RefreshCacheEntry(uint32 lowGuid);
|
||||||
|
|
||||||
void AddCharacterCacheEntry(ObjectGuid const& guid, uint32 accountId, std::string const& name, uint8 gender, uint8 race, uint8 playerClass, uint8 level);
|
void AddCharacterCacheEntry(ObjectGuid const& guid, uint32 accountId, std::string const& name, uint8 gender, uint8 race, uint8 playerClass, uint8 level, uint32 playerFlags = 0, uint8 gameMode = 0);
|
||||||
void DeleteCharacterCacheEntry(ObjectGuid const& guid, std::string const& name);
|
void DeleteCharacterCacheEntry(ObjectGuid const& guid, std::string const& name);
|
||||||
|
|
||||||
void UpdateCharacterData(ObjectGuid const& guid, std::string const& name, Optional<uint8> gender = {}, Optional<uint8> race = {});
|
void UpdateCharacterData(ObjectGuid const& guid, std::string const& name, Optional<uint8> gender = {}, Optional<uint8> race = {}, Optional<uint32> playerFlags = {}, Optional<uint8> gameMode = {});
|
||||||
void UpdateCharacterLevel(ObjectGuid const& guid, uint8 level);
|
void UpdateCharacterLevel(ObjectGuid const& guid, uint8 level);
|
||||||
void UpdateCharacterAccountId(ObjectGuid const& guid, uint32 accountId);
|
void UpdateCharacterAccountId(ObjectGuid const& guid, uint32 accountId);
|
||||||
void UpdateCharacterGuildId(ObjectGuid const& guid, ObjectGuid::LowType guildId);
|
void UpdateCharacterGuildId(ObjectGuid const& guid, ObjectGuid::LowType guildId);
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ namespace lfg
|
|||||||
{
|
{
|
||||||
if (!itemRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
if (!itemRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
||||||
{
|
{
|
||||||
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == player->GetTeamId(true))
|
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == player->GetTeamId())
|
||||||
{
|
{
|
||||||
if (!player->HasItemCount(itemRequirement->id, 1))
|
if (!player->HasItemCount(itemRequirement->id, 1))
|
||||||
{
|
{
|
||||||
@@ -446,7 +446,7 @@ namespace lfg
|
|||||||
{
|
{
|
||||||
if (!questRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
if (!questRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
||||||
{
|
{
|
||||||
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == player->GetTeamId(true))
|
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == player->GetTeamId())
|
||||||
{
|
{
|
||||||
if (!player->GetQuestRewardStatus(questRequirement->id))
|
if (!player->GetQuestRewardStatus(questRequirement->id))
|
||||||
{
|
{
|
||||||
@@ -468,7 +468,7 @@ namespace lfg
|
|||||||
{
|
{
|
||||||
if (!achievementRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
if (!achievementRequirement->checkLeaderOnly || !group || group->GetLeaderGUID() == player->GetGUID())
|
||||||
{
|
{
|
||||||
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == player->GetTeamId(true))
|
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == player->GetTeamId())
|
||||||
{
|
{
|
||||||
if (!player->HasAchieved(achievementRequirement->id))
|
if (!player->HasAchieved(achievementRequirement->id))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,6 +147,80 @@ enum CharacterCustomizeFlags
|
|||||||
CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc...
|
CHAR_CUSTOMIZE_FLAG_RACE = 0x00100000 // name, gender, race, etc...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr uint32 ALLIANCE_TRAITOR_REPUTATION_FACTIONS[] = { 72, 47, 69, 54, 930, 890, 730, 509 };
|
||||||
|
constexpr uint32 HORDE_TRAITOR_REPUTATION_FACTIONS[] = { 76, 81, 68, 530, 911, 889, 729, 510 };
|
||||||
|
constexpr uint32 TRAITOR_REPUTATION_FACTION_COUNT = 8;
|
||||||
|
|
||||||
|
bool IsGameModeEnabled(PlayerGameMode gameMode)
|
||||||
|
{
|
||||||
|
if (gameMode == PLAYER_GAMEMODE_NORMAL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!sConfigMgr->GetOption<bool>("GameModes.Enable", true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (gameMode)
|
||||||
|
{
|
||||||
|
case PLAYER_GAMEMODE_TRAITOR:
|
||||||
|
return sConfigMgr->GetOption<bool>("GameModes.Traitor.Enable", true);
|
||||||
|
case PLAYER_GAMEMODE_NORMAL:
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TeamId GetOppositeTeamId(TeamId team)
|
||||||
|
{
|
||||||
|
switch (team)
|
||||||
|
{
|
||||||
|
case TEAM_ALLIANCE:
|
||||||
|
return TEAM_HORDE;
|
||||||
|
case TEAM_HORDE:
|
||||||
|
return TEAM_ALLIANCE;
|
||||||
|
default:
|
||||||
|
return TEAM_NEUTRAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerGameMode NormalizeGameModeValue(uint8 gameMode)
|
||||||
|
{
|
||||||
|
return gameMode == PLAYER_GAMEMODE_TRAITOR ? PLAYER_GAMEMODE_TRAITOR : PLAYER_GAMEMODE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerGameMode GameModeFromOutfitId(uint8 outfitId)
|
||||||
|
{
|
||||||
|
if (outfitId == 0)
|
||||||
|
return PLAYER_GAMEMODE_NORMAL;
|
||||||
|
|
||||||
|
// We repurpose the create packet's outfit byte for game mode selection.
|
||||||
|
// The stock client normally sends 0 here, so treat any non-zero custom value
|
||||||
|
// as a request for the only create-time custom mode we currently support.
|
||||||
|
PlayerGameMode gameMode = NormalizeGameModeValue(outfitId);
|
||||||
|
|
||||||
|
if (gameMode == PLAYER_GAMEMODE_NORMAL)
|
||||||
|
gameMode = PLAYER_GAMEMODE_TRAITOR;
|
||||||
|
|
||||||
|
return IsGameModeEnabled(gameMode) ? gameMode : PLAYER_GAMEMODE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 GetRepresentativeRaceForTeam(TeamId team)
|
||||||
|
{
|
||||||
|
return team == TEAM_HORDE ? RACE_ORC : RACE_HUMAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInfo const* GetRepresentativePlayerInfoForTeam(TeamId team, uint8 classId)
|
||||||
|
{
|
||||||
|
uint8 representativeRace = GetRepresentativeRaceForTeam(team);
|
||||||
|
|
||||||
|
if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(representativeRace, classId))
|
||||||
|
return info;
|
||||||
|
|
||||||
|
return sObjectMgr->GetPlayerInfo(representativeRace, CLASS_WARRIOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
|
static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
|
||||||
|
|
||||||
// we can disable this warning for this since it only
|
// we can disable this warning for this since it only
|
||||||
@@ -486,12 +560,13 @@ void Player::CleanupsBeforeDelete(bool finalCleanup)
|
|||||||
|
|
||||||
bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo)
|
bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo)
|
||||||
{
|
{
|
||||||
// FIXME: outfitId not used in player creating
|
m_outfitId = createInfo->OutfitId;
|
||||||
/// @todo: need more checks against packet modifications
|
/// @todo: need more checks against packet modifications
|
||||||
// should check that skin, face, hair* are valid via DBC per race/class
|
// should check that skin, face, hair* are valid via DBC per race/class
|
||||||
// also do it in Player::BuildEnumData, Player::LoadFromDB
|
// also do it in Player::BuildEnumData, Player::LoadFromDB
|
||||||
|
|
||||||
Object::_Create(guidlow, 0, HighGuid::Player);
|
Object::_Create(guidlow, 0, HighGuid::Player);
|
||||||
|
SetGameMode(GameModeFromOutfitId(m_outfitId));
|
||||||
|
|
||||||
m_name = createInfo->Name;
|
m_name = createInfo->Name;
|
||||||
|
|
||||||
@@ -506,8 +581,6 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
|
|||||||
for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
|
for (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
|
||||||
m_items[i] = nullptr;
|
m_items[i] = nullptr;
|
||||||
|
|
||||||
Relocate(info->positionX, info->positionY, info->positionZ, info->orientation);
|
|
||||||
|
|
||||||
ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
|
ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class);
|
||||||
if (!cEntry)
|
if (!cEntry)
|
||||||
{
|
{
|
||||||
@@ -516,8 +589,6 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetMap(sMapMgr->CreateMap(info->mapId, this));
|
|
||||||
|
|
||||||
uint8 powertype = cEntry->powerType;
|
uint8 powertype = cEntry->powerType;
|
||||||
|
|
||||||
SetObjectScale(1.0f);
|
SetObjectScale(1.0f);
|
||||||
@@ -525,8 +596,6 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
|
|||||||
m_realRace = createInfo->Race; // set real race flag
|
m_realRace = createInfo->Race; // set real race flag
|
||||||
m_race = createInfo->Race; // set real race flag
|
m_race = createInfo->Race; // set real race flag
|
||||||
|
|
||||||
SetFactionForRace(createInfo->Race);
|
|
||||||
|
|
||||||
if (!IsValidGender(createInfo->Gender))
|
if (!IsValidGender(createInfo->Gender))
|
||||||
{
|
{
|
||||||
LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account {} tried creating a character named '{}' with an invalid gender ({}) - refusing to do so",
|
LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account {} tried creating a character named '{}' with an invalid gender ({}) - refusing to do so",
|
||||||
@@ -535,8 +604,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
|
uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
|
||||||
|
|
||||||
SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
|
SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
|
||||||
|
|
||||||
|
SetFactionForRace(createInfo->Race);
|
||||||
|
|
||||||
|
WorldLocation startPosition = GetStartPosition();
|
||||||
|
Relocate(startPosition);
|
||||||
|
SetMap(sMapMgr->CreateMap(startPosition.GetMapId(), this));
|
||||||
|
|
||||||
InitDisplayIds();
|
InitDisplayIds();
|
||||||
if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
|
if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
|
||||||
{
|
{
|
||||||
@@ -1111,8 +1186,8 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
|
|||||||
// characters.hairColor, characters.facialStyle, character.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z,
|
// characters.hairColor, characters.facialStyle, character.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z,
|
||||||
// 16 17 18 19 20 21 22 23
|
// 16 17 18 19 20 21 22 23
|
||||||
// guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, character_banned.guid,
|
// guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, character_banned.guid,
|
||||||
// 24 25
|
// 24 25 26
|
||||||
// characters.extra_flags, character_declinedname.genitive
|
// characters.extra_flags, mod_gamemodes_characters.game_mode, character_declinedname.genitive
|
||||||
|
|
||||||
Field* fields = result->Fetch();
|
Field* fields = result->Fetch();
|
||||||
|
|
||||||
@@ -1184,12 +1259,15 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
|
|||||||
charFlags |= CHARACTER_FLAG_LOCKED_BY_BILLING;
|
charFlags |= CHARACTER_FLAG_LOCKED_BY_BILLING;
|
||||||
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
|
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
|
||||||
{
|
{
|
||||||
if (!fields[25].Get<std::string>().empty())
|
if (!fields[26].Get<std::string>().empty())
|
||||||
charFlags |= CHARACTER_FLAG_DECLINED;
|
charFlags |= CHARACTER_FLAG_DECLINED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
charFlags |= CHARACTER_FLAG_DECLINED;
|
charFlags |= CHARACTER_FLAG_DECLINED;
|
||||||
|
|
||||||
|
if (fields[25].Get<uint8>() == 1 /* GAMEMODE_TRAITOR */)
|
||||||
|
charFlags |= CHARACTER_FLAG_UNK31;
|
||||||
|
|
||||||
*data << uint32(charFlags); // character flags
|
*data << uint32(charFlags); // character flags
|
||||||
|
|
||||||
// character customize flags
|
// character customize flags
|
||||||
@@ -4065,6 +4143,10 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up
|
|||||||
stmt->SetData(0, lowGuid);
|
stmt->SetData(0, lowGuid);
|
||||||
trans->Append(stmt);
|
trans->Append(stmt);
|
||||||
|
|
||||||
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GAME_MODE);
|
||||||
|
stmt->SetData(0, lowGuid);
|
||||||
|
trans->Append(stmt);
|
||||||
|
|
||||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA);
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA);
|
||||||
stmt->SetData(0, lowGuid);
|
stmt->SetData(0, lowGuid);
|
||||||
trans->Append(stmt);
|
trans->Append(stmt);
|
||||||
@@ -5850,17 +5932,46 @@ TeamId Player::TeamIdForRace(uint8 race)
|
|||||||
|
|
||||||
void Player::SetFactionForRace(uint8 race)
|
void Player::SetFactionForRace(uint8 race)
|
||||||
{
|
{
|
||||||
m_team = TeamIdForRace(race);
|
TeamId nativeTeam = TeamIdForRace(race);
|
||||||
|
m_team = IsTraitor() ? GetOppositeTeamId(nativeTeam) : nativeTeam;
|
||||||
|
|
||||||
sScriptMgr->OnPlayerUpdateFaction(this);
|
sScriptMgr->OnPlayerUpdateFaction(this);
|
||||||
|
|
||||||
if (GetTeamId(true) != GetTeamId())
|
uint8 factionRace = IsTraitor() ? GetRepresentativeRaceForTeam(m_team) : race;
|
||||||
return;
|
ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(factionRace);
|
||||||
|
|
||||||
ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
|
|
||||||
SetFaction(rEntry ? rEntry->FactionID : 0);
|
SetFaction(rEntry ? rEntry->FactionID : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayerGameMode Player::ResolveGameModeFromStorage(uint32 playerFlags, uint8 gameMode)
|
||||||
|
{
|
||||||
|
PlayerGameMode resolvedGameMode = NormalizeGameModeValue(gameMode);
|
||||||
|
|
||||||
|
if (resolvedGameMode == PLAYER_GAMEMODE_NORMAL && (playerFlags & PLAYER_FLAGS_TRAITOR))
|
||||||
|
resolvedGameMode = PLAYER_GAMEMODE_TRAITOR;
|
||||||
|
|
||||||
|
return IsGameModeEnabled(resolvedGameMode) ? resolvedGameMode : PLAYER_GAMEMODE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInfo const* Player::GetStartPlayerInfo() const
|
||||||
|
{
|
||||||
|
if (IsTraitor())
|
||||||
|
return GetRepresentativePlayerInfoForTeam(GetTeamId(), getClass());
|
||||||
|
|
||||||
|
return sObjectMgr->GetPlayerInfo(getRace(true), getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::SetGameMode(PlayerGameMode gameMode)
|
||||||
|
{
|
||||||
|
m_gameMode = NormalizeGameModeValue(static_cast<uint8>(gameMode));
|
||||||
|
if (!IsGameModeEnabled(m_gameMode))
|
||||||
|
m_gameMode = PLAYER_GAMEMODE_NORMAL;
|
||||||
|
|
||||||
|
if (m_gameMode == PLAYER_GAMEMODE_TRAITOR)
|
||||||
|
SetPlayerFlag(PLAYER_FLAGS_TRAITOR);
|
||||||
|
else
|
||||||
|
RemovePlayerFlag(PLAYER_FLAGS_TRAITOR);
|
||||||
|
}
|
||||||
|
|
||||||
ReputationRank Player::GetReputationRank(uint32 faction) const
|
ReputationRank Player::GetReputationRank(uint32 faction) const
|
||||||
{
|
{
|
||||||
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
|
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
|
||||||
@@ -5972,7 +6083,7 @@ void Player::RewardReputation(Unit* victim)
|
|||||||
ChampioningFaction = GetChampioningFaction();
|
ChampioningFaction = GetChampioningFaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
TeamId teamId = GetTeamId(true); // Always check player original reputation when rewarding
|
TeamId teamId = GetTeamId();
|
||||||
|
|
||||||
if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE))
|
if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE))
|
||||||
{
|
{
|
||||||
@@ -6086,6 +6197,78 @@ void Player::RewardReputation(Quest const* quest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::ApplyTraitorReputationState()
|
||||||
|
{
|
||||||
|
if (!IsTraitor())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto normalizeFactionState = [this](FactionEntry const* factionEntry, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
if (!factionEntry || factionEntry->reputationListID < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FactionState* factionState = const_cast<FactionState*>(GetReputationMgr().GetState(factionEntry));
|
||||||
|
if (!factionState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
factionState->Flags &= ~(FACTION_FLAG_VISIBLE | FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED |
|
||||||
|
FACTION_FLAG_PEACE_FORCED | FACTION_FLAG_INACTIVE);
|
||||||
|
|
||||||
|
if (visible)
|
||||||
|
factionState->Flags |= FACTION_FLAG_VISIBLE;
|
||||||
|
else
|
||||||
|
factionState->Flags |= FACTION_FLAG_HIDDEN | FACTION_FLAG_INVISIBLE_FORCED;
|
||||||
|
|
||||||
|
if (atWar)
|
||||||
|
factionState->Flags |= FACTION_FLAG_AT_WAR;
|
||||||
|
else
|
||||||
|
factionState->Flags &= ~FACTION_FLAG_AT_WAR;
|
||||||
|
|
||||||
|
factionState->needSend = true;
|
||||||
|
factionState->needSave = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto setFactionStanding = [this, &normalizeFactionState](uint32 factionId, int32 targetDisplayed, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId);
|
||||||
|
if (!factionEntry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
GetReputationMgr().SetOneFactionReputation(factionEntry, static_cast<float>(targetDisplayed), false);
|
||||||
|
normalizeFactionState(factionEntry, visible, atWar);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto normalizeFactionAndParent = [&normalizeFactionState](uint32 factionId, bool visible, bool atWar)
|
||||||
|
{
|
||||||
|
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId))
|
||||||
|
{
|
||||||
|
normalizeFactionState(factionEntry, visible, atWar);
|
||||||
|
|
||||||
|
if (factionEntry->team)
|
||||||
|
normalizeFactionState(sFactionStore.LookupEntry(factionEntry->team), visible, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TeamId nativeTeam = GetTeamId(true);
|
||||||
|
uint32 const* friendlyFactions = nativeTeam == TEAM_ALLIANCE ? HORDE_TRAITOR_REPUTATION_FACTIONS : ALLIANCE_TRAITOR_REPUTATION_FACTIONS;
|
||||||
|
uint32 const* hostileFactions = nativeTeam == TEAM_ALLIANCE ? ALLIANCE_TRAITOR_REPUTATION_FACTIONS : HORDE_TRAITOR_REPUTATION_FACTIONS;
|
||||||
|
|
||||||
|
if (HasAtLoginFlag(AT_LOGIN_FIRST))
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < TRAITOR_REPUTATION_FACTION_COUNT; ++i)
|
||||||
|
setFactionStanding(friendlyFactions[i], 0, true, false);
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < TRAITOR_REPUTATION_FACTION_COUNT; ++i)
|
||||||
|
setFactionStanding(hostileFactions[i], -6000, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < TRAITOR_REPUTATION_FACTION_COUNT; ++i)
|
||||||
|
{
|
||||||
|
normalizeFactionAndParent(friendlyFactions[i], true, false);
|
||||||
|
normalizeFactionAndParent(hostileFactions[i], false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Player::RewardExtraBonusTalentPoints(uint32 bonusTalentPoints)
|
void Player::RewardExtraBonusTalentPoints(uint32 bonusTalentPoints)
|
||||||
{
|
{
|
||||||
if (bonusTalentPoints)
|
if (bonusTalentPoints)
|
||||||
@@ -10301,7 +10484,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
|
|||||||
// only one mount ID for both sides. Probably not good to use 315 in case DBC nodes
|
// only one mount ID for both sides. Probably not good to use 315 in case DBC nodes
|
||||||
// change but I couldn't find a suitable alternative. OK to use class because only DK
|
// change but I couldn't find a suitable alternative. OK to use class because only DK
|
||||||
// can use this taxi.
|
// can use this taxi.
|
||||||
uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(true), npc == nullptr || (sourcenode == 315 && IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TAXI)));
|
uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(), npc == nullptr || (sourcenode == 315 && IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TAXI)));
|
||||||
|
|
||||||
// in spell case allow 0 model
|
// in spell case allow 0 model
|
||||||
if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
|
if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
|
||||||
@@ -10396,7 +10579,7 @@ void Player::ContinueTaxiFlight()
|
|||||||
|
|
||||||
LOG_DEBUG("entities.unit", "WORLD: Restart character {} taxi flight", GetGUID().ToString());
|
LOG_DEBUG("entities.unit", "WORLD: Restart character {} taxi flight", GetGUID().ToString());
|
||||||
|
|
||||||
uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode, GetTeamId(true), true);
|
uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode, GetTeamId(), true);
|
||||||
if (!mountDisplayId)
|
if (!mountDisplayId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -10675,7 +10858,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsGameMaster() && ((pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && GetTeamId(true) == TEAM_ALLIANCE) || (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && GetTeamId(true) == TEAM_HORDE)))
|
if (!IsGameMaster() && ((pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && GetTeamId() == TEAM_ALLIANCE) || (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && GetTeamId() == TEAM_HORDE)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -11338,7 +11521,7 @@ void Player::ReportedAfkBy(Player* reporter)
|
|||||||
|
|
||||||
WorldLocation Player::GetStartPosition() const
|
WorldLocation Player::GetStartPosition() const
|
||||||
{
|
{
|
||||||
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass());
|
PlayerInfo const* info = GetStartPlayerInfo();
|
||||||
uint32 mapId = info->mapId;
|
uint32 mapId = info->mapId;
|
||||||
if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) && HasSpell(50977))
|
if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) && HasSpell(50977))
|
||||||
return WorldLocation(0, 2352.0f, -5709.0f, 154.5f, 0.0f);
|
return WorldLocation(0, 2352.0f, -5709.0f, 154.5f, 0.0f);
|
||||||
@@ -14698,6 +14881,7 @@ void Player::_SaveEntryPoint(CharacterDatabaseTransaction trans)
|
|||||||
stmt->SetData(7, m_entryPointData.taxiPath[1]);
|
stmt->SetData(7, m_entryPointData.taxiPath[1]);
|
||||||
stmt->SetData(8, m_entryPointData.mountSpell);
|
stmt->SetData(8, m_entryPointData.mountSpell);
|
||||||
trans->Append(stmt);
|
trans->Append(stmt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::DeleteEquipmentSet(uint64 setGuid)
|
void Player::DeleteEquipmentSet(uint64 setGuid)
|
||||||
@@ -14765,6 +14949,25 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
uint8 index = 0;
|
uint8 index = 0;
|
||||||
|
|
||||||
auto finiteAlways = [](float f) { return std::isfinite(f) ? f : 0.0f; };
|
auto finiteAlways = [](float f) { return std::isfinite(f) ? f : 0.0f; };
|
||||||
|
PlayerGameMode persistedGameMode = GetGameMode();
|
||||||
|
|
||||||
|
// Character enum and login restore traitor mode from persisted storage, not from the
|
||||||
|
// transient create packet. Re-derive the initial mode from the raw create byte before
|
||||||
|
// the first save in case later create initialization has reset the in-memory state.
|
||||||
|
if (create && persistedGameMode == PLAYER_GAMEMODE_NORMAL && m_outfitId != 0)
|
||||||
|
{
|
||||||
|
persistedGameMode = GameModeFromOutfitId(m_outfitId);
|
||||||
|
|
||||||
|
if (persistedGameMode != GetGameMode())
|
||||||
|
{
|
||||||
|
SetGameMode(persistedGameMode);
|
||||||
|
SetFactionForRace(getRace(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 playerFlagsForSave = GetPlayerFlags();
|
||||||
|
if (create && persistedGameMode == PLAYER_GAMEMODE_TRAITOR)
|
||||||
|
playerFlagsForSave |= PLAYER_FLAGS_TRAITOR;
|
||||||
|
|
||||||
if (create)
|
if (create)
|
||||||
{
|
{
|
||||||
@@ -14787,7 +14990,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 0));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 0));
|
||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 2));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 2));
|
||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 3));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 3));
|
||||||
stmt->SetData(index++, (uint32)GetPlayerFlags());
|
stmt->SetData(index++, playerFlagsForSave);
|
||||||
stmt->SetData(index++, (uint16)GetMapId());
|
stmt->SetData(index++, (uint16)GetMapId());
|
||||||
stmt->SetData(index++, (uint32)GetInstanceId());
|
stmt->SetData(index++, (uint32)GetInstanceId());
|
||||||
stmt->SetData(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
|
stmt->SetData(index++, (uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4));
|
||||||
@@ -14905,7 +15108,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 0));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 0));
|
||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 2));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 2));
|
||||||
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 3));
|
stmt->SetData(index++, GetByteValue(PLAYER_BYTES_2, 3));
|
||||||
stmt->SetData(index++, GetPlayerFlags());
|
stmt->SetData(index++, playerFlagsForSave);
|
||||||
|
|
||||||
if (!IsBeingTeleported())
|
if (!IsBeingTeleported())
|
||||||
{
|
{
|
||||||
@@ -15033,6 +15236,20 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
|
|||||||
}
|
}
|
||||||
|
|
||||||
trans->Append(stmt);
|
trans->Append(stmt);
|
||||||
|
|
||||||
|
if (persistedGameMode == PLAYER_GAMEMODE_TRAITOR)
|
||||||
|
{
|
||||||
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_GAME_MODE);
|
||||||
|
stmt->SetData(0, GetGUID().GetCounter());
|
||||||
|
stmt->SetData(1, uint8(persistedGameMode));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GAME_MODE);
|
||||||
|
stmt->SetData(0, GetGUID().GetCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
trans->Append(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::_LoadGlyphs(PreparedQueryResult result)
|
void Player::_LoadGlyphs(PreparedQueryResult result)
|
||||||
|
|||||||
@@ -454,6 +454,13 @@ enum DrunkenState
|
|||||||
|
|
||||||
#define MAX_DRUNKEN 4
|
#define MAX_DRUNKEN 4
|
||||||
|
|
||||||
|
enum PlayerGameMode : uint8
|
||||||
|
{
|
||||||
|
PLAYER_GAMEMODE_NORMAL = 0,
|
||||||
|
PLAYER_GAMEMODE_TRAITOR = 1,
|
||||||
|
PLAYER_GAMEMODE_MAX
|
||||||
|
};
|
||||||
|
|
||||||
enum PlayerFlags : uint32
|
enum PlayerFlags : uint32
|
||||||
{
|
{
|
||||||
PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
|
PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
|
||||||
@@ -486,7 +493,7 @@ enum PlayerFlags : uint32
|
|||||||
PLAYER_FLAGS_UNK27 = 0x08000000,
|
PLAYER_FLAGS_UNK27 = 0x08000000,
|
||||||
PLAYER_FLAGS_UNK28 = 0x10000000,
|
PLAYER_FLAGS_UNK28 = 0x10000000,
|
||||||
PLAYER_FLAGS_UNK29 = 0x20000000,
|
PLAYER_FLAGS_UNK29 = 0x20000000,
|
||||||
PLAYER_FLAGS_UNK30 = 0x40000000,
|
PLAYER_FLAGS_TRAITOR = 0x40000000, // mod-gamemodes: player is playing for the opposite faction
|
||||||
PLAYER_FLAGS_UNK31 = 0x80000000,
|
PLAYER_FLAGS_UNK31 = 0x80000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1156,7 +1163,7 @@ public:
|
|||||||
PlayerSocial* GetSocial() { return m_social; }
|
PlayerSocial* GetSocial() { return m_social; }
|
||||||
|
|
||||||
PlayerTaxi m_taxi;
|
PlayerTaxi m_taxi;
|
||||||
void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(), getClass(), GetLevel()); }
|
void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(IsTraitor() ? (GetTeamId() == TEAM_ALLIANCE ? RACE_HUMAN : RACE_ORC) : getRace(), getClass(), GetLevel()); }
|
||||||
bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc = nullptr, uint32 spellid = 1);
|
bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc = nullptr, uint32 spellid = 1);
|
||||||
bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 1);
|
bool ActivateTaxiPathTo(uint32 taxi_path_id, uint32 spellid = 1);
|
||||||
void CleanupAfterTaxiFlight();
|
void CleanupAfterTaxiFlight();
|
||||||
@@ -2126,10 +2133,18 @@ public:
|
|||||||
void CheckAreaExploreAndOutdoor();
|
void CheckAreaExploreAndOutdoor();
|
||||||
|
|
||||||
static TeamId TeamIdForRace(uint8 race);
|
static TeamId TeamIdForRace(uint8 race);
|
||||||
|
[[nodiscard]] static PlayerGameMode ResolveGameModeFromStorage(uint32 playerFlags, uint8 gameMode);
|
||||||
[[nodiscard]] TeamId GetTeamId(bool original = false) const { return original ? TeamIdForRace(getRace(true)) : m_team; };
|
[[nodiscard]] TeamId GetTeamId(bool original = false) const { return original ? TeamIdForRace(getRace(true)) : m_team; };
|
||||||
|
[[nodiscard]] PlayerGameMode GetGameMode() const { return m_gameMode; }
|
||||||
|
[[nodiscard]] bool HasGameMode(PlayerGameMode gameMode) const { return m_gameMode == gameMode; }
|
||||||
|
[[nodiscard]] bool IsTraitor() const { return HasGameMode(PLAYER_GAMEMODE_TRAITOR); }
|
||||||
|
[[nodiscard]] PlayerInfo const* GetStartPlayerInfo() const;
|
||||||
|
void SetGameMode(PlayerGameMode gameMode);
|
||||||
void SetFactionForRace(uint8 race);
|
void SetFactionForRace(uint8 race);
|
||||||
void setTeamId(TeamId teamid) { m_team = teamid; };
|
void setTeamId(TeamId teamid) { m_team = teamid; };
|
||||||
|
|
||||||
|
[[nodiscard]] uint8 GetOutfitId() const { return m_outfitId; }
|
||||||
|
|
||||||
void InitDisplayIds();
|
void InitDisplayIds();
|
||||||
|
|
||||||
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
|
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
|
||||||
@@ -2148,6 +2163,7 @@ public:
|
|||||||
[[nodiscard]] ReputationRank GetReputationRank(uint32 faction_id) const;
|
[[nodiscard]] ReputationRank GetReputationRank(uint32 faction_id) const;
|
||||||
void RewardReputation(Unit* victim);
|
void RewardReputation(Unit* victim);
|
||||||
void RewardReputation(Quest const* quest);
|
void RewardReputation(Quest const* quest);
|
||||||
|
void ApplyTraitorReputationState();
|
||||||
|
|
||||||
float CalculateReputationGain(ReputationSource source, uint32 creatureOrQuestLevel, float rep, int32 faction, bool noQuestBonus = false);
|
float CalculateReputationGain(ReputationSource source, uint32 creatureOrQuestLevel, float rep, int32 faction, bool noQuestBonus = false);
|
||||||
|
|
||||||
@@ -2823,6 +2839,8 @@ protected:
|
|||||||
ObjectGuid m_lootGuid;
|
ObjectGuid m_lootGuid;
|
||||||
|
|
||||||
TeamId m_team;
|
TeamId m_team;
|
||||||
|
PlayerGameMode m_gameMode{PLAYER_GAMEMODE_NORMAL};
|
||||||
|
uint8 m_outfitId{0};
|
||||||
uint32 m_nextSave; // pussywizard
|
uint32 m_nextSave; // pussywizard
|
||||||
uint16 m_additionalSaveTimer; // pussywizard
|
uint16 m_additionalSaveTimer; // pussywizard
|
||||||
uint8 m_additionalSaveMask; // pussywizard
|
uint8 m_additionalSaveMask; // pussywizard
|
||||||
|
|||||||
@@ -1106,8 +1106,18 @@ bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) const
|
|||||||
uint32 reqraces = qInfo->GetAllowableRaces();
|
uint32 reqraces = qInfo->GetAllowableRaces();
|
||||||
if (reqraces == 0)
|
if (reqraces == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if ((reqraces & getRaceMask()) == 0)
|
if ((reqraces & getRaceMask()) == 0)
|
||||||
{
|
{
|
||||||
|
// Allow faction-swapped characters to take quests restricted to their active faction,
|
||||||
|
// while still respecting truly race-specific quests on the opposite side.
|
||||||
|
if (GetTeamId() != GetTeamId(true))
|
||||||
|
{
|
||||||
|
uint32 currentFactionRaceMask = (GetTeamId() == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE;
|
||||||
|
if ((reqraces & currentFactionRaceMask) == reqraces)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg)
|
if (msg)
|
||||||
SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
|
SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
|
/// @todo: this import is not necessary for compilation and marked as unused by the IDE
|
||||||
// however, for some reasons removing it would cause a damn linking issue
|
// however, for some reasons removing it would cause a damn linking issue
|
||||||
@@ -2378,12 +2379,12 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
|
|||||||
return EQUIP_ERR_ITEM_NOT_FOUND;
|
return EQUIP_ERR_ITEM_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && GetTeamId(true) != TEAM_HORDE)
|
if (proto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && GetTeamId() != TEAM_HORDE)
|
||||||
{
|
{
|
||||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && GetTeamId(true) != TEAM_ALLIANCE)
|
if (proto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && GetTeamId() != TEAM_ALLIANCE)
|
||||||
{
|
{
|
||||||
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||||
}
|
}
|
||||||
@@ -5014,8 +5015,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
|
//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
|
||||||
// 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
// 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
||||||
//"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles,
|
//"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles,
|
||||||
// 70 71 72 73 74
|
// 70 71 72 73 74 75
|
||||||
//"actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date) FROM characters WHERE guid = '{}'", guid);
|
//"actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date), game_mode FROM characters WHERE guid = '{}'", guid);
|
||||||
PreparedQueryResult result = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
|
PreparedQueryResult result = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
@@ -5093,6 +5094,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
|
|
||||||
// load character creation date, relevant for achievements of type average
|
// load character creation date, relevant for achievements of type average
|
||||||
SetCreationTime(fields[74].Get<Seconds>());
|
SetCreationTime(fields[74].Get<Seconds>());
|
||||||
|
PlayerGameMode storedGameMode = ResolveGameModeFromStorage(fields[16].Get<uint32>(), fields[75].Get<uint8>());
|
||||||
|
SetGameMode(storedGameMode);
|
||||||
|
|
||||||
// load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
|
// load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
|
||||||
m_achievementMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_OFFLINE_ACHIEVEMENTS_UPDATES));
|
m_achievementMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_OFFLINE_ACHIEVEMENTS_UPDATES));
|
||||||
@@ -5112,6 +5115,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
SetByteValue(PLAYER_BYTES_3, 0, fields[5].Get<uint8>());
|
SetByteValue(PLAYER_BYTES_3, 0, fields[5].Get<uint8>());
|
||||||
SetByteValue(PLAYER_BYTES_3, 1, fields[54].Get<uint8>());
|
SetByteValue(PLAYER_BYTES_3, 1, fields[54].Get<uint8>());
|
||||||
ReplaceAllPlayerFlags((PlayerFlags)fields[16].Get<uint32>());
|
ReplaceAllPlayerFlags((PlayerFlags)fields[16].Get<uint32>());
|
||||||
|
SetGameMode(storedGameMode);
|
||||||
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[53].Get<uint32>());
|
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fields[53].Get<uint32>());
|
||||||
|
|
||||||
SetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES, fields[52].Get<uint64>());
|
SetUInt64Value(PLAYER_FIELD_KNOWN_CURRENCIES, fields[52].Get<uint64>());
|
||||||
@@ -5149,12 +5153,22 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
|
|
||||||
InitPrimaryProfessions(); // to max set before any spell loaded
|
InitPrimaryProfessions(); // to max set before any spell loaded
|
||||||
|
|
||||||
|
bool isFirstLogin = (fields[38].Get<uint16>() & AT_LOGIN_FIRST) != 0;
|
||||||
|
|
||||||
// init saved position, and fix it later if problematic
|
// init saved position, and fix it later if problematic
|
||||||
int32 transLowGUID = fields[35].Get<int32>();
|
int32 transLowGUID = fields[35].Get<int32>();
|
||||||
Relocate(fields[17].Get<float>(), fields[18].Get<float>(), fields[19].Get<float>(), fields[21].Get<float>());
|
Relocate(fields[17].Get<float>(), fields[18].Get<float>(), fields[19].Get<float>(), fields[21].Get<float>());
|
||||||
uint32 mapId = fields[20].Get<uint16>();
|
uint32 mapId = fields[20].Get<uint16>();
|
||||||
uint32 instanceId = fields[63].Get<uint32>();
|
uint32 instanceId = fields[63].Get<uint32>();
|
||||||
|
|
||||||
|
if (isFirstLogin && IsTraitor())
|
||||||
|
{
|
||||||
|
WorldLocation startPosition = GetStartPosition();
|
||||||
|
Relocate(startPosition);
|
||||||
|
mapId = startPosition.GetMapId();
|
||||||
|
instanceId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 dungeonDiff = fields[43].Get<uint8>() & 0x0F;
|
uint32 dungeonDiff = fields[43].Get<uint8>() & 0x0F;
|
||||||
if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY)
|
if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY)
|
||||||
dungeonDiff = DUNGEON_DIFFICULTY_NORMAL;
|
dungeonDiff = DUNGEON_DIFFICULTY_NORMAL;
|
||||||
@@ -5311,7 +5325,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
else if (!taxi_nodes.empty())
|
else if (!taxi_nodes.empty())
|
||||||
{
|
{
|
||||||
instanceId = 0;
|
instanceId = 0;
|
||||||
if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes, GetTeamId(true)))
|
if (!m_taxi.LoadTaxiDestinationsFromString(taxi_nodes, GetTeamId()))
|
||||||
{
|
{
|
||||||
// xinef: could no load valid data for taxi, relocate to homebind and clear
|
// xinef: could no load valid data for taxi, relocate to homebind and clear
|
||||||
m_taxi.ClearTaxiDestinations();
|
m_taxi.ClearTaxiDestinations();
|
||||||
@@ -5543,6 +5557,13 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
|
|
||||||
// must be before inventory (some items required reputation check)
|
// must be before inventory (some items required reputation check)
|
||||||
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
|
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
|
||||||
|
sScriptMgr->OnPlayerReputationLoadFromDB(this);
|
||||||
|
|
||||||
|
// Traitor game mode swaps the player's team without changing race, but the
|
||||||
|
// reputation list is always rebuilt from the native race. Re-apply the
|
||||||
|
// traitor reputation state here so the new faction's reputations stay
|
||||||
|
// visible/friendly and the native ones hidden/at-war on every login.
|
||||||
|
ApplyTraitorReputationState();
|
||||||
|
|
||||||
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
|
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
|
||||||
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
|
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
|
||||||
@@ -6850,7 +6871,7 @@ bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map
|
|||||||
missingItems = &missingLeaderItems;
|
missingItems = &missingLeaderItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == checkPlayer->GetTeamId(true))
|
if (itemRequirement->faction == TEAM_NEUTRAL || itemRequirement->faction == checkPlayer->GetTeamId())
|
||||||
{
|
{
|
||||||
if (!checkPlayer->HasItemCount(itemRequirement->id, 1))
|
if (!checkPlayer->HasItemCount(itemRequirement->id, 1))
|
||||||
{
|
{
|
||||||
@@ -6872,7 +6893,7 @@ bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map
|
|||||||
missingAchievements = &missingLeaderAchievements;
|
missingAchievements = &missingLeaderAchievements;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == GetTeamId(true))
|
if (achievementRequirement->faction == TEAM_NEUTRAL || achievementRequirement->faction == GetTeamId())
|
||||||
{
|
{
|
||||||
if (!checkPlayer || !checkPlayer->HasAchieved(achievementRequirement->id))
|
if (!checkPlayer || !checkPlayer->HasAchieved(achievementRequirement->id))
|
||||||
{
|
{
|
||||||
@@ -6894,7 +6915,7 @@ bool Player::Satisfy(DungeonProgressionRequirements const* ar, uint32 target_map
|
|||||||
missingQuests = &missingLeaderQuests;
|
missingQuests = &missingLeaderQuests;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == checkPlayer->GetTeamId(true))
|
if (questRequirement->faction == TEAM_NEUTRAL || questRequirement->faction == checkPlayer->GetTeamId())
|
||||||
{
|
{
|
||||||
if (!checkPlayer->GetQuestRewardStatus(questRequirement->id))
|
if (!checkPlayer->GetQuestRewardStatus(questRequirement->id))
|
||||||
{
|
{
|
||||||
@@ -7081,8 +7102,9 @@ bool Player::CheckInstanceCount(uint32 instanceId) const
|
|||||||
|
|
||||||
bool Player::_LoadHomeBind(PreparedQueryResult result)
|
bool Player::_LoadHomeBind(PreparedQueryResult result)
|
||||||
{
|
{
|
||||||
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass());
|
PlayerInfo const* info = GetStartPlayerInfo();
|
||||||
if (!info)
|
PlayerInfo const* nativeInfo = sObjectMgr->GetPlayerInfo(getRace(true), getClass());
|
||||||
|
if (!info || !nativeInfo)
|
||||||
{
|
{
|
||||||
LOG_ERROR("entities.player", "Player (Name {}) has incorrect race/class pair. Can't be loaded.", GetName());
|
LOG_ERROR("entities.player", "Player (Name {}) has incorrect race/class pair. Can't be loaded.", GetName());
|
||||||
return false;
|
return false;
|
||||||
@@ -7114,6 +7136,25 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ok && IsTraitor())
|
||||||
|
{
|
||||||
|
auto sameCoord = [](float left, float right) { return std::fabs(left - right) < 0.01f; };
|
||||||
|
bool matchesNativeHomebind = m_homebindMapId == nativeInfo->mapId &&
|
||||||
|
m_homebindAreaId == nativeInfo->areaId &&
|
||||||
|
sameCoord(m_homebindX, nativeInfo->positionX) &&
|
||||||
|
sameCoord(m_homebindY, nativeInfo->positionY) &&
|
||||||
|
sameCoord(m_homebindZ, nativeInfo->positionZ);
|
||||||
|
|
||||||
|
bool matchesEffectiveHomebind = m_homebindMapId == info->mapId &&
|
||||||
|
m_homebindAreaId == info->areaId &&
|
||||||
|
sameCoord(m_homebindX, info->positionX) &&
|
||||||
|
sameCoord(m_homebindY, info->positionY) &&
|
||||||
|
sameCoord(m_homebindZ, info->positionZ);
|
||||||
|
|
||||||
|
if (matchesNativeHomebind && !matchesEffectiveHomebind)
|
||||||
|
SetHomebind(WorldLocation(info->mapId, info->positionX, info->positionY, info->positionZ, 0.0f), info->areaId);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
m_homebindMapId = info->mapId;
|
m_homebindMapId = info->mapId;
|
||||||
|
|||||||
@@ -1244,7 +1244,7 @@ void Player::UpdateArea(uint32 newArea)
|
|||||||
else
|
else
|
||||||
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
|
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
|
||||||
|
|
||||||
uint32 const areaRestFlag = (GetTeamId(true) == TEAM_ALLIANCE)
|
uint32 const areaRestFlag = (GetTeamId() == TEAM_ALLIANCE)
|
||||||
? AREA_FLAG_REST_ZONE_ALLIANCE
|
? AREA_FLAG_REST_ZONE_ALLIANCE
|
||||||
: AREA_FLAG_REST_ZONE_HORDE;
|
: AREA_FLAG_REST_ZONE_HORDE;
|
||||||
if (area && area->flags & areaRestFlag)
|
if (area && area->flags & areaRestFlag)
|
||||||
@@ -1305,12 +1305,12 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea, bool force)
|
|||||||
{
|
{
|
||||||
case AREATEAM_ALLY:
|
case AREATEAM_ALLY:
|
||||||
pvpInfo.IsInHostileArea =
|
pvpInfo.IsInHostileArea =
|
||||||
GetTeamId(true) != TEAM_ALLIANCE &&
|
GetTeamId() != TEAM_ALLIANCE &&
|
||||||
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
|
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
|
||||||
break;
|
break;
|
||||||
case AREATEAM_HORDE:
|
case AREATEAM_HORDE:
|
||||||
pvpInfo.IsInHostileArea =
|
pvpInfo.IsInHostileArea =
|
||||||
GetTeamId(true) != TEAM_HORDE &&
|
GetTeamId() != TEAM_HORDE &&
|
||||||
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
|
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
|
||||||
break;
|
break;
|
||||||
case AREATEAM_NONE:
|
case AREATEAM_NONE:
|
||||||
|
|||||||
@@ -1483,7 +1483,7 @@ void Guild::HandleInviteMember(WorldSession* session, std::string const& name)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && pInvitee->GetTeamId(true) != player->GetTeamId(true))
|
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && pInvitee->GetTeamId() != player->GetTeamId())
|
||||||
{
|
{
|
||||||
SendCommandResult(session, GUILD_COMMAND_INVITE, ERR_GUILD_NOT_ALLIED, name);
|
SendCommandResult(session, GUILD_COMMAND_INVITE, ERR_GUILD_NOT_ALLIED, name);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -548,7 +548,9 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
|
if (newChar->IsTraitor())
|
||||||
|
newChar->setCinematic(1);
|
||||||
|
else if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2)
|
||||||
newChar->setCinematic(1); // not show intro
|
newChar->setCinematic(1); // not show intro
|
||||||
|
|
||||||
newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
|
newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login
|
||||||
@@ -558,6 +560,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
|||||||
|
|
||||||
// Player created, save it now
|
// Player created, save it now
|
||||||
newChar->SaveToDB(characterTransaction, true, false);
|
newChar->SaveToDB(characterTransaction, true, false);
|
||||||
|
|
||||||
createInfo->CharCount++;
|
createInfo->CharCount++;
|
||||||
|
|
||||||
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
|
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM);
|
||||||
@@ -579,7 +582,8 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData)
|
|||||||
{
|
{
|
||||||
LOG_INFO("entities.player.character", "Account: {} (IP: {}) Create Character: {} {}", GetAccountId(), GetRemoteAddress(), newChar->GetName(), newChar->GetGUID().ToString());
|
LOG_INFO("entities.player.character", "Account: {} (IP: {}) Create Character: {} {}", GetAccountId(), GetRemoteAddress(), newChar->GetName(), newChar->GetGUID().ToString());
|
||||||
sScriptMgr->OnPlayerCreate(newChar.get());
|
sScriptMgr->OnPlayerCreate(newChar.get());
|
||||||
sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->getGender(), newChar->getRace(), newChar->getClass(), newChar->GetLevel());
|
sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->getGender(), newChar->getRace(), newChar->getClass(), newChar->GetLevel(),
|
||||||
|
newChar->GetUInt32Value(PLAYER_FLAGS), static_cast<uint8>(newChar->GetGameMode()));
|
||||||
SendCharCreate(CHAR_CREATE_SUCCESS);
|
SendCharCreate(CHAR_CREATE_SUCCESS);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -446,12 +446,12 @@ bool LootItem::AllowedForPlayer(Player const* player, ObjectGuid source) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not show loot for not own team
|
// not show loot for not own team
|
||||||
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && player->GetTeamId(true) != TEAM_HORDE)
|
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_HORDE) && player->GetTeamId() != TEAM_HORDE)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeamId(true) != TEAM_ALLIANCE)
|
if (pProto->HasFlag2(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeamId() != TEAM_ALLIANCE)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,20 @@ const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 300
|
|||||||
const int32 ReputationMgr::Reputation_Cap = 42999;
|
const int32 ReputationMgr::Reputation_Cap = 42999;
|
||||||
const int32 ReputationMgr::Reputation_Bottom = -42000;
|
const int32 ReputationMgr::Reputation_Bottom = -42000;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
uint32 GetEffectiveReputationRaceMask(Player const* player)
|
||||||
|
{
|
||||||
|
// Always use the player's actual race mask so the server BaseRep calculation
|
||||||
|
// matches what the client computes (client always uses the original race, not
|
||||||
|
// the current team). This is correct for both the race-change service (where
|
||||||
|
// getRace() already reflects the new race) and for game modes like Traitor
|
||||||
|
// that swap team without changing race (where the original race mask is the
|
||||||
|
// right baseline for reputation display and incremental gain alike).
|
||||||
|
return player->getRaceMask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
|
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
|
||||||
{
|
{
|
||||||
int32 limit = Reputation_Cap + 1;
|
int32 limit = Reputation_Cap + 1;
|
||||||
@@ -93,7 +107,7 @@ int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
|
|||||||
if (!factionEntry)
|
if (!factionEntry)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
uint32 raceMask = _player->getRaceMask();
|
uint32 raceMask = GetEffectiveReputationRaceMask(_player);
|
||||||
uint32 classMask = _player->getClassMask();
|
uint32 classMask = _player->getClassMask();
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
@@ -147,7 +161,7 @@ uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) con
|
|||||||
if (!factionEntry)
|
if (!factionEntry)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
uint32 raceMask = _player->getRaceMask();
|
uint32 raceMask = GetEffectiveReputationRaceMask(_player);
|
||||||
uint32 classMask = _player->getClassMask();
|
uint32 classMask = _player->getClassMask();
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
@@ -464,10 +478,18 @@ void ReputationMgr::SetVisible(FactionTemplateEntry const* factionTemplateEntry)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
|
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
|
||||||
|
{
|
||||||
|
if (_player->GetTeamId() != _player->GetTeamId(true))
|
||||||
|
{
|
||||||
|
SetVisible(factionEntry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Never show factions of the opposing team
|
// Never show factions of the opposing team
|
||||||
if (!(factionEntry->BaseRepRaceMask[1] & _player->getRaceMask() && factionEntry->BaseRepValue[1] == Reputation_Bottom))
|
if (!(factionEntry->BaseRepRaceMask[1] & _player->getRaceMask() && factionEntry->BaseRepValue[1] == Reputation_Bottom))
|
||||||
SetVisible(factionEntry);
|
SetVisible(factionEntry);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ReputationMgr::SetVisible(FactionEntry const* factionEntry)
|
void ReputationMgr::SetVisible(FactionEntry const* factionEntry)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -215,6 +215,11 @@ void ScriptMgr::OnPlayerLoadFromDB(Player* player)
|
|||||||
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LOAD_FROM_DB, script->OnPlayerLoadFromDB(player));
|
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LOAD_FROM_DB, script->OnPlayerLoadFromDB(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScriptMgr::OnPlayerReputationLoadFromDB(Player* player)
|
||||||
|
{
|
||||||
|
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_REPUTATION_LOAD_FROM_DB, script->OnPlayerReputationLoadFromDB(player));
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptMgr::OnPlayerBeforeLogout(Player* player)
|
void ScriptMgr::OnPlayerBeforeLogout(Player* player)
|
||||||
{
|
{
|
||||||
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_LOGOUT, script->OnPlayerBeforeLogout(player));
|
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_LOGOUT, script->OnPlayerBeforeLogout(player));
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ enum PlayerHook
|
|||||||
PLAYERHOOK_ON_TEXT_EMOTE,
|
PLAYERHOOK_ON_TEXT_EMOTE,
|
||||||
PLAYERHOOK_ON_SPELL_CAST,
|
PLAYERHOOK_ON_SPELL_CAST,
|
||||||
PLAYERHOOK_ON_LOAD_FROM_DB,
|
PLAYERHOOK_ON_LOAD_FROM_DB,
|
||||||
|
PLAYERHOOK_ON_REPUTATION_LOAD_FROM_DB,
|
||||||
PLAYERHOOK_ON_LOGIN,
|
PLAYERHOOK_ON_LOGIN,
|
||||||
PLAYERHOOK_ON_BEFORE_LOGOUT,
|
PLAYERHOOK_ON_BEFORE_LOGOUT,
|
||||||
PLAYERHOOK_ON_LOGOUT,
|
PLAYERHOOK_ON_LOGOUT,
|
||||||
@@ -320,6 +321,7 @@ public:
|
|||||||
|
|
||||||
// Called during data loading
|
// Called during data loading
|
||||||
virtual void OnPlayerLoadFromDB(Player* /*player*/) { };
|
virtual void OnPlayerLoadFromDB(Player* /*player*/) { };
|
||||||
|
virtual void OnPlayerReputationLoadFromDB(Player* /*player*/) { };
|
||||||
|
|
||||||
// Called when a player logs in.
|
// Called when a player logs in.
|
||||||
virtual void OnPlayerLogin(Player* /*player*/) { }
|
virtual void OnPlayerLogin(Player* /*player*/) { }
|
||||||
|
|||||||
@@ -346,6 +346,7 @@ public: /* PlayerScript */
|
|||||||
void OnPlayerSpellCast(Player* player, Spell* spell, bool skipCheck);
|
void OnPlayerSpellCast(Player* player, Spell* spell, bool skipCheck);
|
||||||
void OnPlayerLogin(Player* player);
|
void OnPlayerLogin(Player* player);
|
||||||
void OnPlayerLoadFromDB(Player* player);
|
void OnPlayerLoadFromDB(Player* player);
|
||||||
|
void OnPlayerReputationLoadFromDB(Player* player);
|
||||||
void OnPlayerBeforeLogout(Player* player);
|
void OnPlayerBeforeLogout(Player* player);
|
||||||
void OnPlayerLogout(Player* player);
|
void OnPlayerLogout(Player* player);
|
||||||
void OnPlayerCreate(Player* player);
|
void OnPlayerCreate(Player* player);
|
||||||
|
|||||||
@@ -111,12 +111,14 @@ public:
|
|||||||
{
|
{
|
||||||
if (sCharacterCache->HasCharacterCacheEntry(cPlayer->GetGUID()))
|
if (sCharacterCache->HasCharacterCacheEntry(cPlayer->GetGUID()))
|
||||||
{
|
{
|
||||||
sCharacterCache->UpdateCharacterData(cPlayer->GetGUID(), cPlayer->GetName(), cPlayer->getGender(), cPlayer->getRace());
|
sCharacterCache->UpdateCharacterData(cPlayer->GetGUID(), cPlayer->GetName(), cPlayer->getGender(), cPlayer->getRace(),
|
||||||
|
cPlayer->GetUInt32Value(PLAYER_FLAGS), static_cast<uint8>(cPlayer->GetGameMode()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sCharacterCache->AddCharacterCacheEntry(cPlayer->GetGUID(), cPlayer->GetSession()->GetAccountId(), cPlayer->GetName(),
|
sCharacterCache->AddCharacterCacheEntry(cPlayer->GetGUID(), cPlayer->GetSession()->GetAccountId(), cPlayer->GetName(),
|
||||||
cPlayer->getGender(), cPlayer->getRace(), cPlayer->getClass(), cPlayer->GetLevel());
|
cPlayer->getGender(), cPlayer->getRace(), cPlayer->getClass(), cPlayer->GetLevel(),
|
||||||
|
cPlayer->GetUInt32Value(PLAYER_FLAGS), static_cast<uint8>(cPlayer->GetGameMode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
sCharacterCache->UpdateCharacterAccountId(cPlayer->GetGUID(), cPlayer->GetSession()->GetAccountId());
|
sCharacterCache->UpdateCharacterAccountId(cPlayer->GetGUID(), cPlayer->GetSession()->GetAccountId());
|
||||||
|
|||||||
Reference in New Issue
Block a user