Skip to content

Commit

Permalink
Merge pull request #469 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
jeuno improvements
  • Loading branch information
CarnifexOptimus authored Nov 26, 2024
2 parents 5aa012e + 57bfca7 commit a751f2d
Show file tree
Hide file tree
Showing 207 changed files with 264 additions and 277 deletions.
2 changes: 1 addition & 1 deletion BossMod/BossModule/AIHints.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public void Clear()
RecommendedPositional = default;
ForbiddenDirections.Clear();
ImminentSpecialMode = default;
MisdirectionThreshold = 20.Degrees();
MisdirectionThreshold = 15.Degrees();
PredictedDamage.Clear();
MaxCastTimeEstimate = float.MaxValue;
ActionsToExecute.Clear();
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Components/TemporaryMisdirection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace BossMod.Components;
// generic temporary misdirection component
public class TemporaryMisdirection(BossModule module, ActionID aid, string hint = "Applies temporary misdirection") : CastHint(module, aid, hint)
{
private static readonly List<uint> tempMisdirectionSIDs = [1422, 2936, 3694, 3909];
private static readonly uint[] tempMisdirectionSIDs = [1422, 2936, 3694, 3909];
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
for (var i = 0; i < 4; ++i)
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Config/AboutTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Draw()
using var wrap = ImRaii.TextWrapPos(0);

ImGui.TextUnformatted("BossModReborn (BMR) provides boss fight radar, auto-rotation, cooldown planning, and AI. All of its modules can be toggled individually. Support for it can be found in the Discord server linked at the bottom of this tab.");
ImGui.TextUnformatted("This is a FORK of veyn's BossMod (VBM). Please do not ask him for any support for problems you encounter while using this fork. Instead ask for support on the Combat Reborn Discord.");
ImGui.TextUnformatted("This is a FORK of the original BossMod (VBM). Only ask for support on the Combat Reborn Discord.");
ImGui.TextUnformatted("Please also make sure to not load VBM and this fork at the same time. The consequences of doing that are unexplored and unsupported.");
ImGui.Spacing();
DrawSection("Radar",
Expand Down
4 changes: 2 additions & 2 deletions BossMod/Config/ModuleViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public ModuleViewer(PlanDatabase? planDB, WorldState ws)
_ws = ws;

uint defaultIcon = 61762;
_expansions = Enum.GetNames<BossModuleInfo.Expansion>().Take((int)BossModuleInfo.Expansion.Count).Select(n => (n, defaultIcon)).ToArray();
_categories = Enum.GetNames<BossModuleInfo.Category>().Take((int)BossModuleInfo.Category.Count).Select(n => (n, defaultIcon)).ToArray();
_expansions = [.. Enum.GetNames<BossModuleInfo.Expansion>().Take((int)BossModuleInfo.Expansion.Count).Select(n => (n, defaultIcon))];
_categories = [.. Enum.GetNames<BossModuleInfo.Category>().Take((int)BossModuleInfo.Category.Count).Select(n => (n, defaultIcon))];

var exVersion = Service.LuminaSheet<ExVersion>()!;
Customize(BossModuleInfo.Expansion.RealmReborn, 61875, exVersion.GetRow(0).Name);
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Data/NetworkState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ public override void Write(ReplayRecorder.Output output) => output.EmitFourCC("I
.Emit(Packet.Epoch)
.Emit(Packet.SourceServerActor, "X8")
.Emit(Packet.SendTimestamp.Ticks)
.Emit(Packet.Payload.ToArray());
.Emit([.. Packet.Payload]);
}
}
4 changes: 2 additions & 2 deletions BossMod/Framework/WorldStateGameSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -617,11 +617,11 @@ private unsafe void UpdateClient()
_ws.Execute(new ClientState.OpBozjaHolsterChange(CalcBozjaHolster(bozjaHolster)));

if (!MemoryExtensions.SequenceEqual(_ws.Client.BlueMageSpells.AsSpan(), actionManager->BlueMageActions))
_ws.Execute(new ClientState.OpBlueMageSpellsChange(actionManager->BlueMageActions.ToArray()));
_ws.Execute(new ClientState.OpBlueMageSpellsChange([.. actionManager->BlueMageActions]));

var levels = uiState->PlayerState.ClassJobLevels;
if (!MemoryExtensions.SequenceEqual(_ws.Client.ClassJobLevels.AsSpan(), levels))
_ws.Execute(new ClientState.OpClassJobLevelsChange(levels.ToArray()));
_ws.Execute(new ClientState.OpClassJobLevelsChange([.. levels]));

var curFate = FateManager.Instance()->CurrentFate;
ClientState.Fate activeFate = curFate != null ? new(curFate->FateId, curFate->Location, curFate->Radius) : default;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace BossMod.Dawntrail.Alliance.A10ElderGobbue;
namespace BossMod.Dawntrail.Alliance.A10Aquarius;

public enum OID : uint
{
Boss = 0x468D, // R2.28
Boss = 0x468F, // R2.1
ElderGobbue = 0x468D, // R2.28
RobberCrab1 = 0x468E, // R0.7
RobberCrab2 = 0x4711, // R0.7
DeathCap = 0x468C, // R1.65
BarkSpider1 = 0x468B, // R1.5
BarkSpider2 = 0x4710, // R1.5
Aquarius = 0x468F, // R2.1
Skimmer1 = 0x468A, // R1.5
Skimmer2 = 0x470F // R0.6
}
Expand All @@ -31,14 +31,14 @@ public enum AID : uint
class CursedSphere(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.CursedSphere), 3);
class WaterIII(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.WaterIII), 7);
class BubbleShower(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BubbleShower), new AOEShapeCone(6, 30.Degrees()));
class Scoop(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BubbleShower), new AOEShapeCone(15, 60.Degrees()));
class Scoop(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Scoop), new AOEShapeCone(15, 60.Degrees()));
class Beatdown(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Beatdown), new AOEShapeRect(9, 1.5f));
class SpiderWeb(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SpiderWeb), new AOEShapeCircle(6));
class HundredFists(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.HundredFists), showNameInHint: true);

