Solocraft : equilibrage reel du bonus solo
Le bonus etait ressenti comme hardcore et incoherent. Quatre causes, toutes corrigees. 1. La taille du groupe etait calculee puis ignoree : elle ne servait qu'au message d'annonce. Un joueur seul et un groupe de 5 recevaient le meme x5 chacun, donc le solo ramait et le groupe complet ecrasait tout. Le bonus vaut desormais difficulte / joueurs presents dans l'instance (5 joueurs en donjon 5 -> x1). 2. Seules les stats primaires etaient buffees. Les degats d'une attaque valent (degats_arme + AP/3.5) et seule la moitie AP en profitait : le joueur encaissait x5 mais tapait a peine plus fort. Ajout d'un bonus sur UNIT_MOD_DAMAGE_MAINHAND/ OFFHAND/RANGED, dose par Solocraft.DamagePct. 3. Les familiers derivent leurs PV et leur AP du maitre mais ne recalculaient pas apres le buff : un pet deja invoque restait a x1. Appel de UpdateAllStats sur pet et gardien. 4. Rejoindre ou quitter un groupe en pleine instance ne recalculait rien. Ajout d'un GroupScript. Aussi : plus de soin complet gratuit si le buff est recalcule en combat, logs passes de INFO a DEBUG sur scripts.solocraft (ils saturaient dc-world.log), Solocraft.Mythic rend le mythique configurable, acces au conteneur protege par mutex. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <algorithm>
|
||||
#include "Config.h"
|
||||
#include "Chat.h"
|
||||
#include "Log.h"
|
||||
@@ -23,162 +25,311 @@
|
||||
#include "Unit.h"
|
||||
#include "Player.h"
|
||||
#include "Pet.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "Map.h"
|
||||
#include "Group.h"
|
||||
#include "GroupReference.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "InstanceScript.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class solocraft_player_instance_handler : public PlayerScript {
|
||||
// Etat du buff reellement applique a un joueur. On memorise les pourcentages EXACTS
|
||||
// passes a HandleStatModifier pour pouvoir les retirer a l'identique, meme si la
|
||||
// config a ete rechargee (ou le groupe a change) entre l'application et le retrait.
|
||||
struct SolocraftBuff
|
||||
{
|
||||
float statPct; // pourcentage TOTAL_PCT sur les stats primaires (400 = x5)
|
||||
float weaponPct; // pourcentage TOTAL_PCT sur les degats d'arme
|
||||
float effective; // multiplicateur effectif, pour l'affichage joueur
|
||||
|
||||
SolocraftBuff() : statPct(0.0f), weaponPct(0.0f), effective(1.0f) { }
|
||||
};
|
||||
|
||||
// Accede depuis les threads de map (OnMapChanged) et depuis le thread world
|
||||
// (GroupScript) -> les acces au conteneur sont proteges.
|
||||
std::mutex _buffMutex;
|
||||
std::map<uint64, SolocraftBuff> _buffs;
|
||||
|
||||
bool IsEnabled()
|
||||
{
|
||||
return sConfigMgr->GetBoolDefault("Solocraft.Enable", false);
|
||||
}
|
||||
|
||||
// Difficulte BRUTE de la carte, c'est a dire pour combien de joueurs le contenu est
|
||||
// calibre. Relue a chaque appel (evenement peu frequent) : 'reload config' en tmux
|
||||
// suffit donc pour tuner l'equilibrage, sans rebuild ni restart.
|
||||
float ReadRawDifficulty(Map* map)
|
||||
{
|
||||
if (!map)
|
||||
return 1.0f;
|
||||
|
||||
// Mythique / Mythique+ : contenu reserve au groupe complet, aucun buff par defaut.
|
||||
// Solocraft.Mythic = 1.0 desactive, une valeur > 1 l'active (ex: 5.0).
|
||||
if (map->IsMythic())
|
||||
return sConfigMgr->GetFloatDefault("Solocraft.Mythic", 1.0f);
|
||||
|
||||
if (map->IsRaid())
|
||||
{
|
||||
switch (map->GetMapDifficulty()->MaxPlayers)
|
||||
{
|
||||
case 10: return sConfigMgr->GetFloatDefault("Solocraft.Raid10", 10.0f);
|
||||
case 25: return sConfigMgr->GetFloatDefault("Solocraft.Raid25", 25.0f);
|
||||
case 30: return sConfigMgr->GetFloatDefault("Solocraft.Raid30", 30.0f);
|
||||
case 40: return sConfigMgr->GetFloatDefault("Solocraft.Raid40", 40.0f);
|
||||
default:
|
||||
TC_LOG_WARN("scripts.solocraft", "[Solocraft] Nombre max de joueurs non reconnu (%d), repli sur Raid10",
|
||||
map->GetMapDifficulty()->MaxPlayers);
|
||||
return sConfigMgr->GetFloatDefault("Solocraft.Raid10", 10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (map->IsDungeon())
|
||||
{
|
||||
if (map->IsHeroic())
|
||||
return sConfigMgr->GetFloatDefault("Solocraft.Heroic", 10.0f);
|
||||
return sConfigMgr->GetFloatDefault("Solocraft.Dungeon", 5.0f);
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Nombre de membres du groupe REELLEMENT presents dans l'instance.
|
||||
// Les membres restes en ville ne doivent pas diluer le buff de ceux qui jouent.
|
||||
uint32 CountPlayersInInstance(Player* player, Map* map)
|
||||
{
|
||||
Group* group = player->GetGroup();
|
||||
if (!group)
|
||||
return 1;
|
||||
|
||||
uint32 count = 0;
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->GetSource();
|
||||
if (member && member->IsInWorld() && member->GetMap() == map)
|
||||
++count;
|
||||
}
|
||||
|
||||
return std::max<uint32>(count, 1);
|
||||
}
|
||||
|
||||
// Cœur du correctif d'equilibrage.
|
||||
//
|
||||
// L'ancienne version calculait bien la taille du groupe... puis ne s'en servait que
|
||||
// dans le message d'annonce. Resultat : un joueur seul et un groupe de 5 recevaient
|
||||
// le meme x5 chacun -> le solo ramait, le groupe complet ecrasait tout.
|
||||
//
|
||||
// effectif = difficulte brute / joueurs presents :
|
||||
// donjon (D=5) en solo -> x5 (le joueur remplace 5 joueurs)
|
||||
// donjon (D=5) a 5 -> x1 (aucun buff, contenu joue normalement)
|
||||
// raid 25 (D=25) a 10 -> x2.5
|
||||
void ComputeMultipliers(float rawDifficulty, uint32 numPlayers, SolocraftBuff& out)
|
||||
{
|
||||
float effective = rawDifficulty / float(numPlayers);
|
||||
if (effective < 1.0f)
|
||||
effective = 1.0f; // jamais de malus si le groupe est plus grand que prevu
|
||||
|
||||
// Deux leviers separes, tous deux reglables a chaud.
|
||||
//
|
||||
// Solocraft.StatsPct : part du bonus appliquee aux stats primaires. Elles portent
|
||||
// les PV (endurance), la puissance des sorts et la puissance
|
||||
// d'attaque -> a 100 le joueur solo a bien x5 PV et x5 AP.
|
||||
//
|
||||
// Solocraft.DamagePct : part appliquee aux degats d'arme. Indispensable : les degats
|
||||
// d'une attaque valent (degats_arme + AP/3.5), et seule la
|
||||
// moitie "AP" profitait du buff de stats. Le joueur devenait
|
||||
// une eponge lente -- il encaissait x5 mais tapait a peine
|
||||
// plus fort, d'ou la sensation de contenu hardcore.
|
||||
// 20 vise un total d'environ x5 degats en solo une fois
|
||||
// combine au x5 d'AP (double comptage compris).
|
||||
const float statsPct = sConfigMgr->GetFloatDefault("Solocraft.StatsPct", 100.0f);
|
||||
const float damagePct = sConfigMgr->GetFloatDefault("Solocraft.DamagePct", 20.0f);
|
||||
|
||||
out.effective = effective;
|
||||
out.statPct = (effective - 1.0f) * statsPct;
|
||||
out.weaponPct = (effective - 1.0f) * damagePct;
|
||||
}
|
||||
|
||||
// Les familiers derivent leurs PV et leur puissance d'attaque de ceux du maitre
|
||||
// (cf. Guardian::UpdateMaxHealth / Guardian::UpdateAttackPowerAndDamage), mais ils ne
|
||||
// recalculent pas spontanement quand le maitre est buffe : un pet deja invoque a
|
||||
// l'entree en instance restait a x1. Chasseur, demoniste, DK impie et chaman elem
|
||||
// perdaient ainsi une grosse part de leurs degats -- d'ou l'ecart ressenti entre classes.
|
||||
void RefreshPets(Player* player)
|
||||
{
|
||||
if (Pet* pet = player->GetPet())
|
||||
pet->UpdateAllStats();
|
||||
|
||||
if (Guardian* guardian = player->GetGuardianPet())
|
||||
if (guardian != player->GetPet())
|
||||
guardian->UpdateAllStats();
|
||||
}
|
||||
|
||||
void ApplyModifiers(Player* player, SolocraftBuff const& buff, bool apply)
|
||||
{
|
||||
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
|
||||
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, buff.statPct, apply);
|
||||
|
||||
if (buff.weaponPct != 0.0f)
|
||||
{
|
||||
player->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, buff.weaponPct, apply);
|
||||
player->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, buff.weaponPct, apply);
|
||||
player->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, buff.weaponPct, apply);
|
||||
}
|
||||
|
||||
RefreshPets(player);
|
||||
}
|
||||
|
||||
// Retire le buff en cours s'il y en a un. Retourne true si quelque chose a ete retire.
|
||||
bool RemoveBuff(Player* player, bool announce)
|
||||
{
|
||||
SolocraftBuff previous;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_buffMutex);
|
||||
std::map<uint64, SolocraftBuff>::iterator itr = _buffs.find(player->GetGUID());
|
||||
if (itr == _buffs.end())
|
||||
return false;
|
||||
|
||||
previous = itr->second;
|
||||
_buffs.erase(itr);
|
||||
}
|
||||
|
||||
ApplyModifiers(player, previous, false);
|
||||
|
||||
TC_LOG_DEBUG("scripts.solocraft", "[Solocraft] Retrait joueur=%s statPct=%.1f weaponPct=%.1f maxHP=%u",
|
||||
player->GetName().c_str(), previous.statPct, previous.weaponPct, (uint32)player->GetMaxHealth());
|
||||
|
||||
if (announce && sConfigMgr->GetBoolDefault("Solocraft.Announce", false) && player->GetSession())
|
||||
ChatHandler(player->GetSession()).PSendSysMessage("|cffFF0000[SoloCraft]|r |cffFF8000Bonus retiré (x%0.2f).|r",
|
||||
previous.effective);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Recalcule et applique le buff correspondant a la situation actuelle du joueur.
|
||||
void RefreshBuff(Player* player, bool announce)
|
||||
{
|
||||
if (!player || !IsEnabled())
|
||||
return;
|
||||
|
||||
Map* map = player->GetMap();
|
||||
if (!map)
|
||||
return;
|
||||
|
||||
float rawDifficulty = ReadRawDifficulty(map);
|
||||
uint32 numPlayers = CountPlayersInInstance(player, map);
|
||||
|
||||
SolocraftBuff buff;
|
||||
ComputeMultipliers(rawDifficulty, numPlayers, buff);
|
||||
|
||||
// Rien a faire : pas de buff en cours et aucun a appliquer (cas de l'immense
|
||||
// majorite des appels -- monde ouvert, champs de bataille, groupes de bots).
|
||||
if (buff.statPct == 0.0f && buff.weaponPct == 0.0f)
|
||||
{
|
||||
RemoveBuff(player, announce);
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveBuff(player, false);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_buffMutex);
|
||||
_buffs[player->GetGUID()] = buff;
|
||||
}
|
||||
|
||||
TC_LOG_DEBUG("scripts.solocraft", "[Solocraft] Application joueur=%s carte=%s brut=%.2f joueurs=%u effectif=%.2f statPct=%.1f weaponPct=%.1f maxHPavant=%u",
|
||||
player->GetName().c_str(), map->GetMapName(), rawDifficulty, numPlayers,
|
||||
buff.effective, buff.statPct, buff.weaponPct, (uint32)player->GetMaxHealth());
|
||||
|
||||
ApplyModifiers(player, buff, true);
|
||||
|
||||
// Ne jamais soigner a plein en combat : sans ce garde, inviter puis exclure un
|
||||
// joueur en plein combat de boss offrirait un heal complet gratuit a repetition.
|
||||
if (!player->IsInCombat())
|
||||
{
|
||||
player->SetFullHealth();
|
||||
if (player->GetPowerType() == POWER_MANA)
|
||||
player->SetPower(POWER_MANA, player->GetMaxPower(POWER_MANA));
|
||||
}
|
||||
|
||||
if (announce && sConfigMgr->GetBoolDefault("Solocraft.Announce", false) && player->GetSession())
|
||||
{
|
||||
ChatHandler(player->GetSession()).PSendSysMessage(
|
||||
"|cffFF0000[SoloCraft]|r |cffFF8000%s - joueurs présents : %u - bonus x%0.2f (dégâts d'arme x%0.2f).|r",
|
||||
map->GetMapName(), numPlayers, buff.effective, 1.0f + buff.weaponPct / 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
class solocraft_player_instance_handler : public PlayerScript
|
||||
{
|
||||
public:
|
||||
|
||||
solocraft_player_instance_handler() : PlayerScript("solocraft_player_instance_handler")
|
||||
{
|
||||
TC_LOG_INFO("scripts.solocraft.player.instance", "[Solocraft] solocraft_player_instance_handler Loaded");
|
||||
TC_LOG_INFO("scripts.solocraft", "[Solocraft] solocraft_player_instance_handler Loaded");
|
||||
}
|
||||
|
||||
void OnMapChanged(Player* player) override
|
||||
{
|
||||
TC_LOG_INFO("server.worldserver", "[SoloDBG] OnMapChanged player=%s mapId=%u maxHP=%u", player->GetName().c_str(), player->GetMapId(), (uint32)player->GetMaxHealth());
|
||||
if (sConfigMgr->GetBoolDefault("Solocraft.Enable", false))
|
||||
{
|
||||
Map* map = player->GetMap();
|
||||
float difficulty = CalculateDifficulty(map, player);
|
||||
int numInGroup = GetNumInGroup(player);
|
||||
ApplyBuffs(player, map, difficulty, numInGroup);
|
||||
}
|
||||
RefreshBuff(player, true);
|
||||
}
|
||||
|
||||
void OnLogout(Player* player) override
|
||||
{
|
||||
// Les modificateurs de stats (TOTAL_PCT) sont remis a 1.0 a chaque login,
|
||||
// mais _unitDifficulty (statique) survivait a la deconnexion. Un ClearBuffs
|
||||
// ulterieur retrouvait alors une entree fantome et DIVISAIT des stats jamais
|
||||
// buffees (perso niveau 1 rabaisse a 20% de ses stats -> 28 PV, cf. bug Selena).
|
||||
_unitDifficulty.erase(player->GetGUID());
|
||||
// mais l'entree du conteneur survivait a la deconnexion. Un retrait ulterieur
|
||||
// retrouvait alors une entree fantome et DIVISAIT des stats jamais buffees
|
||||
// (perso niveau 1 rabaisse a 20% de ses stats -> 28 PV, cf. bug Selena).
|
||||
std::lock_guard<std::mutex> lock(_buffMutex);
|
||||
_buffs.erase(player->GetGUID());
|
||||
}
|
||||
};
|
||||
|
||||
// Sans ce script, rejoindre ou quitter un groupe en pleine instance ne recalculait
|
||||
// rien : un joueur entre seul (x5) gardait son bonus solo apres avoir invite quatre
|
||||
// amis, et l'inverse -- un joueur reste seul apres le depart du groupe conservait un
|
||||
// bonus divise par 5, ce qui rendait le donjon injouable.
|
||||
class solocraft_group_handler : public GroupScript
|
||||
{
|
||||
public:
|
||||
solocraft_group_handler() : GroupScript("solocraft_group_handler") { }
|
||||
|
||||
void OnAddMember(Group* group, ObjectGuid guid) override
|
||||
{
|
||||
RefreshGroup(group, guid);
|
||||
}
|
||||
|
||||
void OnRemoveMember(Group* group, ObjectGuid guid, RemoveMethod /*method*/, ObjectGuid /*kicker*/, char const* /*reason*/) override
|
||||
{
|
||||
RefreshGroup(group, guid);
|
||||
}
|
||||
|
||||
void OnDisband(Group* group) override
|
||||
{
|
||||
RefreshGroup(group, ObjectGuid::Empty);
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<uint64, float> _unitDifficulty;
|
||||
|
||||
// Set the instance difficulty
|
||||
float CalculateDifficulty(Map* map, Player* player)
|
||||
static void RefreshGroup(Group* group, ObjectGuid changedMember)
|
||||
{
|
||||
// Valeurs lues a CHAQUE changement de carte (evenement peu frequent) plutot qu'une seule fois
|
||||
// au boot -> 'reload config' (tmux: reload config) suffit desormais pour tuner, sans restart.
|
||||
const float D5 = sConfigMgr->GetFloatDefault("Solocraft.Dungeon", 5.0f);
|
||||
const float D5H = sConfigMgr->GetFloatDefault("Solocraft.Heroic", 10.0f);
|
||||
const float D10 = sConfigMgr->GetFloatDefault("Solocraft.Raid10", 10.0f);
|
||||
const float D25 = sConfigMgr->GetFloatDefault("Solocraft.Raid25", 25.0f);
|
||||
const float D30 = sConfigMgr->GetFloatDefault("Solocraft.Raid30", 30.0f);
|
||||
const float D40 = sConfigMgr->GetFloatDefault("Solocraft.Raid40", 40.0f);
|
||||
float difficulty = 1.0;
|
||||
if (map)
|
||||
{
|
||||
// Pas de Solocraft en Mythique / Mythique+ : Map::IsMythic() couvre le donjon Mythique (23),
|
||||
// la clef Mythique+ (DIFFICULTY_MYTHIC_KEYSTONE 8) et le raid Mythique. Contenu reserve au groupe.
|
||||
// On retourne 0 (aucun buff) ; ApplyBuffs/ClearBuffs retirera tout buff Solocraft anterieur.
|
||||
if (map->IsMythic())
|
||||
return 0.0f;
|
||||
if (!IsEnabled())
|
||||
return;
|
||||
|
||||
if (map->IsRaid())
|
||||
{
|
||||
switch (map->GetMapDifficulty()->MaxPlayers)
|
||||
{
|
||||
case 10:
|
||||
difficulty = D10; break;
|
||||
case 25:
|
||||
difficulty = D25; break;
|
||||
case 30:
|
||||
difficulty = D30; break;
|
||||
case 40:
|
||||
difficulty = D40; break;
|
||||
default:
|
||||
TC_LOG_WARN("scripts.solocraft.player.instance", "[SoloCraft] Unrecognized max players %d, defaulting to 10 man difficulty",
|
||||
map->GetMapDifficulty()->MaxPlayers);
|
||||
difficulty = D10;
|
||||
}
|
||||
}
|
||||
else if (map->IsDungeon())
|
||||
{
|
||||
if (map->IsHeroic())
|
||||
difficulty = D5H;
|
||||
else
|
||||
difficulty = D5;
|
||||
}
|
||||
}
|
||||
return difficulty - 1.0; // HandleStatModifier treats 100 as +100%, so a difficulty of 1 ends up being applied as a 200% buff
|
||||
}
|
||||
|
||||
// Get the groups size
|
||||
int GetNumInGroup(Player* player)
|
||||
{
|
||||
int numInGroup = 1;
|
||||
Group* group = player->GetGroup();
|
||||
if (group)
|
||||
{
|
||||
Group::MemberSlotList const& groupMembers = group->GetMemberSlots();
|
||||
numInGroup = groupMembers.size();
|
||||
}
|
||||
return numInGroup;
|
||||
}
|
||||
|
||||
// Apply the player buffs
|
||||
void ApplyBuffs(Player* player, Map* map, float difficulty, int numInGroup)
|
||||
{
|
||||
ClearBuffs(player, map);
|
||||
|
||||
if (difficulty != 0)
|
||||
{
|
||||
// InstanceMap *instanceMap = map->ToInstanceMap();
|
||||
// InstanceScript *instanceScript = instanceMap->GetInstanceScript();
|
||||
|
||||
// Announce to player
|
||||
std::ostringstream ss;
|
||||
ss << "|cffFF0000[SoloCraft] |cffFF8000" << player->GetName() << " entered %s - # of Players: %d - Difficulty Offset: %0.2f.";
|
||||
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str(), map->GetMapName(), numInGroup, difficulty + 1.0);
|
||||
|
||||
// Adjust player stats
|
||||
_unitDifficulty[player->GetGUID()] = difficulty;
|
||||
TC_LOG_INFO("server.worldserver", "[SoloDBG] ApplyBuffs player=%s diff=%.2f stamPctBefore=%.3f maxHPbefore=%u", player->GetName().c_str(), difficulty, player->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_PCT), (uint32)player->GetMaxHealth());
|
||||
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
|
||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
// Buff the player
|
||||
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, difficulty * 100.0, true);
|
||||
}
|
||||
TC_LOG_INFO("server.worldserver", "[SoloDBG] ApplyBuffs DONE player=%s stamPctAfter=%.3f maxHPafter=%u", player->GetName().c_str(), player->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_PCT), (uint32)player->GetMaxHealth());
|
||||
|
||||
// Set player health
|
||||
player->SetFullHealth();
|
||||
if (player->GetPowerType() == POWER_MANA)
|
||||
{
|
||||
// Buff the player's health
|
||||
player->SetPower(POWER_MANA, player->GetMaxPower(POWER_MANA));
|
||||
Player* member = itr->GetSource();
|
||||
if (member && member->IsInWorld() && member->GetMap() && member->GetMap()->IsDungeon())
|
||||
RefreshBuff(member, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBuffs(Player* player, Map* map)
|
||||
{
|
||||
std::map<uint64, float>::iterator unitDifficultyIterator = _unitDifficulty.find(player->GetGUID());
|
||||
TC_LOG_INFO("server.worldserver", "[SoloDBG] ClearBuffs player=%s found=%d stamPct=%.3f maxHP=%u", player->GetName().c_str(), unitDifficultyIterator != _unitDifficulty.end() ? 1 : 0, player->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_PCT), (uint32)player->GetMaxHealth());
|
||||
if (unitDifficultyIterator != _unitDifficulty.end())
|
||||
{
|
||||
float difficulty = unitDifficultyIterator->second;
|
||||
_unitDifficulty.erase(unitDifficultyIterator);
|
||||
|
||||
// Inform the player
|
||||
std::ostringstream ss;
|
||||
ss << "|cffFF0000[SoloCraft] |cffFF8000" << player->GetName() << " exited to %s - Reverting Difficulty Offset: %0.2f.";
|
||||
ChatHandler(player->GetSession()).PSendSysMessage(ss.str().c_str(), map->GetMapName(), difficulty + 1.0);
|
||||
|
||||
// Clear the buffs
|
||||
for (int32 i = STAT_STRENGTH; i < MAX_STATS; ++i)
|
||||
{
|
||||
player->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_PCT, difficulty * 100.0, false);
|
||||
}
|
||||
}
|
||||
// Le membre concerne n'est deja (ou pas encore) plus dans la liste : il se
|
||||
// retrouve peut-etre seul dans l'instance et doit etre recalcule a part.
|
||||
if (!changedMember.IsEmpty())
|
||||
if (Player* changed = ObjectAccessor::FindPlayer(changedMember))
|
||||
if (changed->IsInWorld() && changed->GetMap() && changed->GetMap()->IsDungeon())
|
||||
RefreshBuff(changed, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -186,4 +337,5 @@ namespace {
|
||||
void AddSC_solocraft()
|
||||
{
|
||||
new solocraft_player_instance_handler();
|
||||
new solocraft_group_handler();
|
||||
}
|
||||
|
||||
@@ -4282,16 +4282,49 @@ Solocraft.Enable = 1
|
||||
Solocraft.Announce = 1
|
||||
|
||||
# Difficulty Multipliers
|
||||
# Description: Difficulty multipliers for dungeons.
|
||||
# Defaults: 5, 10, 10, 25, 30, 40
|
||||
|
||||
# Description: Pour COMBIEN DE JOUEURS le contenu est calibre. Le bonus reellement
|
||||
# accorde vaut cette valeur DIVISEE par le nombre de membres du groupe
|
||||
# presents dans l'instance.
|
||||
# Solocraft.Dungeon = 5 -> joueur seul : x5 ; groupe de 5 : x1 (aucun bonus)
|
||||
# Solocraft.Raid25 = 25 -> groupe de 10 : x2.5
|
||||
# Solocraft.Heroic peut depasser 5 pour compenser la difficulte accrue
|
||||
# des mecaniques heroiques.
|
||||
# Defaults: 5, 8, 10, 25, 30, 40
|
||||
|
||||
Solocraft.Dungeon = 5
|
||||
Solocraft.Heroic = 10
|
||||
Solocraft.Heroic = 8
|
||||
Solocraft.Raid10 = 10
|
||||
Solocraft.Raid25 = 25
|
||||
Solocraft.Raid30 = 30
|
||||
Solocraft.Raid40 = 40
|
||||
|
||||
# Solocraft.Mythic
|
||||
# Description: Bonus en donjon Mythique / clef Mythique+ / raid Mythique.
|
||||
# 1.0 = aucun bonus (contenu reserve au groupe complet).
|
||||
# Default: 1.0
|
||||
|
||||
Solocraft.Mythic = 1.0
|
||||
|
||||
# Solocraft.StatsPct
|
||||
# Description: Part du bonus (en %) appliquee aux stats primaires -- elles portent les
|
||||
# PV via l'endurance, la puissance des sorts et la puissance d'attaque.
|
||||
# 100 = un joueur seul en donjon a bien x5 PV.
|
||||
# Default: 100
|
||||
|
||||
Solocraft.StatsPct = 100
|
||||
|
||||
# Solocraft.DamagePct
|
||||
# Description: Part du bonus (en %) appliquee aux degats d'arme. Les degats d'une
|
||||
# attaque valent (degats_arme + AP/3.5) et seule la moitie "AP" profite du
|
||||
# bonus de stats : sans ce reglage le joueur encaisse x5 mais tape a peine
|
||||
# plus fort (contenu ressenti comme hardcore). 20 vise environ x5 degats
|
||||
# en solo, tous bonus cumules.
|
||||
# Monter cette valeur si les combats restent trop longs, la baisser si le
|
||||
# contenu devient trivial.
|
||||
# Default: 20
|
||||
|
||||
Solocraft.DamagePct = 20
|
||||
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
Reference in New Issue
Block a user