735 lines
27 KiB
C#
735 lines
27 KiB
C#
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using Dalamud.Game.Addon.Lifecycle;
|
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
|
using Dalamud.Game.Gui.ContextMenu;
|
|
using Dalamud.Game.Text;
|
|
using Dalamud.IoC;
|
|
using Dalamud.Memory;
|
|
using Dalamud.Plugin;
|
|
using Dalamud.Plugin.Services;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
|
using FFXIVClientStructs.FFXIV.Client.Graphics;
|
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
|
|
namespace PartyDamage;
|
|
|
|
public class Plugin : IDalamudPlugin {
|
|
[PluginService]
|
|
internal static IPluginLog Log { get; private set; }
|
|
|
|
[PluginService]
|
|
private IAddonLifecycle AddonLifecycle { get; init; }
|
|
|
|
[PluginService]
|
|
private IClientState ClientState { get; init; }
|
|
|
|
[PluginService]
|
|
private IFramework Framework { get; init; }
|
|
|
|
[PluginService]
|
|
private IPartyList PartyList { get; init; }
|
|
|
|
[PluginService]
|
|
internal IDalamudPluginInterface Interface { get; init; }
|
|
|
|
[PluginService]
|
|
internal ICommandManager CommandManager { get; init; }
|
|
|
|
[PluginService]
|
|
internal IContextMenu ContextMenu { get; init; }
|
|
|
|
internal Configuration Config { get; }
|
|
private Client Client { get; }
|
|
internal PluginUi Ui { get; }
|
|
private Commands Commands { get; }
|
|
|
|
private Stopwatch AlternateWatch { get; } = Stopwatch.StartNew();
|
|
private Stopwatch DelayWatch { get; } = new();
|
|
private bool _ranLastTick;
|
|
private bool _showDps = true;
|
|
private bool _reset;
|
|
private bool _wasActive;
|
|
private bool _manuallyReset = true;
|
|
|
|
private readonly byte[] _manaUsers = [
|
|
6, // cnj
|
|
7, // thm
|
|
19, // pld
|
|
24, // whm
|
|
25, // blm
|
|
26, // acn
|
|
27, // smn
|
|
28, // sch
|
|
32, // drk
|
|
33, // ast
|
|
35, // rdm
|
|
36, // blu
|
|
40, // sge
|
|
42, // pct
|
|
];
|
|
|
|
public Plugin() {
|
|
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
|
|
this.Client = new Client();
|
|
this.Ui = new PluginUi(this);
|
|
this.Commands = new Commands(this);
|
|
|
|
this.AddonLifecycle!.RegisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList);
|
|
this.ContextMenu!.OnMenuOpened += this.MenuOpened;
|
|
}
|
|
|
|
public void Dispose() {
|
|
this.ContextMenu.OnMenuOpened -= this.MenuOpened;
|
|
this.AddonLifecycle.UnregisterListener(AddonEvent.PostUpdate, "_PartyList", this.UpdateList);
|
|
this.Commands.Dispose();
|
|
this.Ui.Dispose();
|
|
this.Client.Dispose();
|
|
}
|
|
|
|
internal void SaveConfig() {
|
|
this.Interface.SavePluginConfig(this.Config);
|
|
}
|
|
|
|
private unsafe void MenuOpened(IMenuOpenedArgs args) {
|
|
var add = args.AddonName == "_PartyList";
|
|
if (!add) {
|
|
var ctx = AgentContext.Instance();
|
|
if (args.AgentPtr != (nint) ctx) {
|
|
return;
|
|
}
|
|
|
|
var targetId = ctx->TargetObjectId.ObjectId;
|
|
if (targetId == 0xE000_0000) {
|
|
return;
|
|
}
|
|
|
|
if (this.ClientState.LocalPlayer is { } player && player.GameObjectId == targetId) {
|
|
add = true;
|
|
} else {
|
|
var group = GroupManager.Instance()->GetGroup();
|
|
if (group != null && group->GetPartyMemberByEntityId(targetId) != null) {
|
|
add = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!add) {
|
|
return;
|
|
}
|
|
|
|
args.AddMenuItem(new MenuItem {
|
|
Name = "Clear parse results",
|
|
Prefix = SeIconChar.BoxedLetterP,
|
|
PrefixColor = 37,
|
|
IsEnabled = this.Client.Data?.IsActive == false,
|
|
OnClicked = _ => {
|
|
this._reset = true;
|
|
this._manuallyReset = true;
|
|
},
|
|
});
|
|
}
|
|
|
|
private unsafe void UpdateList(AddonEvent type, AddonArgs args) {
|
|
var ranLast = this._ranLastTick;
|
|
this._ranLastTick = false;
|
|
var list = (AddonPartyList*) AtkStage.Instance()->RaptureAtkUnitManager->GetAddonByName("_PartyList");
|
|
if (list == null) {
|
|
return;
|
|
}
|
|
|
|
var shouldUpdate = false;
|
|
|
|
var wasActive = this._wasActive;
|
|
var becameInactive = false;
|
|
if (this.Client.Data is { } data) {
|
|
if (wasActive && !data.IsActive) {
|
|
becameInactive = true;
|
|
}
|
|
|
|
this._wasActive = data.IsActive;
|
|
}
|
|
|
|
if (this.Client.Data is { IsActive: true }) {
|
|
shouldUpdate = true;
|
|
this._manuallyReset = false;
|
|
} else if (becameInactive) {
|
|
if (this.Config.ClearResultsOnInactive) {
|
|
this.DelayWatch.Restart();
|
|
}
|
|
|
|
if (this.Config.UseEvaluatorNpc && this.Client.Data != null) {
|
|
var combatants = this.Client.Data.Combatants.Values.ToList();
|
|
combatants.Sort((a, b) => b.EncDps.CompareTo(a.EncDps));
|
|
var rank = combatants.FindIndex(combatant => combatant.Name == "YOU");
|
|
// 001 Venat
|
|
// 002 Zenos
|
|
// 003 Evil? Zenos
|
|
// 004 Fandaniel
|
|
// 005 ? some garlean
|
|
// 006 ?
|
|
// 007 Estinien
|
|
// 008 Alphinaud
|
|
// 009 radiant host dude
|
|
// 010 Forchenault
|
|
// 011 Alisaie
|
|
// 012 G'raha Tia
|
|
// 013 Thancred
|
|
// 014 oh god I should know his name
|
|
// 015 one of the padjali, the boy
|
|
// 016 Emmanelaine or however you spell it
|
|
// 017 ?
|
|
// 018 Lucia
|
|
// 019 Pipin?
|
|
// 020 Lyse
|
|
// 021 Magnai for sure
|
|
// 022 Sadu
|
|
// 023 Cirina
|
|
// 024 Thancred again
|
|
// 025 Y'shtola
|
|
// 026 Urianger
|
|
// 027 Nero with helmet on
|
|
// 028 different alphinaud's dad
|
|
// 029 Kan-E-Senna
|
|
// 030 Aymeric
|
|
// 031 ?
|
|
// 032 oh god I should know his name... stormblood main boy
|
|
// 033 ? (monster)
|
|
// 034 Alphinaud again
|
|
// 035 Estinien casual
|
|
// 036 Alisaie cold attire
|
|
// 037 some kobold
|
|
// 038 bearded roe
|
|
// 039 ?
|
|
// 040 ardbert with ascian mask?
|
|
// 041 ardbert but creepy smile
|
|
// 042 ?
|
|
// 043 oh god some fviera
|
|
// 044 another fviera
|
|
// 045 yet another fviera
|
|
// 046 ? soranus
|
|
// 047 gaius?
|
|
// 048 imperial buckethead
|
|
// 049 imperial helmet
|
|
// 050 ? soranus
|
|
// 051 imperial helmet but less aggro
|
|
// 052 Lyna
|
|
// 053 Alphinaud in a red shawl
|
|
// 054 mustachioed-man with horn helmet
|
|
// 055 different horn helmet
|
|
// 056 Alisaie in a red a shawl
|
|
// 057 horn helmet
|
|
// 058 horn helmet
|
|
// 059 fu manchu man
|
|
// 060 Minfilia (Ryne)
|
|
// 061 Alisaie cold attire
|
|
// 062 Alphi cold attire
|
|
// 063 Ranjit?
|
|
// 064 Ryne
|
|
// 065 Female Jester red
|
|
// 066 Female Jester blue
|
|
// 067 a dwarf
|
|
// 068 Fandaniel hood covering eyes
|
|
// 069 Merlwyb
|
|
// 070 Vrtra boy avatar
|
|
// 071 Krile
|
|
// 072 Tataru
|
|
// 073 Hydaelyn?
|
|
// 074 Faurchenault
|
|
// 075 Vrtra
|
|
// 076 some boss
|
|
// 077 Zero
|
|
// 078 Vrtra man avatar
|
|
// 079 Zenos Helmet?
|
|
// 080 Lyse in red dress
|
|
// 081 Yugiri
|
|
// 082 Zenos? helmet revealing eyes
|
|
// 083 Sadu
|
|
// 084 some blue au ra male
|
|
// 085 Y'shtola
|
|
// 086 garlean guy
|
|
// 087 garlean pot helmet
|
|
// 088 garlean bucket helmet
|
|
// 089 garlean pot helmet
|
|
// 090 purple haired chin strap guy
|
|
// 091 garlean bucket helmet
|
|
// 092 evil oni?
|
|
// 093 Asahi
|
|
// 094 Lakshmi
|
|
// 095 cripple man
|
|
// 096 criminal woman
|
|
// 097 ?
|
|
// 098 some machine?
|
|
// 099 some guy with radiation hair
|
|
// 100 Krile but happy
|
|
// 101 lalafell in horn helmet
|
|
// 102 some ala mhigan resistance guy
|
|
// 103 criminal lady again
|
|
// 104 Lyse in red jacket
|
|
// 105 green Kojin main guy
|
|
// 106 Yotsuyu
|
|
// 107 magnai
|
|
// 108 Gosetsu
|
|
// 109 old guy
|
|
// 110 imperial bucket head
|
|
// 111 some guy with a scar maybe ala mhigan resistance
|
|
// 112 Thancred with blindfold?
|
|
// 113 face mask ala mhigan resistance
|
|
// 114 imperial pot helmet
|
|
// 115 lupin guy
|
|
// 116 another lupin
|
|
// 117 imperial pot helmet but roe
|
|
// 118 straw hat
|
|
// 119 some boy
|
|
// 120 Raubahn
|
|
// 121 Sultana?
|
|
// 122 elezen man
|
|
// 123 angry bird thing
|
|
// 124 Haurchefant
|
|
// 125 Tataru
|
|
// 126 no idea elezen
|
|
// 127 eyepatch elezen
|
|
// 128 Alphinaud old outfit
|
|
// 129 Yugiri with dagger
|
|
// 130 Raubahn maybe with two arms?
|
|
// 131 some uldahn lalafell
|
|
// 132 Ilberd
|
|
// 133 a crystal brave
|
|
// 134 diff crystal brave
|
|
// 135 woman crystal brave
|
|
// 136 Estinien full armour
|
|
// 137 some dude in chain mail elezen face plate
|
|
// 138 someone other person in same outfit not elezen maybe hyur
|
|
// 139 Hilda
|
|
// 140 elezen guy
|
|
// 141 ishgardian soldier face plate chain mail
|
|
// 142 a vanu vanu or gundu or whatever
|
|
// 143 goblin brayflox
|
|
// 144 the machine boss from brayflox hard
|
|
// 145 imperial in cool helmet (not gaius?)
|
|
// 146 red dragoon but doesn't look like estinien
|
|
// 147 some child
|
|
// 148 some old elezen
|
|
// 149 emmanelaine's brother
|
|
// 150 old elezen woman
|
|
// 151 someone in a hat
|
|
// 152 other person in a hat
|
|
// 153 Raubahn in big helmet
|
|
// 154 Ardbert?
|
|
// 155 old outfit Urianger no mask
|
|
// 156 Yda
|
|
// 157 imperial pot helmet long hair
|
|
// 158 ???
|
|
// 159 ???
|
|
// 160 mammet with green face
|
|
// 161 Hancock
|
|
// 162 pink haired fem elezen
|
|
// 163 not sure dragoon
|
|
// 164 Papalymo
|
|
// 165 old Thancred
|
|
// 166 some elezen girl
|
|
// 167 Yugiri in full face mask
|
|
// 168 roe in big plate armour
|
|
// 169 child f (doman?)
|
|
// 170 another child m
|
|
// 171 m elezen caster in witch's hat
|
|
// 172 another child m/f
|
|
// 173 another child m
|
|
// 174 Thancred ascian mask
|
|
// 175 full armour person
|
|
// 176 red imperial buckethead
|
|
// 177 Minfilia (original)
|
|
// 178 Urianger (old outfit with mask thing)
|
|
// 179 imperial pot helmet
|
|
// 180 imperial pot helmet
|
|
// 181 biggs or wedge (the lala)
|
|
// 182 biggs or wedge (the roe)
|
|
// 183 imperial buckethead
|
|
// 184 Cid (old outfit)
|
|
// 185 imperial pot helmet
|
|
// 186 imperial pot helmet (roe)
|
|
// 187 Y'shtola with normal sight and aetherometer or whatever
|
|
// 188 crazy hair girl
|
|
// 189 an elezen with a beard whoa
|
|
// 190 some kind of monster? maybe old mamool ja
|
|
// 191 Haurchefant in chain mail
|
|
// 192 Haurchefant in chain mail
|
|
// 193 elezen with a soul patch
|
|
// 194 young elezen (emman?)
|
|
// 195 elezen with awful facial hair
|
|
// 196 roe with straw hat (not the ronin kind)
|
|
// 197 generic man
|
|
// 198 generic anime man
|
|
// 199 Ranjit
|
|
// 200 Amalj'aa
|
|
// 201 shirtless roe man
|
|
// 202 different shirtless roe man
|
|
// 203 mutton chops guy
|
|
// 204 elezen with a sick red beard whoa
|
|
// 205 imperial pot helmet roe beard
|
|
// 206 creepy mask miqo
|
|
// 207 sylph purple
|
|
// 208 sylph green
|
|
// 209 weird monster guy
|
|
// 210 Erenville
|
|
// 211 Wuk Lamat (with hat)
|
|
// 212 giant Toucan
|
|
// 213 Arkasodara
|
|
// 214 some guy
|
|
// 215 imperial pot helmet
|
|
// 216 gridanian guy?
|
|
// 217 lady with cap
|
|
// 218 man with cap
|
|
// 219 roe with hat
|
|
// 220 lalafell with pot helm
|
|
// 221 miqo with pot helm
|
|
// 222 dude with mask
|
|
// 223 girl with pot helm
|
|
// 224 lala with pot helm
|
|
// 225 guy with cool hair
|
|
// 226 dude with pot helm
|
|
// 227 lala pot helm
|
|
// 228 hyur pot helm
|
|
// 229 pot helm
|
|
// 230 roe with bandana mask
|
|
// 231 yet another pot helm
|
|
// 232 poooot helm
|
|
// 233 thief rogue?
|
|
// 234 full face mask ascian with red floaty mask and hood
|
|
// 235 POT. HELM.
|
|
// 236 ph
|
|
// 237 roe man
|
|
// 238 sailor roe man
|
|
// 239 maelstrom roe lady
|
|
// 240 Mamool Ja
|
|
// 241 pot helm
|
|
// 242 creepy mask guy
|
|
// 243 wood wailer creepy mask
|
|
// 244 ix...al?
|
|
// 245 cfm
|
|
// 246 cfm ww
|
|
// 247 elezen with face paint
|
|
// 248 Moogle
|
|
// 249 lala with tiara and mustache
|
|
// 250 boy
|
|
// 251 hooded m roe
|
|
// 252 hyur m
|
|
// 253 hyur m
|
|
// 254 Papa...lymo?
|
|
// 255 dude
|
|
// 256 Emet Selch elpis version
|
|
// 257 Krile
|
|
// 258 Gulool Ja Ja with cover for dead head
|
|
// 259 some mamool ja
|
|
// 260 Bakool Ja Ja mystic
|
|
// 261 Bakool Ja Ja both heads
|
|
// 262 a mamool ja
|
|
// 263 another mamool ja
|
|
// 264 another mamool ja
|
|
// 265 Wuk Lamat without hat
|
|
// 266 Koana
|
|
// 267 old Gulool Ja Ja with both heads alive
|
|
// 268 head of resolve
|
|
// 269 head of reason
|
|
// 270 Sphene
|
|
// 271 evil Otis in machine form
|
|
// 272 good Otis in machine form
|
|
// 273 Sphene final boss form
|
|
// 274 looks like Zero kind of
|
|
// "73" + str(number) for index
|
|
var msg = rank switch {
|
|
< 0 => null,
|
|
1 => "Well done! I never doubted for a second that you'd do so well.",
|
|
_ => "Keep trying, friend! You'll get there in the end.",
|
|
};
|
|
UIModule.Instance()->ShowBattleTalkImage("G'raha Tia", msg, 5f, 73012, 0);
|
|
}
|
|
}
|
|
|
|
if (this.Config.ClearResultsOnInactive && this.DelayWatch.IsRunning && this.DelayWatch.Elapsed >= TimeSpan.FromSeconds(this.Config.ClearDelaySeconds)) {
|
|
this.DelayWatch.Reset();
|
|
this._reset = true;
|
|
}
|
|
|
|
if (!this._reset && !this._manuallyReset) {
|
|
// only do these checks if we shouldn't reset and if we're waiting for active data
|
|
|
|
// keep running if we're in the delay period
|
|
if (this.DelayWatch.IsRunning) {
|
|
shouldUpdate = true;
|
|
}
|
|
|
|
// keep running if the user wants to manually clear results
|
|
if (!this.Config.ClearResultsOnInactive) {
|
|
shouldUpdate = true;
|
|
}
|
|
}
|
|
|
|
if (this._reset) {
|
|
this.DelayWatch.Reset();
|
|
this._reset = false;
|
|
this._manuallyReset = true;
|
|
this.ResetMembers(list);
|
|
}
|
|
|
|
if (shouldUpdate && this.UpdateListInner(list) && ranLast) {
|
|
this.ResetMembers(list);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the party list.
|
|
/// </summary>
|
|
/// <returns>true if the list should be reset immediately</returns>
|
|
private unsafe bool UpdateListInner(AddonPartyList* list) {
|
|
if (this.Client.Data is not { } data) {
|
|
return false;
|
|
}
|
|
|
|
if (this.ClientState.LocalPlayer is not { } player) {
|
|
return false;
|
|
}
|
|
|
|
var chara = (Character*) player.Address;
|
|
var playerName = player.Name.TextValue;
|
|
|
|
if (list->HoveredIndex >= 0 || list->TargetedIndex >= 0) {
|
|
return true;
|
|
}
|
|
|
|
var names = new List<(string, int)>();
|
|
var members = AgentHUD.Instance()->PartyMembers;
|
|
for (var i = 0; i < members.Length && i < list->PartyMembers.Length; i++) {
|
|
var member = members[i];
|
|
if (member.Name == null) {
|
|
continue;
|
|
}
|
|
|
|
// controller soft target not handled by hoveredindex above
|
|
if (chara->GetSoftTargetId() == member.EntityId) {
|
|
return true;
|
|
}
|
|
|
|
var name = MemoryHelper.ReadStringNullTerminated((nint) member.Name);
|
|
names.Add((name, i));
|
|
}
|
|
|
|
this._ranLastTick = true;
|
|
|
|
if (this.AlternateWatch.Elapsed.TotalMilliseconds > this.Config.AlternateSeconds * 1_000) {
|
|
this.AlternateWatch.Restart();
|
|
this._showDps ^= true;
|
|
}
|
|
|
|
for (var i = 0; i < names.Count; i++) {
|
|
var (name, membersIdx) = names[i];
|
|
var lookupName = name == playerName
|
|
? "YOU"
|
|
: name;
|
|
var member = members[membersIdx];
|
|
|
|
if (!data.Combatants.TryGetValue(lookupName, out var combatant)) {
|
|
continue;
|
|
}
|
|
|
|
this.UpdateMember(list, i, member.Object, data.Encounter, combatant);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private unsafe void ResetMember(
|
|
AddonPartyList* list,
|
|
BattleChara* chara,
|
|
int listIndex,
|
|
bool includeTargeted
|
|
) {
|
|
var unit = list->PartyMembers[listIndex];
|
|
if (unit.TargetGlow != null && (includeTargeted || list->TargetedIndex != listIndex)) {
|
|
unit.TargetGlow->SetAlpha(255);
|
|
unit.TargetGlow->SetScaleX(0);
|
|
unit.TargetGlow->AddRed = 0;
|
|
unit.TargetGlow->AddGreen = 0;
|
|
unit.TargetGlow->AddBlue = 0;
|
|
unit.TargetGlow->MultiplyRed = 100;
|
|
unit.TargetGlow->MultiplyGreen = 100;
|
|
unit.TargetGlow->MultiplyBlue = 100;
|
|
unit.TargetGlow->ToggleVisibility(true);
|
|
}
|
|
|
|
var left = (AtkTextNode*) unit.MPGaugeBar->GetTextNodeById(2);
|
|
var right = (AtkTextNode*) unit.MPGaugeBar->GetTextNodeById(3);
|
|
|
|
var hasLeft = left != null && left->IsVisible();
|
|
var hasRight = right != null && right->IsVisible();
|
|
var hasChara = chara != null;
|
|
|
|
if (!hasLeft) {
|
|
return;
|
|
}
|
|
|
|
left->TextColor = new ByteColor {
|
|
RGBA = 0xFFFFFFFF,
|
|
};
|
|
|
|
var manaString = hasChara
|
|
? chara->Mana.ToString(CultureInfo.InvariantCulture)
|
|
: "???";
|
|
if (hasRight) {
|
|
if (manaString.Length <= 1) {
|
|
left->SetText("");
|
|
right->SetText(manaString);
|
|
} else {
|
|
left->SetText(manaString[..^2]);
|
|
right->SetText(manaString[^2..]);
|
|
}
|
|
} else {
|
|
left->SetText(manaString);
|
|
}
|
|
|
|
left->AddRed = 0;
|
|
left->AddGreen = 0;
|
|
left->AddBlue = 0;
|
|
left->MultiplyRed = 100;
|
|
left->MultiplyGreen = 100;
|
|
left->MultiplyBlue = 100;
|
|
|
|
if (hasRight) {
|
|
right->TextColor = left->TextColor;
|
|
right->AddRed = left->AddRed;
|
|
right->AddGreen = left->AddGreen;
|
|
right->AddBlue = left->AddBlue;
|
|
right->MultiplyRed = left->MultiplyRed;
|
|
right->MultiplyGreen = left->MultiplyGreen;
|
|
right->MultiplyBlue = left->MultiplyBlue;
|
|
}
|
|
|
|
// fixme: need to figure out a way to get the game to repop
|
|
// var defaultName = new StringBuilder("\ue06a");
|
|
// foreach (var digit in member.Object->Level.ToString(CultureInfo.InvariantCulture)) {
|
|
// var offset = digit - '0';
|
|
// defaultName.Append((char) ('\ue060' + offset));
|
|
// }
|
|
|
|
// defaultName.Append(' ');
|
|
// defaultName.Append(member.Object->NameString);
|
|
|
|
// unit.Name->SetText(defaultName.ToString());
|
|
// unit.Name->TextColor = new ByteColor {
|
|
// RGBA = 0xFFFFFFFF,
|
|
// };
|
|
|
|
// unit.Name->AddRed = 0;
|
|
// unit.Name->AddGreen = 0;
|
|
// unit.Name->AddBlue = 0;
|
|
// unit.Name->MultiplyRed = 100;
|
|
// unit.Name->MultiplyGreen = 100;
|
|
// unit.Name->MultiplyBlue = 100;
|
|
}
|
|
|
|
private unsafe void ResetMembers(AddonPartyList* list) {
|
|
var members = AgentHUD.Instance()->PartyMembers;
|
|
for (var i = 0; i < members.Length && i < list->PartyMembers.Length; i++) {
|
|
this.ResetMember(list, members[i].Object, i, false);
|
|
}
|
|
}
|
|
|
|
private unsafe void UpdateMember(
|
|
AddonPartyList* list,
|
|
int listIndex,
|
|
BattleChara* chara,
|
|
Encounter encounter,
|
|
Combatant combatant
|
|
) {
|
|
var member = list->PartyMembers[listIndex];
|
|
if (this.Config.UseDpsBar && member.TargetGlow != null) {
|
|
member.TargetGlow->ToggleVisibility(true);
|
|
member.TargetGlow->SetAlpha((byte) Math.Round(this.Config.BarAlpha * 255));
|
|
member.TargetGlow->SetScaleX(
|
|
encounter.EncDps == 0
|
|
? 0
|
|
: combatant.EncDps / encounter.EncDps
|
|
);
|
|
member.TargetGlow->AddRed = (byte) Math.Clamp(this.Config.BarAddRed, 0, 255);
|
|
member.TargetGlow->AddGreen = (byte) Math.Clamp(this.Config.BarAddGreen, 0, 255);
|
|
member.TargetGlow->AddBlue = (byte) Math.Clamp(this.Config.BarAddBlue, 0, 255);
|
|
member.TargetGlow->MultiplyRed = (byte) Math.Clamp(this.Config.BarMulRed, 0, 100);
|
|
member.TargetGlow->MultiplyGreen = (byte) Math.Clamp(this.Config.BarMulGreen, 0, 100);
|
|
member.TargetGlow->MultiplyBlue = (byte) Math.Clamp(this.Config.BarMulBlue, 0, 100);
|
|
}
|
|
|
|
if (this.Config.Mode == MeterMode.Mana) {
|
|
var left = (AtkTextNode*) member.MPGaugeBar->GetTextNodeById(2);
|
|
var right = (AtkTextNode*) member.MPGaugeBar->GetTextNodeById(3);
|
|
var hasLeft = left != null && left->IsVisible();
|
|
var hasRight = right != null && right->IsVisible();
|
|
var hasChara = chara != null;
|
|
|
|
if (!hasLeft) {
|
|
return;
|
|
}
|
|
|
|
if (this.Config.Alternate && !this._showDps) {
|
|
var isCaster = hasChara && Array.IndexOf(this._manaUsers, chara->ClassJob) != -1;
|
|
if (!this.Config.ManaModeAlternateOnlyManaUsers || isCaster) {
|
|
this.ResetMember(list, chara, listIndex, true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
left->TextColor = new ByteColor {
|
|
RGBA = this.Config.TextColour,
|
|
};
|
|
left->AddRed = (byte) Math.Clamp(this.Config.TextAddRed, 0, 255);
|
|
left->AddGreen = (byte) Math.Clamp(this.Config.TextAddGreen, 0, 255);
|
|
left->AddBlue = (byte) Math.Clamp(this.Config.TextAddBlue, 0, 255);
|
|
left->MultiplyRed = (byte) Math.Clamp(this.Config.TextMulRed, 0, 100);
|
|
left->MultiplyGreen = (byte) Math.Clamp(this.Config.TextMulGreen, 0, 100);
|
|
left->MultiplyBlue = (byte) Math.Clamp(this.Config.TextMulBlue, 0, 100);
|
|
|
|
if (hasRight) {
|
|
right->TextColor = left->TextColor;
|
|
right->AddRed = left->AddRed;
|
|
right->AddGreen = left->AddGreen;
|
|
right->AddBlue = left->AddBlue;
|
|
right->MultiplyRed = left->MultiplyRed;
|
|
right->MultiplyGreen = left->MultiplyGreen;
|
|
right->MultiplyBlue = left->MultiplyBlue;
|
|
}
|
|
|
|
string leftText, rightText;
|
|
|
|
if (combatant.EncDps == 0 || float.IsInfinity(combatant.EncDps) || float.IsNaN(combatant.EncDps)) {
|
|
leftText = "0.";
|
|
rightText = "00";
|
|
} else if (combatant.EncDps < 1_000) {
|
|
var dps = Math.Round(combatant.EncDps * 100).ToString(CultureInfo.InvariantCulture);
|
|
leftText = $"{dps[..^2]}.";
|
|
rightText = dps[^2..];
|
|
} else if (combatant.EncDps < 1_000_000) {
|
|
var dps = Math.Round(combatant.EncDps / 100).ToString(CultureInfo.InvariantCulture);
|
|
leftText = $"{dps[..^1]}.";
|
|
rightText = $"{dps[^1..]}K";
|
|
} else if (combatant.EncDps < 1_000_000_000) {
|
|
var dps = Math.Round(combatant.EncDps / 100_000).ToString(CultureInfo.InvariantCulture);
|
|
leftText = $"{dps[..^1]}.";
|
|
rightText = $"{dps[^1..]}M";
|
|
} else {
|
|
var dps = Math.Round(combatant.EncDps / 100_000_000).ToString(CultureInfo.InvariantCulture);
|
|
leftText = $"{dps[..^1]}.";
|
|
rightText = $"{dps[^1..]}B";
|
|
}
|
|
|
|
if (hasRight) {
|
|
left->SetText(leftText);
|
|
right->SetText(rightText);
|
|
} else {
|
|
left->SetText($"{leftText}{rightText}");
|
|
}
|
|
}
|
|
}
|
|
}
|