public class A10ElderGobbueStates : StateMachineBuilder
public class A10AquariusStates : StateMachineBuilder
{
public A10ElderGobbueStates(BossModule module) : base(module)
public A10AquariusStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<WaterIII>()
Expand All @@ -52,8 +52,8 @@ public A10ElderGobbueStates(BossModule module) : base(module)
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13603, SortOrder = 3)]
public class A10ElderGobbue(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena)
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13605, SortOrder = 3)]
public class A10Aquarius(WorldState ws, Actor primary) : BossModule(ws, primary, arena.Center, arena)
{
private static readonly WPos[] vertices = [new(-500.62f, 686.9f), new(-488.18f, 686.93f), new(-487.84f, 687.31f), new(-486.66f, 692.24f), new(-486.41f, 692.91f),
new(-486.06f, 693.39f), new(-485.44f, 693.68f), new(-484.78f, 693.9f), new(-484.23f, 693.88f), new(-483.59f, 693.9f),
Expand Down
15 changes: 2 additions & 13 deletions BossMod/Modules/Dawntrail/Alliance/A10Trash/A10Despot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,8 @@ class ScraplineTyphoon(BossModule module) : Components.GenericAOEs(module)

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var count = _aoes.Count;
if (count > 0)
{
for (var i = 0; i < count; ++i)
{
var boss = Module.PrimaryActor.Position; // if boss moves initial origin is not correct anymore, so we update it every frame to be safe
var aoe = _aoes[i];
if (i == 0)
yield return count > 1 ? aoe with { Color = Colors.Danger, Origin = boss } : aoe with { Origin = boss };
else if (i > 0)
yield return aoe with { Origin = boss, Risky = false };
}
}
if (_aoes.Count > 0)
yield return _aoes[0] with { Origin = Module.PrimaryActor.Position };
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public A10GroundskeeperStates(BossModule module) : base(module)
TrivialPhase()
.ActivateOnEnter<IsleDrop>()
.ActivateOnEnter<MysteriousLight>()
.Raw.Update = () => Module.WorldState.Actors.Where(x => x.IsTargetable && !x.IsAlly).All(x => x.IsDeadOrDestroyed);
.Raw.Update = () => Module.WorldState.Actors.Where(x => !x.IsAlly && x.IsTargetable && x.InCombat).All(x => x.IsDeadOrDestroyed);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ class CriticalReaverRaidwide(BossModule module) : Components.CastCounter(module,
class CriticalReaverEnrage(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.CriticalReaverEnrage));
class Meteor(BossModule module) : Components.CastInterruptHint(module, ActionID.MakeSpell(AID.Meteor));
class TachiGekko(BossModule module) : Components.CastGaze(module, ActionID.MakeSpell(AID.TachiGekko));
class TachiKasha(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TachiKasha), new AOEShapeCircle(4));
class TachiKasha(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TachiKasha), new AOEShapeCircle(20));
class TachiYukikaze(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TachiYukikaze), new AOEShapeRect(50, 2.5f));
class ConcertedDissolution(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ConcertedDissolution), new AOEShapeCone(40, 15.Degrees()));
class LightsChain(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LightsChain), new AOEShapeDonut(4, 40));
class DivineDominion(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.DivineDominion), new AOEShapeCircle(6));
class CrossReaver(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CrossReaverAOE), new AOEShapeCross(50, 6));
class Holy(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Holy));
class Raiton(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Holy));
class Raiton(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.Raiton));

[ModuleInfo(BossModuleInfo.Maturity.Verified, PrimaryActorOID = (uint)OID.BossGK, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1015, NameID = 13640, SortOrder = 7)]
public class A13ArkAngels(WorldState ws, Actor primary) : BossModule(ws, primary, new(865, -820), new ArenaBoundsCircle(29.5f))
public class A13ArkAngels(WorldState ws, Actor primary) : BossModule(ws, primary, new(865, -820), new ArenaBoundsCircle(34.5f))
{
private Actor? _bossHM;
private Actor? _bossEV;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace BossMod.Dawntrail.Alliance.A13ArkAngels;

class ArenaChange(BossModule module) : Components.GenericAOEs(module)
{
private static readonly AOEShapeDonut donut = new(25, 30);
private static readonly AOEShapeDonut donut = new(25, 35);
private AOEInstance? _aoe;

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
public class A14ShadowLord(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaCenter, DefaultBounds)
{
private const int RadiusSmall = 8;
private const float HalfWidth = 1.94f; // the HalfWidth of the bridge seems to be either slightly less than 2 or they are not perfectly centered
public static readonly WPos ArenaCenter = new(150, 800);
public static readonly ArenaBoundsCircle DefaultBounds = new(30);
private static readonly Circle[] circles = [new(new(166, 800), RadiusSmall), new(new(134, 800), RadiusSmall),
new(new(150, 816), RadiusSmall), new(new(150, 784), RadiusSmall)];
private static readonly RectangleSE[] rects = [new(circles[1].Center, circles[2].Center, 2), new(circles[1].Center, circles[3].Center, 2),
new(circles[3].Center, circles[0].Center, 2), new(circles[0].Center, circles[2].Center, 2)];
private static readonly RectangleSE[] rects = [new(circles[1].Center, circles[2].Center, HalfWidth), new(circles[1].Center, circles[3].Center, HalfWidth),
new(circles[3].Center, circles[0].Center, HalfWidth), new(circles[0].Center, circles[2].Center, HalfWidth)];
public static readonly Shape[] Combined = [.. circles, .. rects];
public static readonly ArenaBoundsComplex ComplexBounds = new(Combined);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class CthonicFury(BossModule module) : Components.GenericAOEs(module)
{
private AOEInstance? _aoe;
public bool Active => _aoe != null || Arena.Bounds != A14ShadowLord.DefaultBounds;
private static readonly Square[] def = [new Square(A14ShadowLord.ArenaCenter, 45)]; // using a square for the difference instead of a circle since less vertices will result in slightly better performance
private static readonly Square[] def = [new Square(A14ShadowLord.ArenaCenter, 30)]; // using a square for the difference instead of a circle since less vertices will result in slightly better performance
public static readonly AOEShapeCustom AOEBurningBattlements = new(def, [new Square(A14ShadowLord.ArenaCenter, 11.5f, 45.Degrees())]);
private static readonly AOEShapeCustom aoeCthonicFury = new(def, A14ShadowLord.Combined);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class ArenaChanges(BossModule module) : Components.GenericAOEs(module)
new(-12, -104)
];

private static readonly Circle[] circles = circlePositions.Select(pos => new Circle(pos, Radius)).ToArray();
private static readonly Circle[] circles = [.. circlePositions.Select(pos => new Circle(pos, Radius))];

private static readonly (int, int)[] rectanglePairs =
[
Expand All @@ -80,8 +80,7 @@ private static readonly (int, int)[] rectanglePairs =
(24, 8), (0, 22), (0, 4), (2, 10), (22, 13),
];

private static readonly RectangleSE[] rectangles = rectanglePairs
.Select(pair => new RectangleSE(circles[pair.Item1].Center, circles[pair.Item2].Center, Radius)).ToArray();
private static readonly RectangleSE[] rectangles = [.. rectanglePairs.Select(pair => new RectangleSE(circles[pair.Item1].Center, circles[pair.Item2].Center, Radius))];

private static readonly AOEShapeCustom rectArenaChange = new(startingRect, defaultRect);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ private static readonly (Square correctTile, Square goalTile)[] tilePairs = [
(new(new(-136, -556), Radius), new(new(-140, -544), Radius))];

private static readonly List<Shape> wholeArena = [new Square(center, 12)];
private static readonly AOEShapeCustom[] forbiddenShapes = tilePairs.Select(tp => new AOEShapeCustom(wholeArena, [middle, tp.correctTile, tp.goalTile])).ToArray();
private static readonly AOEShapeCustom[] safeShapes = tilePairs.Select(tp => new AOEShapeCustom([tp.correctTile, tp.goalTile], InvertForbiddenZone: true)).ToArray();
private static readonly AOEShapeCustom[] forbiddenShapes = [.. tilePairs.Select(tp => new AOEShapeCustom(wholeArena, [middle, tp.correctTile, tp.goalTile]))];
private static readonly AOEShapeCustom[] safeShapes = [.. tilePairs.Select(tp => new AOEShapeCustom([tp.correctTile, tp.goalTile], InvertForbiddenZone: true))];

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class GhostlyGuise(BossModule module) : Components.GenericAOEs(module)
private readonly IllIntentMaliciousMist _seek = module.FindComponent<IllIntentMaliciousMist>()!;

private static readonly WPos[] positions = [new(137.5f, -443.5f), new(158.5f, -443.5f), new(137.5f, -422.5f), new(158.5f, -422.5f)];
private static readonly Circle[] circles = positions.Select(pos => new Circle(pos, 3)).ToArray();
private static readonly Circle[] circles = [.. positions.Select(pos => new Circle(pos, 3))];
private static readonly AOEShapeCustom circlesInverted = new(circles, InvertForbiddenZone: true);
private static readonly AOEShapeCustom circlesAvoid = new(circles, []);
private bool activated;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public enum AID : uint
}

class GlassPunch(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GlassPunch), new AOEShapeCone(7, 60.Degrees()));
class Catapult(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Catapult), 8);
class Catapult(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.Catapult), 6);

class D90StationSpecterStates : StateMachineBuilder
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class TulidisasterEnrage2(BossModule module) : Components.CastCounter(module, Ac
class TulidisasterEnrage3(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.TulidisasterEnrageAOE3));

// TODO: investigate how exactly are omens drawn for northern cross & susurrant breath
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 833, NameID = 12854, PlanLevel = 100)]
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 833, NameID = 12854, PlanLevel = 100)]
public class Ex1Valigarmanda(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsRect(20, 15))
{
protected override void DrawEnemies(int pcSlot, Actor pc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class BurningChains(BossModule module) : Components.Chains(module, (uint)TetherI
class HalfCircuitDonut(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HalfCircuitAOEDonut), new AOEShapeDonut(10, 30));
class HalfCircuitCircle(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HalfCircuitAOECircle), new AOEShapeCircle(10));

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 996, NameID = 12882, PlanLevel = 100)]
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 996, NameID = 12882, PlanLevel = 100)]
public class Ex2ZoraalJa(WorldState ws, Actor primary) : Trial.T02ZoraalJa.ZoraalJa(ws, primary)
{
public static readonly ArenaBoundsCustom NWPlatformBounds = BuildTwoPlatformsBounds(135.Degrees());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ProsecutionOfWar(BossModule module) : Components.TankSwap(module, ActionID
class DyingMemory(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.DyingMemory));
class DyingMemoryLast(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.DyingMemoryLast));

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn, Malediktus", PrimaryActorOID = (uint)OID.BossP1, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1017, NameID = 13029, PlanLevel = 100)]
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "Malediktus", PrimaryActorOID = (uint)OID.BossP1, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1017, NameID = 13029, PlanLevel = 100)]
public class Ex3QueenEternal(WorldState ws, Actor primary) : BossModule(ws, primary, ArenaCenter, NormalBounds)
{
public static readonly WPos ArenaCenter = Trial.T03QueenEternal.T03QueenEternal.ArenaCenter, HalfBoundsCenter = new(100, 110);
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Modules/Dawntrail/FATE/MicaTheMagicalMu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,5 +259,5 @@ public MicaTheMagicalMuStates(BossModule module) : base(module)
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn", GroupType = BossModuleInfo.GroupType.Fate, GroupID = 1922, NameID = 13049)]
[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.Fate, GroupID = 1922, NameID = 13049)]
public class MicaTheMagicalMu(WorldState ws, Actor primary) : BossModule(ws, primary, new(791, 593), new ArenaBoundsRect(21, 20));
Loading

0 comments on commit a751f2d

Please sign in to comment.