ModernUO/Scripts/Engines/ConPVP/Tournament.cs

3565 lines
No EOL
108 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Server.ContextMenus;
using Server.Ethics;
using Server.Factions;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Network;
using Server.Regions;
using Server.Targeting;
namespace Server.Engines.ConPVP
{
public enum TournamentStage
{
Inactive,
Signup,
Fighting
}
public enum GroupingType
{
HighVsLow,
Nearest,
Random
}
public enum TieType
{
Random,
Highest,
Lowest,
FullElimination,
FullAdvancement
}
public class TournamentRegistrar : Banker
{
[Constructible]
public TournamentRegistrar()
{
Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback);
}
public TournamentRegistrar(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public TournamentController Tournament{ get; set; }
private void Announce_Callback()
{
Tournament tourny = null;
if (Tournament != null)
tourny = Tournament.Tournament;
if (tourny != null && tourny.Stage == TournamentStage.Signup)
PublicOverheadMessage(MessageType.Regular, 0x35, false,
"Come one, come all! Do you aspire to be a fighter of great renown? Join this tournament and show the world your abilities.");
}
public override void OnMovement(Mobile m, Point3D oldLocation)
{
base.OnMovement(m, oldLocation);
Tournament tourny = null;
if (Tournament != null)
tourny = Tournament.Tournament;
if (InRange(m, 4) && !InRange(oldLocation, 4) && tourny != null && tourny.Stage == TournamentStage.Signup &&
m.CanBeginAction(this))
{
Ladder ladder = Ladder.Instance;
LadderEntry entry = ladder?.Find(m);
if (entry != null && Ladder.GetLevel(entry.Experience) < tourny.LevelRequirement)
return;
if (tourny.IsFactionRestricted && Faction.Find(m) == null) return;
if (tourny.HasParticipant(m))
return;
PrivateOverheadMessage(MessageType.Regular, 0x35, false,
$"Hello m'{(m.Female ? "Lady" : "Lord")}. Dost thou wish to enter this tournament? You need only to write your name in this book.",
m.NetState);
m.BeginAction(this);
Timer.DelayCall(TimeSpan.FromSeconds(10.0), new TimerStateCallback(ReleaseLock_Callback), m);
}
}
private void ReleaseLock_Callback(object obj)
{
((Mobile)obj).EndAction(this);
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(Tournament);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Tournament = reader.ReadItem() as TournamentController;
break;
}
}
Timer.DelayCall(TimeSpan.FromSeconds(30.0), TimeSpan.FromSeconds(30.0), Announce_Callback);
}
}
public class TournamentSignupItem : Item
{
[Constructible]
public TournamentSignupItem() : base(4029)
{
Movable = false;
}
public TournamentSignupItem(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public TournamentController Tournament{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public Mobile Registrar{ get; set; }
public override string DefaultName => "tournament signup book";
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that
}
else
{
Tournament tourny = Tournament?.Tournament;
if (tourny != null)
{
if (Registrar != null)
Registrar.Direction = Registrar.GetDirectionTo(this);
switch (tourny.Stage)
{
case TournamentStage.Fighting:
{
if (Registrar != null)
{
if (tourny.HasParticipant(from))
Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "Excuse me? You are already signed up.", from.NetState);
else
Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "The tournament has already begun. You are too late to signup now.",
from.NetState);
}
break;
}
case TournamentStage.Inactive:
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "The tournament is closed.", from.NetState);
break;
}
case TournamentStage.Signup:
{
Ladder ladder = Ladder.Instance;
LadderEntry entry = ladder?.Find(from);
if (entry != null && Ladder.GetLevel(entry.Experience) < tourny.LevelRequirement)
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "You have not yet proven yourself a worthy dueler.", from.NetState);
break;
}
if (tourny.IsFactionRestricted && Faction.Find(from) == null)
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "Only those who have declared their faction allegiance may participate.",
from.NetState);
break;
}
if (from.HasGump(typeof(AcceptTeamGump)))
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "You must first respond to the offer I've given you.", from.NetState);
}
else if (from.HasGump(typeof(AcceptDuelGump)))
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "You must first cancel your duel offer.", from.NetState);
}
else if (from is PlayerMobile mobile && mobile.DuelContext != null)
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "You are already participating in a duel.", mobile.NetState);
}
else if (!tourny.HasParticipant(from))
{
ArrayList players = new ArrayList();
players.Add(from);
from.CloseGump(typeof(ConfirmSignupGump));
from.SendGump(new ConfirmSignupGump(from, Registrar, tourny, players));
}
else
{
Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "You have already entered this tournament.", from.NetState);
}
break;
}
}
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(Tournament);
writer.Write(Registrar);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Tournament = reader.ReadItem() as TournamentController;
Registrar = reader.ReadMobile();
break;
}
}
}
}
public class ConfirmSignupGump : Gump
{
private const int BlackColor32 = 0x000008;
private const int LabelColor32 = 0xFFFFFF;
private Mobile m_From;
private ArrayList m_Players;
private Mobile m_Registrar;
private Tournament m_Tournament;
public ConfirmSignupGump(Mobile from, Mobile registrar, Tournament tourny, ArrayList players) : base(50, 50)
{
m_From = from;
m_Registrar = registrar;
m_Tournament = tourny;
m_Players = players;
m_From.CloseGump(typeof(AcceptTeamGump));
m_From.CloseGump(typeof(AcceptDuelGump));
m_From.CloseGump(typeof(DuelContextGump));
m_From.CloseGump(typeof(ConfirmSignupGump));
#region Rules
Ruleset ruleset = tourny.Ruleset;
Ruleset basedef = ruleset.Base;
int height = 185 + 60 + 12;
int changes = 0;
BitArray defs;
if (ruleset.Flavors.Count > 0)
{
defs = new BitArray(basedef.Options);
for (int i = 0; i < ruleset.Flavors.Count; ++i)
defs.Or(((Ruleset)ruleset.Flavors[i]).Options);
height += ruleset.Flavors.Count * 18;
}
else
{
defs = basedef.Options;
}
BitArray opts = ruleset.Options;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
++changes;
height += changes * 22;
height += 10 + 22 + 25 + 25;
if (tourny.PlayersPerParticipant > 1)
height += 36 + tourny.PlayersPerParticipant * 20;
#endregion
Closable = false;
AddPage(0);
//AddBackground( 0, 0, 400, 220, 9150 );
AddBackground(1, 1, 398, height, 3600);
//AddBackground( 16, 15, 369, 189, 9100 );
AddImageTiled(16, 15, 369, height - 29, 3604);
AddAlphaRegion(16, 15, 369, height - 29);
AddImage(215, -43, 0xEE40);
//AddImage( 330, 141, 0x8BA );
StringBuilder sb = new StringBuilder();
if (tourny.TournyType == TournyType.FreeForAll)
{
sb.Append("FFA");
}
else if (tourny.TournyType == TournyType.RandomTeam)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team");
}
else if (tourny.TournyType == TournyType.Faction)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team Faction");
}
else if (tourny.TournyType == TournyType.RedVsBlue)
{
sb.Append("Red v Blue");
}
else
{
for (int i = 0; i < tourny.ParticipantsPerMatch; ++i)
{
if (sb.Length > 0)
sb.Append('v');
sb.Append(tourny.PlayersPerParticipant);
}
}
if (tourny.EventController != null)
sb.Append(' ').Append(tourny.EventController.Title);
sb.Append(" Tournament Signup");
AddBorderedText(22, 22, 294, 20, Center(sb.ToString()), LabelColor32, BlackColor32);
AddBorderedText(22, 50, 294, 40, "You have requested to join the tournament. Do you accept the rules?", 0xB0C868,
BlackColor32);
AddImageTiled(32, 88, 264, 1, 9107);
AddImageTiled(42, 90, 264, 1, 9157);
#region Rules
int y = 100;
string groupText = null;
switch (tourny.GroupType)
{
case GroupingType.HighVsLow:
groupText = "High vs Low";
break;
case GroupingType.Nearest:
groupText = "Closest opponent";
break;
case GroupingType.Random:
groupText = "Random";
break;
}
AddBorderedText(35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32);
y += 20;
string tieText = null;
switch (tourny.TieType)
{
case TieType.Random:
tieText = "Random";
break;
case TieType.Highest:
tieText = "Highest advances";
break;
case TieType.Lowest:
tieText = "Lowest advances";
break;
case TieType.FullAdvancement:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances";
break;
case TieType.FullElimination:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated";
break;
}
AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32);
y += 20;
string sdText = "Off";
if (tourny.SuddenDeath > TimeSpan.Zero)
{
sdText = $"{(int)tourny.SuddenDeath.TotalMinutes}:{tourny.SuddenDeath.Seconds:D2}";
if (tourny.SuddenDeathRounds > 0)
sdText = $"{sdText} (first {tourny.SuddenDeathRounds} rounds)";
else
sdText = $"{sdText} (all rounds)";
}
AddBorderedText(35, y, 240, 20, $"Sudden Death: {sdText}", LabelColor32, BlackColor32);
y += 20;
y += 6;
AddImageTiled(32, y - 1, 264, 1, 9107);
AddImageTiled(42, y + 1, 264, 1, 9157);
y += 6;
AddBorderedText(35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32);
y += 20;
for (int i = 0; i < ruleset.Flavors.Count; ++i, y += 18)
AddBorderedText(35, y, 190, 20, $" + {((Ruleset)ruleset.Flavors[i]).Title}", LabelColor32, BlackColor32);
y += 4;
if (changes > 0)
{
AddBorderedText(35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32);
y += 20;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
{
string name = ruleset.Layout.FindByIndex(i);
if (name != null) // sanity
{
AddImage(35, y, opts[i] ? 0xD3 : 0xD2);
AddBorderedText(60, y, 165, 22, name, LabelColor32, BlackColor32);
}
y += 22;
}
}
else
{
AddBorderedText(35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32);
y += 20;
}
#endregion
#region Team
if (tourny.PlayersPerParticipant > 1)
{
y += 8;
AddImageTiled(32, y - 1, 264, 1, 9107);
AddImageTiled(42, y + 1, 264, 1, 9157);
y += 8;
AddBorderedText(35, y, 190, 20, "Your Team", LabelColor32, BlackColor32);
y += 20;
for (int i = 0; i < players.Count; ++i, y += 20)
{
if (i == 0)
AddImage(35, y, 0xD2);
else
AddGoldenButton(35, y, 1 + i);
AddBorderedText(60, y, 200, 20, ((Mobile)players[i]).Name, LabelColor32, BlackColor32);
}
for (int i = players.Count; i < tourny.PlayersPerParticipant; ++i, y += 20)
{
if (i == 0)
AddImage(35, y, 0xD2);
else
AddGoldenButton(35, y, 1 + i);
AddBorderedText(60, y, 200, 20, "(Empty)", LabelColor32, BlackColor32);
}
}
#endregion
y += 8;
AddImageTiled(32, y - 1, 264, 1, 9107);
AddImageTiled(42, y + 1, 264, 1, 9157);
y += 8;
AddRadio(24, y, 9727, 9730, true, 1);
AddBorderedText(60, y + 5, 250, 20, "Yes, I wish to join the tournament.", LabelColor32, BlackColor32);
y += 35;
AddRadio(24, y, 9727, 9730, false, 2);
AddBorderedText(60, y + 5, 250, 20, "No, I do not wish to join.", LabelColor32, BlackColor32);
y += 35;
y -= 3;
AddButton(314, y, 247, 248, 1, GumpButtonType.Reply, 0);
}
public string Center(string text)
{
return $"<CENTER>{text}</CENTER>";
}
public string Color(string text, int color)
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor)
{
AddColoredText(x - 1, y - 1, width, height, text, borderColor);
AddColoredText(x - 1, y + 1, width, height, text, borderColor);
AddColoredText(x + 1, y - 1, width, height, text, borderColor);
AddColoredText(x + 1, y + 1, width, height, text, borderColor);
AddColoredText(x, y, width, height, text, color);
}
private void AddColoredText(int x, int y, int width, int height, string text, int color)
{
if (color == 0)
AddHtml(x, y, width, height, text, false, false);
else
AddHtml(x, y, width, height, Color(text, color), false, false);
}
public void AddGoldenButton(int x, int y, int bid)
{
AddButton(x, y, 0xD2, 0xD2, bid, GumpButtonType.Reply, 0);
AddButton(x + 3, y + 3, 0xD8, 0xD8, bid, GumpButtonType.Reply, 0);
}
public override void OnResponse(NetState sender, RelayInfo info)
{
if (info.ButtonID == 1 && info.IsSwitched(1))
{
Tournament tourny = m_Tournament;
Mobile from = m_From;
switch (tourny.Stage)
{
case TournamentStage.Fighting:
{
if (m_Registrar != null)
{
if (m_Tournament.HasParticipant(from))
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "Excuse me? You are already signed up.", from.NetState);
else
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "The tournament has already begun. You are too late to signup now.",
from.NetState);
}
break;
}
case TournamentStage.Inactive:
{
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "The tournament is closed.", from.NetState);
break;
}
case TournamentStage.Signup:
{
if (m_Players.Count != tourny.PlayersPerParticipant)
{
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "You have not yet chosen your team.", from.NetState);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
break;
}
Ladder ladder = Ladder.Instance;
for (int i = 0; i < m_Players.Count; ++i)
{
Mobile mob = (Mobile)m_Players[i];
LadderEntry entry = ladder?.Find(mob);
if (entry != null && Ladder.GetLevel(entry.Experience) < tourny.LevelRequirement)
{
if (m_Registrar != null)
{
if (mob == from)
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "You have not yet proven yourself a worthy dueler.", from.NetState);
else
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, $"{mob.Name} has not yet proven themselves a worthy dueler.",
from.NetState);
}
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
return;
}
if (tourny.IsFactionRestricted && Faction.Find(mob) == null)
{
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "Only those who have declared their faction allegiance may participate.",
from.NetState);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
return;
}
if (tourny.HasParticipant(mob))
{
if (m_Registrar != null)
{
if (mob == from)
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, "You have already entered this tournament.", from.NetState);
else
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, $"{mob.Name} has already entered this tournament.", from.NetState);
}
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
return;
}
if (mob is PlayerMobile mobile && mobile.DuelContext != null)
{
if (mob == from)
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false,
"You are already assigned to a duel. You must yield it before joining this tournament.",
from.NetState);
else
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x35, false,
$"{mobile.Name} is already assigned to a duel. They must yield it before joining this tournament.",
from.NetState);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
return;
}
}
if (m_Registrar != null)
{
string fmt;
if (tourny.PlayersPerParticipant == 1)
fmt =
"As you say m'{0}. I've written your name to the bracket. The tournament will begin {1}.";
else if (tourny.PlayersPerParticipant == 2)
fmt =
"As you wish m'{0}. The tournament will begin {1}, but first you must name your partner.";
else
fmt = "As you wish m'{0}. The tournament will begin {1}, but first you must name your team.";
string timeUntil;
int minutesUntil = (int)Math.Round((tourny.SignupStart + tourny.SignupPeriod - DateTime.UtcNow)
.TotalMinutes);
if (minutesUntil == 0)
timeUntil = "momentarily";
else
timeUntil = $"in {minutesUntil} minute{(minutesUntil == 1 ? "" : "s")}";
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x35, false, string.Format(fmt, from.Female ? "Lady" : "Lord", timeUntil), from.NetState);
}
TournyParticipant part = new TournyParticipant(from);
part.Players.Clear();
part.Players.AddRange(m_Players);
tourny.Participants.Add(part);
break;
}
}
}
else if (info.ButtonID > 1)
{
int index = info.ButtonID - 1;
if (index > 0 && index < m_Players.Count)
{
m_Players.RemoveAt(index);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
}
else if (m_Players.Count < m_Tournament.PlayersPerParticipant)
{
m_From.BeginTarget(12, false, TargetFlags.None, AddPlayer_OnTarget);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
}
}
}
private void AddPlayer_OnTarget(Mobile from, object obj)
{
if (!(obj is Mobile mob) || mob == from)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "Excuse me?", from.NetState);
}
else if (!mob.Player)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
if (mob.Body.IsHuman)
mob.SayTo(from, 1005443); // Nay, I would rather stay here and watch a nail rust.
else
mob.SayTo(from, 1005444); // The creature ignores your offer.
}
else if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They ignore your invitation.", from.NetState);
}
else
{
if (!(mob is PlayerMobile pm))
return;
if (pm.DuelContext != null)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They are already assigned to another duel.", from.NetState);
}
else if (mob.HasGump(typeof(AcceptTeamGump)))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They have already been offered a partnership.", from.NetState);
}
else if (mob.HasGump(typeof(ConfirmSignupGump)))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They are already trying to join this tournament.", from.NetState);
}
else if (m_Players.Contains(mob))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "You have already named them as a team member.", from.NetState);
}
else if (m_Tournament.HasParticipant(mob))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They have already entered this tournament.", from.NetState);
}
else if (m_Players.Count >= m_Tournament.PlayersPerParticipant)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "Your team is full.", from.NetState);
}
else
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
mob.SendGump(new AcceptTeamGump(from, mob, m_Tournament, m_Registrar, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x59, false,
$"As you command m'{(from.Female ? "Lady" : "Lord")}. I've given your offer to {mob.Name}.",
from.NetState);
}
}
}
}
public class AcceptTeamGump : Gump
{
private const int BlackColor32 = 0x000008;
private const int LabelColor32 = 0xFFFFFF;
private bool m_Active;
private Mobile m_From;
private ArrayList m_Players;
private Mobile m_Registrar;
private Mobile m_Requested;
private Tournament m_Tournament;
public AcceptTeamGump(Mobile from, Mobile requested, Tournament tourny, Mobile registrar, ArrayList players) :
base(50, 50)
{
m_From = from;
m_Requested = requested;
m_Tournament = tourny;
m_Registrar = registrar;
m_Players = players;
m_Active = true;
#region Rules
Ruleset ruleset = tourny.Ruleset;
Ruleset basedef = ruleset.Base;
int height = 185 + 35 + 60 + 12;
int changes = 0;
BitArray defs;
if (ruleset.Flavors.Count > 0)
{
defs = new BitArray(basedef.Options);
for (int i = 0; i < ruleset.Flavors.Count; ++i)
defs.Or(((Ruleset)ruleset.Flavors[i]).Options);
height += ruleset.Flavors.Count * 18;
}
else
{
defs = basedef.Options;
}
BitArray opts = ruleset.Options;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
++changes;
height += changes * 22;
height += 10 + 22 + 25 + 25;
#endregion
Closable = false;
AddPage(0);
AddBackground(1, 1, 398, height, 3600);
AddImageTiled(16, 15, 369, height - 29, 3604);
AddAlphaRegion(16, 15, 369, height - 29);
AddImage(215, -43, 0xEE40);
StringBuilder sb = new StringBuilder();
if (tourny.TournyType == TournyType.FreeForAll)
{
sb.Append("FFA");
}
else if (tourny.TournyType == TournyType.RandomTeam)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team");
}
else if (tourny.TournyType == TournyType.Faction)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team Faction");
}
else if (tourny.TournyType == TournyType.RedVsBlue)
{
sb.Append("Red v Blue");
}
else
{
for (int i = 0; i < tourny.ParticipantsPerMatch; ++i)
{
if (sb.Length > 0)
sb.Append('v');
sb.Append(tourny.PlayersPerParticipant);
}
}
if (tourny.EventController != null)
sb.Append(' ').Append(tourny.EventController.Title);
sb.Append(" Tournament Invitation");
AddBorderedText(22, 22, 294, 20, Center(sb.ToString()), LabelColor32, BlackColor32);
AddBorderedText(22, 50, 294, 40,
$"You have been asked to partner with {from.Name} in a tournament. Do you accept?",
0xB0C868, BlackColor32);
AddImageTiled(32, 88, 264, 1, 9107);
AddImageTiled(42, 90, 264, 1, 9157);
#region Rules
int y = 100;
string groupText = null;
switch (tourny.GroupType)
{
case GroupingType.HighVsLow:
groupText = "High vs Low";
break;
case GroupingType.Nearest:
groupText = "Closest opponent";
break;
case GroupingType.Random:
groupText = "Random";
break;
}
AddBorderedText(35, y, 190, 20, $"Grouping: {groupText}", LabelColor32, BlackColor32);
y += 20;
string tieText = null;
switch (tourny.TieType)
{
case TieType.Random:
tieText = "Random";
break;
case TieType.Highest:
tieText = "Highest advances";
break;
case TieType.Lowest:
tieText = "Lowest advances";
break;
case TieType.FullAdvancement:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances";
break;
case TieType.FullElimination:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated";
break;
}
AddBorderedText(35, y, 190, 20, $"Tiebreaker: {tieText}", LabelColor32, BlackColor32);
y += 20;
string sdText = "Off";
if (tourny.SuddenDeath > TimeSpan.Zero)
{
sdText = $"{(int)tourny.SuddenDeath.TotalMinutes}:{tourny.SuddenDeath.Seconds:D2}";
if (tourny.SuddenDeathRounds > 0)
sdText = $"{sdText} (first {tourny.SuddenDeathRounds} rounds)";
else
sdText = $"{sdText} (all rounds)";
}
AddBorderedText(35, y, 240, 20, $"Sudden Death: {sdText}", LabelColor32, BlackColor32);
y += 20;
y += 6;
AddImageTiled(32, y - 1, 264, 1, 9107);
AddImageTiled(42, y + 1, 264, 1, 9157);
y += 6;
AddBorderedText(35, y, 190, 20, $"Ruleset: {basedef.Title}", LabelColor32, BlackColor32);
y += 20;
for (int i = 0; i < ruleset.Flavors.Count; ++i, y += 18)
AddBorderedText(35, y, 190, 20, $" + {((Ruleset)ruleset.Flavors[i]).Title}", LabelColor32, BlackColor32);
y += 4;
if (changes > 0)
{
AddBorderedText(35, y, 190, 20, "Modifications:", LabelColor32, BlackColor32);
y += 20;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
{
string name = ruleset.Layout.FindByIndex(i);
if (name != null) // sanity
{
AddImage(35, y, opts[i] ? 0xD3 : 0xD2);
AddBorderedText(60, y, 165, 22, name, LabelColor32, BlackColor32);
}
y += 22;
}
}
else
{
AddBorderedText(35, y, 190, 20, "Modifications: None", LabelColor32, BlackColor32);
y += 20;
}
#endregion
y += 8;
AddImageTiled(32, y - 1, 264, 1, 9107);
AddImageTiled(42, y + 1, 264, 1, 9157);
y += 8;
AddRadio(24, y, 9727, 9730, true, 1);
AddBorderedText(60, y + 5, 250, 20, "Yes, I will join them.", LabelColor32, BlackColor32);
y += 35;
AddRadio(24, y, 9727, 9730, false, 2);
AddBorderedText(60, y + 5, 250, 20, "No, I do not wish to fight.", LabelColor32, BlackColor32);
y += 35;
AddRadio(24, y, 9727, 9730, false, 3);
AddBorderedText(60, y + 5, 270, 20, "No, most certainly not. Do not ask again.", LabelColor32, BlackColor32);
y += 35;
y -= 3;
AddButton(314, y, 247, 248, 1, GumpButtonType.Reply, 0);
Timer.DelayCall(TimeSpan.FromSeconds(15.0), AutoReject);
}
public string Center(string text)
{
return $"<CENTER>{text}</CENTER>";
}
public string Color(string text, int color)
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor)
{
AddColoredText(x - 1, y - 1, width, height, text, borderColor);
AddColoredText(x - 1, y + 1, width, height, text, borderColor);
AddColoredText(x + 1, y - 1, width, height, text, borderColor);
AddColoredText(x + 1, y + 1, width, height, text, borderColor);
AddColoredText(x, y, width, height, text, color);
}
private void AddColoredText(int x, int y, int width, int height, string text, int color)
{
if (color == 0)
AddHtml(x, y, width, height, text, false, false);
else
AddHtml(x, y, width, height, Color(text, color), false, false);
}
public void AutoReject()
{
if (!m_Active)
return;
m_Active = false;
m_Requested.CloseGump(typeof(AcceptTeamGump));
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
if (m_Registrar != null)
{
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, $"{m_Requested.Name} seems unresponsive.", m_From.NetState);
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, $"You have declined the partnership with {m_From.Name}.", m_Requested.NetState);
}
}
public override void OnResponse(NetState sender, RelayInfo info)
{
Mobile from = m_From;
Mobile mob = m_Requested;
if (info.ButtonID != 1 || !m_Active)
return;
m_Active = false;
if (info.IsSwitched(1))
{
if (!(mob is PlayerMobile pm))
return;
if (AcceptDuelGump.IsIgnored(mob, from) || mob.Blessed)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They ignore your invitation.", from.NetState);
}
else if (pm.DuelContext != null)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They are already assigned to another duel.", from.NetState);
}
else if (m_Players.Contains(mob))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "You have already named them as a team member.", from.NetState);
}
else if (m_Tournament.HasParticipant(mob))
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "They have already entered this tournament.", from.NetState);
}
else if (m_Players.Count >= m_Tournament.PlayersPerParticipant)
{
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
m_Registrar?.PrivateOverheadMessage(MessageType.Regular,
0x22, false, "Your team is full.", from.NetState);
}
else
{
m_Players.Add(mob);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
if (m_Registrar != null)
{
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x59, false, $"{mob.Name} has accepted your offer of partnership.", from.NetState);
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x59, false, $"You have accepted the partnership with {from.Name}.", mob.NetState);
}
}
}
else
{
if (info.IsSwitched(3))
AcceptDuelGump.BeginIgnore(m_Requested, m_From);
m_From.SendGump(new ConfirmSignupGump(m_From, m_Registrar, m_Tournament, m_Players));
if (m_Registrar != null)
{
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, $"{mob.Name} has declined your offer of partnership.", from.NetState);
m_Registrar.PrivateOverheadMessage(MessageType.Regular,
0x22, false, $"You have declined the partnership with {from.Name}.", mob.NetState);
}
}
}
}
public class TournamentController : Item
{
private static ArrayList m_Instances = new ArrayList();
private Tournament m_Tournament;
[Constructible]
public TournamentController() : base(0x1B7A)
{
Visible = false;
Movable = false;
m_Tournament = new Tournament();
m_Instances.Add(this);
}
public TournamentController(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public Tournament Tournament
{
get => m_Tournament;
set { }
}
public static bool IsActive
{
get
{
for (int i = 0; i < m_Instances.Count; ++i)
{
TournamentController controller = (TournamentController)m_Instances[i];
if (controller != null && !controller.Deleted && controller.Tournament != null &&
controller.Tournament.Stage != TournamentStage.Inactive)
return true;
}
return false;
}
}
public override string DefaultName => "tournament controller";
public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
{
base.GetContextMenuEntries(from, list);
if (from.AccessLevel >= AccessLevel.GameMaster && m_Tournament != null)
{
list.Add(new EditEntry(m_Tournament));
if (m_Tournament.CurrentStage == TournamentStage.Inactive)
list.Add(new StartEntry(m_Tournament));
}
}
public override void OnDoubleClick(Mobile from)
{
if (from.AccessLevel >= AccessLevel.GameMaster && m_Tournament != null)
{
from.CloseGump(typeof(PickRulesetGump));
from.CloseGump(typeof(RulesetGump));
from.SendGump(new PickRulesetGump(from, null, m_Tournament.Ruleset));
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
m_Tournament.Serialize(writer);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
m_Tournament = new Tournament(reader);
break;
}
}
m_Instances.Add(this);
}
public override void OnDelete()
{
base.OnDelete();
m_Instances.Remove(this);
}
private class EditEntry : ContextMenuEntry
{
private Tournament m_Tournament;
public EditEntry(Tournament tourny) : base(5101)
{
m_Tournament = tourny;
}
public override void OnClick()
{
Owner.From.SendGump(new PropertiesGump(Owner.From, m_Tournament));
}
}
private class StartEntry : ContextMenuEntry
{
private Tournament m_Tournament;
public StartEntry(Tournament tourny) : base(5113)
{
m_Tournament = tourny;
}
public override void OnClick()
{
if (m_Tournament.Stage == TournamentStage.Inactive)
{
m_Tournament.SignupStart = DateTime.UtcNow;
m_Tournament.Stage = TournamentStage.Signup;
m_Tournament.Participants.Clear();
m_Tournament.Pyramid.Levels.Clear();
m_Tournament.Alert("Hear ye! Hear ye!",
"Tournament signup has opened. You can enter by signing up with the registrar.");
}
}
}
}
public enum TournyType
{
Standard,
FreeForAll,
RandomTeam,
RedVsBlue,
Faction
}
[PropertyObject]
public class Tournament
{
private static readonly TimeSpan SliceInterval = TimeSpan.FromSeconds(12.0);
private int m_ParticipantsPerMatch;
private int m_PlayersPerParticipant;
public Tournament(GenericReader reader)
{
int version = reader.ReadEncodedInt();
switch (version)
{
case 5:
{
FactionRestricted = reader.ReadBool();
goto case 4;
}
case 4:
{
EventController = reader.ReadItem() as EventController;
goto case 3;
}
case 3:
{
SuddenDeathRounds = reader.ReadEncodedInt();
goto case 2;
}
case 2:
{
TournyType = (TournyType)reader.ReadEncodedInt();
goto case 1;
}
case 1:
{
GroupType = (GroupingType)reader.ReadEncodedInt();
TieType = (TieType)reader.ReadEncodedInt();
SignupPeriod = reader.ReadTimeSpan();
goto case 0;
}
case 0:
{
if (version < 3)
SuddenDeathRounds = 3;
m_ParticipantsPerMatch = reader.ReadEncodedInt();
m_PlayersPerParticipant = reader.ReadEncodedInt();
SignupPeriod = reader.ReadTimeSpan();
CurrentStage = TournamentStage.Inactive;
Pyramid = new TournyPyramid();
Ruleset = new Ruleset(RulesetLayout.Root);
Ruleset.ApplyDefault(Ruleset.Layout.Defaults[0]);
Participants = new ArrayList();
Undefeated = new ArrayList();
Arenas = new ArrayList();
break;
}
}
Timer.DelayCall(SliceInterval, SliceInterval, Slice);
}
public Tournament()
{
m_ParticipantsPerMatch = 2;
m_PlayersPerParticipant = 1;
Pyramid = new TournyPyramid();
Ruleset = new Ruleset(RulesetLayout.Root);
Ruleset.ApplyDefault(Ruleset.Layout.Defaults[0]);
Participants = new ArrayList();
Undefeated = new ArrayList();
Arenas = new ArrayList();
SignupPeriod = TimeSpan.FromMinutes(10.0);
Timer.DelayCall(SliceInterval, SliceInterval, Slice);
}
public bool IsNotoRestricted => TournyType != TournyType.Standard;
[CommandProperty(AccessLevel.GameMaster)]
public EventController EventController{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int SuddenDeathRounds{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TournyType TournyType{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public GroupingType GroupType{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TieType TieType{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan SuddenDeath{ get; set; }
public Ruleset Ruleset{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int ParticipantsPerMatch
{
get => m_ParticipantsPerMatch;
set
{
if (value < 2) value = 2;
else if (value > 10) value = 10;
m_ParticipantsPerMatch = value;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int PlayersPerParticipant
{
get => m_PlayersPerParticipant;
set
{
if (value < 1) value = 1;
else if (value > 10) value = 10;
m_PlayersPerParticipant = value;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public int LevelRequirement{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public bool FactionRestricted{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TimeSpan SignupPeriod{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public DateTime SignupStart{ get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public TournamentStage CurrentStage{ get; private set; }
public TournamentStage Stage
{
get => CurrentStage;
set => CurrentStage = value;
}
public TournyPyramid Pyramid{ get; set; }
public ArrayList Arenas{ get; set; }
public ArrayList Participants{ get; set; }
public ArrayList Undefeated{ get; set; }
public bool IsFactionRestricted => FactionRestricted || TournyType == TournyType.Faction;
public bool HasParticipant(Mobile mob)
{
for (int i = 0; i < Participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)Participants[i];
if (part.Players.Contains(mob))
return true;
}
return false;
}
public void Serialize(GenericWriter writer)
{
writer.WriteEncodedInt(5); // version
writer.Write(FactionRestricted);
writer.Write(EventController);
writer.WriteEncodedInt(SuddenDeathRounds);
writer.WriteEncodedInt((int)TournyType);
writer.WriteEncodedInt((int)GroupType);
writer.WriteEncodedInt((int)TieType);
writer.Write(SuddenDeath);
writer.WriteEncodedInt(m_ParticipantsPerMatch);
writer.WriteEncodedInt(m_PlayersPerParticipant);
writer.Write(SignupPeriod);
}
public void HandleTie(Arena arena, TournyMatch match, ArrayList remaining)
{
if (remaining.Count == 1)
HandleWon(arena, match, (TournyParticipant)remaining[0]);
if (remaining.Count < 2)
return;
StringBuilder sb = new StringBuilder();
sb.Append("The match has ended in a tie ");
if (remaining.Count == 2)
sb.Append("between ");
else
sb.Append("among ");
sb.Append(remaining.Count);
if (((TournyParticipant)remaining[0]).Players.Count == 1)
sb.Append(" players: ");
else
sb.Append(" teams: ");
bool hasAppended = false;
for (int j = 0; j < match.Participants.Count; ++j)
{
TournyParticipant part = (TournyParticipant)match.Participants[j];
if (remaining.Contains(part))
{
if (hasAppended)
sb.Append(", ");
sb.Append(part.NameList);
hasAppended = true;
}
else
{
Undefeated.Remove(part);
}
}
sb.Append(". ");
string whole = remaining.Count == 2 ? "both" : "all";
TieType tieType = TieType;
if (tieType == TieType.FullElimination && remaining.Count >= Undefeated.Count)
tieType = TieType.FullAdvancement;
switch (TieType)
{
case TieType.FullAdvancement:
{
sb.AppendFormat("In accordance with the rules, {0} parties are advanced.", whole);
break;
}
case TieType.FullElimination:
{
for (int j = 0; j < remaining.Count; ++j)
Undefeated.Remove(remaining[j]);
sb.AppendFormat("In accordance with the rules, {0} parties are eliminated.", whole);
break;
}
case TieType.Random:
{
TournyParticipant advanced = (TournyParticipant)remaining[Utility.Random(remaining.Count)];
for (int i = 0; i < remaining.Count; ++i)
if (remaining[i] != advanced)
Undefeated.Remove(remaining[i]);
if (advanced != null)
sb.AppendFormat("In accordance with the rules, {0} {1} advanced.", advanced.NameList,
advanced.Players.Count == 1 ? "is" : "are");
break;
}
case TieType.Highest:
{
TournyParticipant advanced = null;
for (int i = 0; i < remaining.Count; ++i)
{
TournyParticipant part = (TournyParticipant)remaining[i];
if (advanced == null || part.TotalLadderXP > advanced.TotalLadderXP)
advanced = part;
}
for (int i = 0; i < remaining.Count; ++i)
if (remaining[i] != advanced)
Undefeated.Remove(remaining[i]);
if (advanced != null)
sb.AppendFormat("In accordance with the rules, {0} {1} advanced.", advanced.NameList,
advanced.Players.Count == 1 ? "is" : "are");
break;
}
case TieType.Lowest:
{
TournyParticipant advanced = null;
for (int i = 0; i < remaining.Count; ++i)
{
TournyParticipant part = (TournyParticipant)remaining[i];
if (advanced == null || part.TotalLadderXP < advanced.TotalLadderXP)
advanced = part;
}
for (int i = 0; i < remaining.Count; ++i)
if (remaining[i] != advanced)
Undefeated.Remove(remaining[i]);
if (advanced != null)
sb.AppendFormat("In accordance with the rules, {0} {1} advanced.", advanced.NameList,
advanced.Players.Count == 1 ? "is" : "are");
break;
}
}
Alert(arena, sb.ToString());
}
public void OnEliminated(DuelPlayer player)
{
Participant part = player.Participant;
if (!part.Eliminated)
return;
if (TournyType == TournyType.FreeForAll)
{
int rem = 0;
for (int i = 0; i < part.Context.Participants.Count; ++i)
{
Participant check = (Participant)part.Context.Participants[i];
if (check != null && !check.Eliminated)
++rem;
}
TournyParticipant tp = part.TournyPart;
if (tp == null)
return;
if (rem == 1)
GiveAwards(tp.Players, TrophyRank.Silver, ComputeCashAward() / 2);
else if (rem == 2)
GiveAwards(tp.Players, TrophyRank.Bronze, ComputeCashAward() / 4);
}
}
public void HandleWon(Arena arena, TournyMatch match, TournyParticipant winner)
{
StringBuilder sb = new StringBuilder();
sb.Append("The match is complete. ");
sb.Append(winner.NameList);
if (winner.Players.Count > 1)
sb.Append(" have bested ");
else
sb.Append(" has bested ");
if (match.Participants.Count > 2)
sb.AppendFormat("{0} other {1}: ", match.Participants.Count - 1,
winner.Players.Count == 1 ? "players" : "teams");
bool hasAppended = false;
for (int j = 0; j < match.Participants.Count; ++j)
{
TournyParticipant part = (TournyParticipant)match.Participants[j];
if (part == winner)
continue;
Undefeated.Remove(part);
if (hasAppended)
sb.Append(", ");
sb.Append(part.NameList);
hasAppended = true;
}
sb.Append(".");
if (TournyType == TournyType.Standard)
Alert(arena, sb.ToString());
}
private int ComputeCashAward()
{
return Participants.Count * m_PlayersPerParticipant * 2500;
}
private void GiveAwards()
{
switch (TournyType)
{
case TournyType.FreeForAll:
{
if (Pyramid.Levels.Count < 1)
break;
PyramidLevel top = Pyramid.Levels[Pyramid.Levels.Count - 1] as PyramidLevel;
if (top.FreeAdvance != null || top.Matches.Count != 1)
break;
TournyMatch match = top.Matches[0] as TournyMatch;
TournyParticipant winner = match.Winner;
if (winner != null)
GiveAwards(winner.Players, TrophyRank.Gold, ComputeCashAward());
break;
}
case TournyType.Standard:
{
if (Pyramid.Levels.Count < 2)
break;
PyramidLevel top = Pyramid.Levels[Pyramid.Levels.Count - 1] as PyramidLevel;
if (top.FreeAdvance != null || top.Matches.Count != 1)
break;
int cash = ComputeCashAward();
TournyMatch match = top.Matches[0] as TournyMatch;
TournyParticipant winner = match.Winner;
for (int i = 0; i < match.Participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)match.Participants[i];
if (part == winner)
GiveAwards(part.Players, TrophyRank.Gold, cash);
else
GiveAwards(part.Players, TrophyRank.Silver, cash / 2);
}
PyramidLevel next = Pyramid.Levels[Pyramid.Levels.Count - 2] as PyramidLevel;
if (next.Matches.Count > 2)
break;
for (int i = 0; i < next.Matches.Count; ++i)
{
match = (TournyMatch)next.Matches[i];
winner = match.Winner;
for (int j = 0; j < match.Participants.Count; ++j)
{
TournyParticipant part = (TournyParticipant)match.Participants[j];
if (part != winner)
GiveAwards(part.Players, TrophyRank.Bronze, cash / 4);
}
}
break;
}
}
}
private void GiveAwards(ArrayList players, TrophyRank rank, int cash)
{
if (players.Count == 0)
return;
if (players.Count > 1)
cash /= players.Count - 1;
cash += 500;
cash /= 1000;
cash *= 1000;
StringBuilder sb = new StringBuilder();
if (TournyType == TournyType.FreeForAll)
{
sb.Append(Participants.Count * m_PlayersPerParticipant);
sb.Append("-man FFA");
}
else if (TournyType == TournyType.RandomTeam)
{
sb.Append(m_ParticipantsPerMatch);
sb.Append("-Team");
}
else if (TournyType == TournyType.Faction)
{
sb.Append(m_ParticipantsPerMatch);
sb.Append("-Team Faction");
}
else if (TournyType == TournyType.RedVsBlue)
{
sb.Append("Red v Blue");
}
else
{
for (int i = 0; i < m_ParticipantsPerMatch; ++i)
{
if (sb.Length > 0)
sb.Append('v');
sb.Append(m_PlayersPerParticipant);
}
}
if (EventController != null)
sb.Append(' ').Append(EventController.Title);
sb.Append(" Champion");
string title = sb.ToString();
for (int i = 0; i < players.Count; ++i)
{
Mobile mob = (Mobile)players[i];
if (mob == null || mob.Deleted)
continue;
Item item = new Trophy(title, rank);
if (!mob.PlaceInBackpack(item))
mob.BankBox.DropItem(item);
if (cash > 0)
{
item = new BankCheck(cash);
if (!mob.PlaceInBackpack(item))
mob.BankBox.DropItem(item);
mob.SendMessage(
"You have been awarded a {0} trophy and {1:N0}gp for your participation in this tournament.",
rank.ToString().ToLower(), cash);
}
else
{
mob.SendMessage("You have been awarded a {0} trophy for your participation in this tournament.",
rank.ToString().ToLower());
}
}
}
public void Slice()
{
if (CurrentStage == TournamentStage.Signup)
{
TimeSpan until = SignupStart + SignupPeriod - DateTime.UtcNow;
if (until <= TimeSpan.Zero)
{
for (int i = Participants.Count - 1; i >= 0; --i)
{
TournyParticipant part = (TournyParticipant)Participants[i];
bool bad = false;
for (int j = 0; j < part.Players.Count; ++j)
{
Mobile check = (Mobile)part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf(typeof(Jail)))
{
bad = true;
break;
}
}
if (bad)
{
for (int j = 0; j < part.Players.Count; ++j)
((Mobile)part.Players[j]).SendMessage("You have been disqualified from the tournament.");
Participants.RemoveAt(i);
}
}
if (Participants.Count >= 2)
{
CurrentStage = TournamentStage.Fighting;
Undefeated.Clear();
Pyramid.Levels.Clear();
Pyramid.AddLevel(m_ParticipantsPerMatch, Participants, GroupType, TournyType);
PyramidLevel level = (PyramidLevel)Pyramid.Levels[0];
if (level.FreeAdvance != null)
Undefeated.Add(level.FreeAdvance);
for (int i = 0; i < level.Matches.Count; ++i)
{
TournyMatch match = (TournyMatch)level.Matches[i];
Undefeated.AddRange(match.Participants);
}
Alert("Hear ye! Hear ye!", "The tournament will begin shortly.");
}
else
{
/*Alert( "Is this all?", "Pitiful. Signup extended." );
m_SignupStart = DateTime.UtcNow;*/
Alert("Is this all?", "Pitiful. Tournament cancelled.");
CurrentStage = TournamentStage.Inactive;
}
}
else if (Math.Abs(until.TotalSeconds - TimeSpan.FromMinutes(1.0).TotalSeconds) <
SliceInterval.TotalSeconds / 2)
{
Alert("Last call!", "If you wish to enter the tournament, sign up with the registrar now.");
}
else if (Math.Abs(until.TotalSeconds - TimeSpan.FromMinutes(5.0).TotalSeconds) <
SliceInterval.TotalSeconds / 2)
{
Alert("The tournament will begin in 5 minutes.", "Sign up now before it's too late.");
}
}
else if (CurrentStage == TournamentStage.Fighting)
{
if (Undefeated.Count == 1)
{
TournyParticipant winner = (TournyParticipant)Undefeated[0];
try
{
if (EventController != null)
{
Alert("The tournament has completed!",
$"Team {EventController.GetTeamName(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner))} has won!");
}
else if (TournyType == TournyType.RandomTeam)
{
Alert("The tournament has completed!",
$"Team {((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) + 1} has won!");
}
else if (TournyType == TournyType.Faction)
{
if (m_ParticipantsPerMatch == 4)
{
string name = "(null)";
switch (((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(
winner))
{
case 0:
{
name = "Minax";
break;
}
case 1:
{
name = "Council of Mages";
break;
}
case 2:
{
name = "True Britannians";
break;
}
case 3:
{
name = "Shadowlords";
break;
}
}
Alert("The tournament has completed!", $"The {name} team has won!");
}
else if (m_ParticipantsPerMatch == 2)
{
Alert("The tournament has completed!",
$"The {(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) == 0 ? "Evil" : "Hero")} team has won!");
}
else
{
Alert("The tournament has completed!",
$"Team {((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) + 1} has won!");
}
}
else if (TournyType == TournyType.RedVsBlue)
{
Alert("The tournament has completed!",
$"Team {(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) == 0 ? "Red" : "Blue")} has won!");
}
else
{
Alert("The tournament has completed!",
$"{winner.NameList} {(winner.Players.Count > 1 ? "are" : "is")} the champion{(winner.Players.Count == 1 ? "" : "s")}.");
}
}
catch
{
}
GiveAwards();
CurrentStage = TournamentStage.Inactive;
Undefeated.Clear();
}
else if (Pyramid.Levels.Count > 0)
{
PyramidLevel activeLevel = (PyramidLevel)Pyramid.Levels[Pyramid.Levels.Count - 1];
bool stillGoing = false;
for (int i = 0; i < activeLevel.Matches.Count; ++i)
{
TournyMatch match = (TournyMatch)activeLevel.Matches[i];
if (match.Winner == null)
{
stillGoing = true;
if (!match.InProgress)
for (int j = 0; j < Arenas.Count; ++j)
{
Arena arena = (Arena)Arenas[j];
if (!arena.IsOccupied)
{
match.Start(arena, this);
break;
}
}
}
}
if (!stillGoing)
{
for (int i = Undefeated.Count - 1; i >= 0; --i)
{
TournyParticipant part = (TournyParticipant)Undefeated[i];
bool bad = false;
for (int j = 0; j < part.Players.Count; ++j)
{
Mobile check = (Mobile)part.Players[j];
if (check.Deleted || check.Map == null || check.Map == Map.Internal || !check.Alive ||
Sigil.ExistsOn(check) || check.Region.IsPartOf(typeof(Jail)))
{
bad = true;
break;
}
}
if (bad)
{
for (int j = 0; j < part.Players.Count; ++j)
((Mobile)part.Players[j]).SendMessage("You have been disqualified from the tournament.");
Undefeated.RemoveAt(i);
if (Undefeated.Count == 1)
{
TournyParticipant winner = (TournyParticipant)Undefeated[0];
try
{
if (EventController != null)
{
Alert("The tournament has completed!",
$"Team {EventController.GetTeamName(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner))} has won");
}
else if (TournyType == TournyType.RandomTeam)
{
Alert("The tournament has completed!",
$"Team {((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) + 1} has won!");
}
else if (TournyType == TournyType.Faction)
{
if (m_ParticipantsPerMatch == 4)
{
string name = "(null)";
switch (((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0])
.Participants.IndexOf(winner))
{
case 0:
{
name = "Minax";
break;
}
case 1:
{
name = "Council of Mages";
break;
}
case 2:
{
name = "True Britannians";
break;
}
case 3:
{
name = "Shadowlords";
break;
}
}
Alert("The tournament has completed!", $"The {name} team has won!");
}
else if (m_ParticipantsPerMatch == 2)
{
Alert("The tournament has completed!",
$"The {(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) == 0 ? "Evil" : "Hero")} team has won!");
}
else
{
Alert("The tournament has completed!",
$"Team {((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) + 1} has won!");
}
}
else if (TournyType == TournyType.RedVsBlue)
{
Alert("The tournament has completed!",
$"Team {(((TournyMatch)((PyramidLevel)Pyramid.Levels[0]).Matches[0]).Participants.IndexOf(winner) == 0 ? "Red" : "Blue")} has won!");
}
else
{
Alert("The tournament has completed!",
$"{winner.NameList} {(winner.Players.Count > 1 ? "are" : "is")} the champion{(winner.Players.Count == 1 ? "" : "s")}.");
}
}
catch
{
}
GiveAwards();
CurrentStage = TournamentStage.Inactive;
Undefeated.Clear();
break;
}
}
}
if (Undefeated.Count > 1)
Pyramid.AddLevel(m_ParticipantsPerMatch, Undefeated, GroupType, TournyType);
}
}
}
}
public void Alert(params string[] alerts)
{
for (int i = 0; i < Arenas.Count; ++i)
Alert((Arena)Arenas[i], alerts);
}
public void Alert(Arena arena, params string[] alerts)
{
if (arena?.Announcer != null)
for (int j = 0; j < alerts.Length; ++j)
Timer.DelayCall(TimeSpan.FromSeconds(Math.Max(j - 0.5, 0.0)), new TimerStateCallback(Alert_Callback),
new object[] { arena.Announcer, alerts[j] });
}
private void Alert_Callback(object state)
{
object[] states = (object[])state;
((Mobile)states[0])?.PublicOverheadMessage(MessageType.Regular, 0x35, false, (string)states[1]);
}
}
public class TournyPyramid
{
public TournyPyramid()
{
Levels = new ArrayList();
}
public ArrayList Levels{ get; set; }
public void AddLevel(int partsPerMatch, ArrayList participants, GroupingType groupType, TournyType tournyType)
{
ArrayList copy = new ArrayList(participants);
if (groupType == GroupingType.Nearest || groupType == GroupingType.HighVsLow)
copy.Sort();
PyramidLevel level = new PyramidLevel();
switch (tournyType)
{
case TournyType.RedVsBlue:
{
TournyParticipant[] parts = new TournyParticipant[2];
for (int i = 0; i < parts.Length; ++i)
parts[i] = new TournyParticipant(new ArrayList());
for (int i = 0; i < copy.Count; ++i)
{
ArrayList players = ((TournyParticipant)copy[i]).Players;
for (int j = 0; j < players.Count; ++j)
{
Mobile mob = (Mobile)players[j];
if (mob.Kills >= 5)
parts[0].Players.Add(mob);
else
parts[1].Players.Add(mob);
}
}
level.Matches.Add(new TournyMatch(new ArrayList(parts)));
break;
}
case TournyType.Faction:
{
TournyParticipant[] parts = new TournyParticipant[partsPerMatch];
for (int i = 0; i < parts.Length; ++i)
parts[i] = new TournyParticipant(new ArrayList());
for (int i = 0; i < copy.Count; ++i)
{
ArrayList players = ((TournyParticipant)copy[i]).Players;
for (int j = 0; j < players.Count; ++j)
{
Mobile mob = (Mobile)players[j];
int index = -1;
if (partsPerMatch == 4)
{
Faction fac = Faction.Find(mob);
if (fac != null) index = fac.Definition.Sort;
}
else if (partsPerMatch == 2)
{
if (Ethic.Evil.IsEligible(mob))
index = 0;
else if (Ethic.Hero.IsEligible(mob)) index = 1;
}
if (index < 0 || index >= partsPerMatch) index = i % partsPerMatch;
parts[index].Players.Add(mob);
}
}
level.Matches.Add(new TournyMatch(new ArrayList(parts)));
break;
}
case TournyType.RandomTeam:
{
TournyParticipant[] parts = new TournyParticipant[partsPerMatch];
for (int i = 0; i < partsPerMatch; ++i)
parts[i] = new TournyParticipant(new ArrayList());
for (int i = 0; i < copy.Count; ++i)
parts[i % parts.Length].Players.AddRange(((TournyParticipant)copy[i]).Players);
level.Matches.Add(new TournyMatch(new ArrayList(parts)));
break;
}
case TournyType.FreeForAll:
{
level.Matches.Add(new TournyMatch(copy));
break;
}
case TournyType.Standard:
{
if (partsPerMatch >= 2 && participants.Count % partsPerMatch == 1)
{
int lowAdvances = int.MaxValue;
for (int i = 0; i < participants.Count; ++i)
{
TournyParticipant p = (TournyParticipant)participants[i];
if (p.FreeAdvances < lowAdvances)
lowAdvances = p.FreeAdvances;
}
ArrayList toAdvance = new ArrayList();
for (int i = 0; i < participants.Count; ++i)
{
TournyParticipant p = (TournyParticipant)participants[i];
if (p.FreeAdvances == lowAdvances)
toAdvance.Add(p);
}
if (toAdvance.Count == 0)
toAdvance = copy; // sanity
int idx = Utility.Random(toAdvance.Count);
((TournyParticipant)toAdvance[idx]).AddLog(
"Advanced automatically due to an odd number of challengers.");
level.FreeAdvance = (TournyParticipant)toAdvance[idx];
++level.FreeAdvance.FreeAdvances;
copy.Remove(toAdvance[idx]);
}
while (copy.Count >= partsPerMatch)
{
ArrayList thisMatch = new ArrayList();
for (int i = 0; i < partsPerMatch; ++i)
{
int idx = 0;
switch (groupType)
{
case GroupingType.HighVsLow:
idx = i * (copy.Count - 1) / (partsPerMatch - 1);
break;
case GroupingType.Nearest:
idx = 0;
break;
case GroupingType.Random:
idx = Utility.Random(copy.Count);
break;
}
thisMatch.Add(copy[idx]);
copy.RemoveAt(idx);
}
level.Matches.Add(new TournyMatch(thisMatch));
}
if (copy.Count > 1)
level.Matches.Add(new TournyMatch(copy));
break;
}
}
Levels.Add(level);
}
}
public class PyramidLevel
{
public PyramidLevel()
{
Matches = new ArrayList();
}
public ArrayList Matches{ get; set; }
public TournyParticipant FreeAdvance{ get; set; }
}
public class TournyMatch
{
public TournyMatch(ArrayList participants)
{
Participants = participants;
for (int i = 0; i < participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)participants[i];
StringBuilder sb = new StringBuilder();
sb.Append("Matched in a duel against ");
if (participants.Count > 2)
sb.AppendFormat("{0} other {1}: ", participants.Count - 1,
part.Players.Count == 1 ? "players" : "teams");
bool hasAppended = false;
for (int j = 0; j < participants.Count; ++j)
{
if (i == j)
continue;
if (hasAppended)
sb.Append(", ");
sb.Append(((TournyParticipant)participants[j]).NameList);
hasAppended = true;
}
sb.Append(".");
part.AddLog(sb.ToString());
}
}
public ArrayList Participants{ get; set; }
public TournyParticipant Winner{ get; set; }
public DuelContext Context{ get; set; }
public bool InProgress => Context != null && Context.Registered;
public void Start(Arena arena, Tournament tourny)
{
TournyParticipant first = (TournyParticipant)Participants[0];
DuelContext dc = new DuelContext((Mobile)first.Players[0], tourny.Ruleset.Layout, false);
dc.Ruleset.Options.SetAll(false);
dc.Ruleset.Options.Or(tourny.Ruleset.Options);
for (int i = 0; i < Participants.Count; ++i)
{
TournyParticipant tournyPart = (TournyParticipant)Participants[i];
Participant duelPart = new Participant(dc, tournyPart.Players.Count);
duelPart.TournyPart = tournyPart;
for (int j = 0; j < tournyPart.Players.Count; ++j)
duelPart.Add((Mobile)tournyPart.Players[j]);
for (int j = 0; j < duelPart.Players.Length; ++j)
if (duelPart.Players[j] != null)
duelPart.Players[j].Ready = true;
dc.Participants.Add(duelPart);
}
if (tourny.EventController != null)
dc.m_EventGame = tourny.EventController.Construct(dc);
dc.m_Tournament = tourny;
dc.m_Match = this;
dc.m_OverrideArena = arena;
if (tourny.SuddenDeath > TimeSpan.Zero &&
(tourny.SuddenDeathRounds == 0 || tourny.Pyramid.Levels.Count <= tourny.SuddenDeathRounds))
dc.StartSuddenDeath(tourny.SuddenDeath);
dc.SendReadyGump(0);
if (dc.StartedBeginCountdown)
{
Context = dc;
for (int i = 0; i < Participants.Count; ++i)
{
TournyParticipant p = (TournyParticipant)Participants[i];
for (int j = 0; j < p.Players.Count; ++j)
{
Mobile mob = (Mobile)p.Players[j];
foreach (Mobile view in mob.GetMobilesInRange(18))
if (!mob.CanSee(view))
mob.Send(view.RemovePacket);
mob.LocalOverheadMessage(MessageType.Emote, 0x3B2, false,
"* Your mind focuses intently on the fight and all other distractions fade away *");
}
}
}
else
{
dc.Unregister();
dc.StopCountdown();
}
}
}
public class TournyParticipant : IComparable
{
public TournyParticipant(Mobile owner)
{
Log = new ArrayList();
Players = new ArrayList();
Players.Add(owner);
}
public TournyParticipant(ArrayList players)
{
Log = new ArrayList();
Players = players;
}
public ArrayList Players{ get; set; }
public ArrayList Log{ get; set; }
public int FreeAdvances{ get; set; }
public int TotalLadderXP
{
get
{
Ladder ladder = Ladder.Instance;
if (ladder == null)
return 0;
int total = 0;
for (int i = 0; i < Players.Count; ++i)
{
Mobile mob = (Mobile)Players[i];
LadderEntry entry = ladder.Find(mob);
if (entry != null)
total += entry.Experience;
}
return total;
}
}
public string NameList
{
get
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Players.Count; ++i)
{
if (Players[i] == null)
continue;
Mobile mob = (Mobile)Players[i];
if (sb.Length > 0)
{
if (Players.Count == 2)
sb.Append(" and ");
else if (i + 1 < Players.Count)
sb.Append(", ");
else
sb.Append(", and ");
}
sb.Append(mob.Name);
}
if (sb.Length == 0)
return "Empty";
return sb.ToString();
}
}
public int CompareTo(object obj)
{
TournyParticipant p = (TournyParticipant)obj;
return p.TotalLadderXP - TotalLadderXP;
}
public void AddLog(string text)
{
Log.Add(text);
}
public void AddLog(string format, params object[] args)
{
AddLog(string.Format(format, args));
}
public void WonMatch(TournyMatch match)
{
AddLog("Match won.");
}
public void LostMatch(TournyMatch match)
{
AddLog("Match lost.");
}
}
public enum TournyBracketGumpType
{
Index,
Rules_Info,
Participant_List,
Participant_Info,
Round_List,
Round_Info,
Match_Info,
Player_Info
}
public class TournamentBracketGump : Gump
{
private const int BlackColor32 = 0x000008;
private const int LabelColor32 = 0xFFFFFF;
private Mobile m_From;
private ArrayList m_List;
private object m_Object;
private int m_Page;
private int m_PerPage;
private Tournament m_Tournament;
private TournyBracketGumpType m_Type;
public TournamentBracketGump(Mobile from, Tournament tourny, TournyBracketGumpType type, ArrayList list, int page,
object obj) : base(50, 50)
{
m_From = from;
m_Tournament = tourny;
m_Type = type;
m_List = list;
m_Page = page;
m_Object = obj;
m_PerPage = 12;
switch (type)
{
case TournyBracketGumpType.Index:
{
AddPage(0);
AddBackground(0, 0, 300, 300, 9380);
StringBuilder sb = new StringBuilder();
if (tourny.TournyType == TournyType.FreeForAll)
{
sb.Append("FFA");
}
else if (tourny.TournyType == TournyType.RandomTeam)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team");
}
else if (tourny.TournyType == TournyType.RedVsBlue)
{
sb.Append("Red v Blue");
}
else if (tourny.TournyType == TournyType.Faction)
{
sb.Append(tourny.ParticipantsPerMatch);
sb.Append("-Team Faction");
}
else
{
for (int i = 0; i < tourny.ParticipantsPerMatch; ++i)
{
if (sb.Length > 0)
sb.Append('v');
sb.Append(tourny.PlayersPerParticipant);
}
}
if (tourny.EventController != null)
sb.Append(' ').Append(tourny.EventController.Title);
sb.Append(" Tournament Bracket");
AddHtml(25, 35, 250, 20, Center(sb.ToString()), false, false);
AddRightArrow(25, 53, ToButtonID(0, 4), "Rules");
AddRightArrow(25, 71, ToButtonID(0, 1), "Participants");
if (m_Tournament.Stage == TournamentStage.Signup)
{
TimeSpan until = m_Tournament.SignupStart + m_Tournament.SignupPeriod - DateTime.UtcNow;
string text;
int secs = (int)until.TotalSeconds;
if (secs > 0)
{
int mins = secs / 60;
secs %= 60;
if (mins > 0 && secs > 0)
text =
$"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")} and {secs} second{(secs == 1 ? "" : "s")}.";
else if (mins > 0)
text = $"The tournament will begin in {mins} minute{(mins == 1 ? "" : "s")}.";
else if (secs > 0)
text = $"The tournament will begin in {secs} second{(secs == 1 ? "" : "s")}.";
else
text = "The tournament will begin shortly.";
}
else
{
text = "The tournament will begin shortly.";
}
AddHtml(25, 92, 250, 40, text, false, false);
}
else
{
AddRightArrow(25, 89, ToButtonID(0, 2), "Rounds");
}
break;
}
case TournyBracketGumpType.Rules_Info:
{
Ruleset ruleset = tourny.Ruleset;
Ruleset basedef = ruleset.Base;
BitArray defs;
if (ruleset.Flavors.Count > 0)
{
defs = new BitArray(basedef.Options);
for (int i = 0; i < ruleset.Flavors.Count; ++i)
defs.Or(((Ruleset)ruleset.Flavors[i]).Options);
}
else
{
defs = basedef.Options;
}
int changes = 0;
BitArray opts = ruleset.Options;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
++changes;
AddPage(0);
AddBackground(0, 0, 300,
60 + 18 + 20 + 20 + 20 + 8 + 20 + ruleset.Flavors.Count * 18 + 4 + 20 + changes * 22 + 6, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 0));
AddHtml(25, 35, 250, 20, Center("Rules"), false, false);
int y = 53;
string groupText = null;
switch (tourny.GroupType)
{
case GroupingType.HighVsLow:
groupText = "High vs Low";
break;
case GroupingType.Nearest:
groupText = "Closest opponent";
break;
case GroupingType.Random:
groupText = "Random";
break;
}
AddHtml(35, y, 190, 20, $"Grouping: {groupText}", false, false);
y += 20;
string tieText = null;
switch (tourny.TieType)
{
case TieType.Random:
tieText = "Random";
break;
case TieType.Highest:
tieText = "Highest advances";
break;
case TieType.Lowest:
tieText = "Lowest advances";
break;
case TieType.FullAdvancement:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both advance" : "Everyone advances";
break;
case TieType.FullElimination:
tieText = tourny.ParticipantsPerMatch == 2 ? "Both eliminated" : "Everyone eliminated";
break;
}
AddHtml(35, y, 190, 20, $"Tiebreaker: {tieText}", false, false);
y += 20;
string sdText = "Off";
if (tourny.SuddenDeath > TimeSpan.Zero)
{
sdText = $"{(int)tourny.SuddenDeath.TotalMinutes}:{tourny.SuddenDeath.Seconds:D2}";
if (tourny.SuddenDeathRounds > 0)
sdText = $"{sdText} (first {tourny.SuddenDeathRounds} rounds)";
else
sdText = $"{sdText} (all rounds)";
}
AddHtml(35, y, 240, 20, $"Sudden Death: {sdText}", false, false);
y += 20;
y += 8;
AddHtml(35, y, 190, 20, $"Ruleset: {basedef.Title}", false, false);
y += 20;
for (int i = 0; i < ruleset.Flavors.Count; ++i, y += 18)
AddHtml(35, y, 190, 20, $" + {((Ruleset)ruleset.Flavors[i]).Title}", false, false);
y += 4;
if (changes > 0)
{
AddHtml(35, y, 190, 20, "Modifications:", false, false);
y += 20;
for (int i = 0; i < opts.Length; ++i)
if (defs[i] != opts[i])
{
string name = ruleset.Layout.FindByIndex(i);
if (name != null) // sanity
{
AddImage(35, y, opts[i] ? 0xD3 : 0xD2);
AddHtml(60, y, 165, 22, name, false, false);
}
y += 22;
}
}
else
{
AddHtml(35, y, 190, 20, "Modifications: None", false, false);
y += 20;
}
break;
}
case TournyBracketGumpType.Participant_List:
{
AddPage(0);
AddBackground(0, 0, 300, 300, 9380);
if (m_List == null)
m_List = new ArrayList(tourny.Participants);
AddLeftArrow(25, 11, ToButtonID(0, 0));
AddHtml(25, 35, 250, 20, Center($"{m_List.Count} Participant{(m_List.Count == 1 ? "" : "s")}"), false,
false);
int index, count, y;
StartPage(out index, out count, out y, 12);
for (int i = 0; i < count; ++i, y += 18)
{
TournyParticipant part = (TournyParticipant)m_List[index + i];
string name = part.NameList;
if (m_Tournament.TournyType != TournyType.Standard && part.Players.Count == 1)
if (part.Players[0] is PlayerMobile pm && pm.DuelPlayer != null)
name = Color(name, pm.DuelPlayer.Eliminated ? 0x6633333 : 0x336666);
AddRightArrow(25, y, ToButtonID(2, index + i), name);
}
break;
}
case TournyBracketGumpType.Participant_Info:
{
if (!(obj is TournyParticipant part))
break;
AddPage(0);
AddBackground(0, 0, 300, 60 + 18 + 20 + part.Players.Count * 18 + 20 + 20 + 160, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 1));
AddHtml(25, 35, 250, 20, Center("Participants"), false, false);
int y = 53;
AddHtml(25, y, 200, 20, part.Players.Count == 1 ? "Players" : "Team", false, false);
y += 20;
for (int i = 0; i < part.Players.Count; ++i)
{
Mobile mob = (Mobile)part.Players[i];
string name = mob.Name;
if (m_Tournament.TournyType != TournyType.Standard)
if (mob is PlayerMobile pm && pm.DuelPlayer != null)
name = Color(name, pm.DuelPlayer.Eliminated ? 0x6633333 : 0x336666);
AddRightArrow(35, y, ToButtonID(4, i), name);
y += 18;
}
AddHtml(25, y, 200, 20,
$"Free Advances: {(part.FreeAdvances == 0 ? "None" : part.FreeAdvances.ToString())}", false, false);
y += 20;
AddHtml(25, y, 200, 20, "Log:", false, false);
y += 20;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < part.Log.Count; ++i)
{
if (sb.Length > 0)
sb.Append("<br>");
sb.Append(part.Log[i]);
}
if (sb.Length == 0)
sb.Append("Nothing logged yet.");
AddHtml(25, y, 250, 150, Color(sb.ToString(), BlackColor32), false, true);
break;
}
case TournyBracketGumpType.Player_Info:
{
AddPage(0);
AddBackground(0, 0, 300, 300, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 3));
AddHtml(25, 35, 250, 20, Center("Participants"), false, false);
if (!(obj is Mobile mob))
break;
Ladder ladder = Ladder.Instance;
LadderEntry entry = ladder?.Find(mob);
AddHtml(25, 53, 250, 20, $"Name: {mob.Name}", false, false);
AddHtml(25, 73, 250, 20,
$"Guild: {(mob.Guild == null ? "None" : mob.Guild.Name + " [" + mob.Guild.Abbreviation + "]")}",
false, false);
AddHtml(25, 93, 250, 20, $"Rank: {(entry == null ? "N/A" : LadderGump.Rank(entry.Index + 1))}", false,
false);
AddHtml(25, 113, 250, 20, $"Level: {(entry == null ? 0 : Ladder.GetLevel(entry.Experience))}", false,
false);
AddHtml(25, 133, 250, 20, $"Wins: {entry?.Wins ?? 0:N0}", false, false);
AddHtml(25, 153, 250, 20, $"Losses: {entry?.Losses ?? 0:N0}", false, false);
break;
}
case TournyBracketGumpType.Round_List:
{
AddPage(0);
AddBackground(0, 0, 300, 300, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 0));
AddHtml(25, 35, 250, 20, Center("Rounds"), false, false);
if (m_List == null)
m_List = new ArrayList(tourny.Pyramid.Levels);
int index, count, y;
StartPage(out index, out count, out y, 12);
for (int i = 0; i < count; ++i, y += 18)
// PyramidLevel level = (PyramidLevel)m_List[index + i];
AddRightArrow(25, y, ToButtonID(3, index + i), "Round #" + (index + i + 1));
break;
}
case TournyBracketGumpType.Round_Info:
{
AddPage(0);
AddBackground(0, 0, 300, 300, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 2));
AddHtml(25, 35, 250, 20, Center("Rounds"), false, false);
if (!(m_Object is PyramidLevel level))
break;
if (m_List == null)
m_List = new ArrayList(level.Matches);
AddRightArrow(25, 53, ToButtonID(5, 0),
$"Free Advance: {(level.FreeAdvance == null ? "None" : level.FreeAdvance.NameList)}");
AddHtml(25, 73, 200, 20, $"{m_List.Count} Match{(m_List.Count == 1 ? "" : "es")}", false, false);
int index, count, y;
StartPage(out index, out count, out y, 10);
for (int i = 0; i < count; ++i, y += 18)
{
TournyMatch match = (TournyMatch)m_List[index + i];
int color = -1;
if (match.InProgress)
color = 0x336666;
else if (match.Context != null && match.Winner == null)
color = 0x666666;
StringBuilder sb = new StringBuilder();
if (m_Tournament.TournyType == TournyType.Standard)
for (int j = 0; j < match.Participants.Count; ++j)
{
if (sb.Length > 0)
sb.Append(" vs ");
TournyParticipant part = (TournyParticipant)match.Participants[j];
string txt = part.NameList;
if (color == -1 && match.Context != null && match.Winner == part)
txt = Color(txt, 0x336633);
else if (color == -1 && match.Context != null)
txt = Color(txt, 0x663333);
sb.Append(txt);
}
else if (m_Tournament.EventController != null || m_Tournament.TournyType == TournyType.RandomTeam ||
m_Tournament.TournyType == TournyType.RedVsBlue ||
m_Tournament.TournyType == TournyType.Faction)
for (int j = 0; j < match.Participants.Count; ++j)
{
if (sb.Length > 0)
sb.Append(" vs ");
TournyParticipant part = (TournyParticipant)match.Participants[j];
string txt;
if (m_Tournament.EventController != null)
{
txt = $"Team {m_Tournament.EventController.GetTeamName(j)} ({part.Players.Count})";
}
else if (m_Tournament.TournyType == TournyType.RandomTeam)
{
txt = $"Team {j + 1} ({part.Players.Count})";
}
else if (m_Tournament.TournyType == TournyType.Faction)
{
if (m_Tournament.ParticipantsPerMatch == 4)
{
string name = "(null)";
switch (j)
{
case 0:
{
name = "Minax";
break;
}
case 1:
{
name = "Council of Mages";
break;
}
case 2:
{
name = "True Britannians";
break;
}
case 3:
{
name = "Shadowlords";
break;
}
}
txt = $"{name} ({part.Players.Count})";
}
else if (m_Tournament.ParticipantsPerMatch == 2)
{
txt = $"{(j == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})";
}
else
{
txt = $"Team {j + 1} ({part.Players.Count})";
}
}
else
{
txt = $"Team {(j == 0 ? "Red" : "Blue")} ({part.Players.Count})";
}
if (color == -1 && match.Context != null && match.Winner == part)
txt = Color(txt, 0x336633);
else if (color == -1 && match.Context != null)
txt = Color(txt, 0x663333);
sb.Append(txt);
}
else if (m_Tournament.TournyType == TournyType.FreeForAll) sb.Append("Free For All");
string str = sb.ToString();
if (color >= 0)
str = Color(str, color);
AddRightArrow(25, y, ToButtonID(5, index + i + 1), str);
}
break;
}
case TournyBracketGumpType.Match_Info:
{
if (!(obj is TournyMatch match))
break;
int ct = m_Tournament.TournyType == TournyType.FreeForAll ? 2 : match.Participants.Count;
AddPage(0);
AddBackground(0, 0, 300, 60 + 18 + 20 + 20 + 20 + ct * 18 + 6, 9380);
AddLeftArrow(25, 11, ToButtonID(0, 5));
AddHtml(25, 35, 250, 20, Center("Rounds"), false, false);
AddHtml(25, 53, 250, 20, $"Winner: {(match.Winner == null ? "N/A" : match.Winner.NameList)}", false,
false);
AddHtml(25, 73, 250, 20,
$"State: {(match.InProgress ? "In progress" : match.Context != null ? "Complete" : "Waiting")}",
false, false);
AddHtml(25, 93, 250, 20, "Participants:", false, false);
if (m_Tournament.TournyType == TournyType.Standard)
for (int i = 0; i < match.Participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)match.Participants[i];
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i), part.NameList);
}
else if (m_Tournament.EventController != null || m_Tournament.TournyType == TournyType.RandomTeam ||
m_Tournament.TournyType == TournyType.RedVsBlue ||
m_Tournament.TournyType == TournyType.Faction)
for (int i = 0; i < match.Participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)match.Participants[i];
if (m_Tournament.EventController != null)
{
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"Team {m_Tournament.EventController.GetTeamName(i)} ({part.Players.Count})");
}
else if (m_Tournament.TournyType == TournyType.RandomTeam)
{
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"Team {i + 1} ({part.Players.Count})");
}
else if (m_Tournament.TournyType == TournyType.Faction)
{
if (m_Tournament.ParticipantsPerMatch == 4)
{
string name = "(null)";
switch (i)
{
case 0:
{
name = "Minax";
break;
}
case 1:
{
name = "Council of Mages";
break;
}
case 2:
{
name = "True Britannians";
break;
}
case 3:
{
name = "Shadowlords";
break;
}
}
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"{name} ({part.Players.Count})");
}
else if (m_Tournament.ParticipantsPerMatch == 2)
{
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"{(i == 0 ? "Evil" : "Hero")} Team ({part.Players.Count})");
}
else
{
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"Team {i + 1} ({part.Players.Count})");
}
}
else
{
AddRightArrow(25, 113 + i * 18, ToButtonID(6, i),
$"Team {(i == 0 ? "Red" : "Blue")} ({part.Players.Count})");
}
}
else if (m_Tournament.TournyType == TournyType.FreeForAll)
AddHtml(25, 113, 250, 20, "Free For All", false, false);
break;
}
}
}
public string Center(string text)
{
return $"<CENTER>{text}</CENTER>";
}
public string Color(string text, int color)
{
return $"<BASEFONT COLOR=#{color:X6}>{text}</BASEFONT>";
}
private void AddBorderedText(int x, int y, int width, int height, string text, int color, int borderColor)
{
AddColoredText(x - 1, y - 1, width, height, text, borderColor);
AddColoredText(x - 1, y + 1, width, height, text, borderColor);
AddColoredText(x + 1, y - 1, width, height, text, borderColor);
AddColoredText(x + 1, y + 1, width, height, text, borderColor);
AddColoredText(x, y, width, height, text, color);
}
private void AddColoredText(int x, int y, int width, int height, string text, int color)
{
if (color == 0)
AddHtml(x, y, width, height, text, false, false);
else
AddHtml(x, y, width, height, Color(text, color), false, false);
}
public void AddRightArrow(int x, int y, int bid, string text)
{
AddButton(x, y, 0x15E1, 0x15E5, bid, GumpButtonType.Reply, 0);
if (text != null)
AddHtml(x + 20, y - 1, 230, 20, text, false, false);
}
public void AddRightArrow(int x, int y, int bid)
{
AddRightArrow(x, y, bid, null);
}
public void AddLeftArrow(int x, int y, int bid, string text)
{
AddButton(x, y, 0x15E3, 0x15E7, bid, GumpButtonType.Reply, 0);
if (text != null)
AddHtml(x + 20, y - 1, 230, 20, text, false, false);
}
public void AddLeftArrow(int x, int y, int bid)
{
AddLeftArrow(x, y, bid, null);
}
public int ToButtonID(int type, int index)
{
return 1 + index * 7 + type;
}
public bool FromButtonID(int bid, out int type, out int index)
{
type = (bid - 1) % 7;
index = (bid - 1) / 7;
return bid >= 1;
}
public void StartPage(out int index, out int count, out int y, int perPage)
{
m_PerPage = perPage;
index = Math.Max(m_Page * perPage, 0);
count = Math.Max(Math.Min(m_List.Count - index, perPage), 0);
y = 53 + (12 - perPage) * 18;
if (m_Page > 0)
AddLeftArrow(242, 35, ToButtonID(1, 0));
if ((m_Page + 1) * perPage < m_List.Count)
AddRightArrow(260, 35, ToButtonID(1, 1));
}
public override void OnResponse(NetState sender, RelayInfo info)
{
int type, index;
if (!FromButtonID(info.ButtonID, out type, out index))
return;
switch (type)
{
case 0:
{
switch (index)
{
case 0:
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Index,
null, 0, null));
break;
case 1:
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Participant_List, null, 0, null));
break;
case 2:
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Round_List,
null, 0, null));
break;
case 4:
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Rules_Info,
null, 0, null));
break;
case 3:
{
Mobile mob = m_Object as Mobile;
for (int i = 0; i < m_Tournament.Participants.Count; ++i)
{
TournyParticipant part = (TournyParticipant)m_Tournament.Participants[i];
if (part.Players.Contains(mob))
{
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Participant_Info, null, 0, part));
break;
}
}
break;
}
case 5:
{
if (!(m_Object is TournyMatch match))
break;
for (int i = 0; i < m_Tournament.Pyramid.Levels.Count; ++i)
{
PyramidLevel level = (PyramidLevel)m_Tournament.Pyramid.Levels[i];
if (level.Matches.Contains(match))
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Round_Info, null, 0, level));
}
break;
}
}
break;
}
case 1:
{
switch (index)
{
case 0:
{
if (m_List != null && m_Page > 0)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, m_Type, m_List, m_Page - 1,
m_Object));
break;
}
case 1:
{
if (m_List != null && (m_Page + 1) * m_PerPage < m_List.Count)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, m_Type, m_List, m_Page + 1,
m_Object));
break;
}
}
break;
}
case 2:
{
if (m_Type != TournyBracketGumpType.Participant_List)
break;
if (index >= 0 && index < m_List.Count)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Participant_Info, null, 0, m_List[index]));
break;
}
case 3:
{
if (m_Type != TournyBracketGumpType.Round_List)
break;
if (index >= 0 && index < m_List.Count)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Round_Info,
null, 0, m_List[index]));
break;
}
case 4:
{
if (m_Type != TournyBracketGumpType.Participant_Info)
break;
if (m_Object is TournyParticipant part && index >= 0 && index < part.Players.Count)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Player_Info,
null, 0, part.Players[index]));
break;
}
case 5:
{
if (m_Type != TournyBracketGumpType.Round_Info)
break;
if (!(m_Object is PyramidLevel level))
break;
if (index == 0)
{
if (level.FreeAdvance != null)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Participant_Info, null, 0, level.FreeAdvance));
else
m_From.SendGump(
new TournamentBracketGump(m_From, m_Tournament, m_Type, m_List, m_Page, m_Object));
}
else if (index >= 1 && index <= level.Matches.Count)
{
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament, TournyBracketGumpType.Match_Info,
null, 0, level.Matches[index - 1]));
}
break;
}
case 6:
{
if (m_Type != TournyBracketGumpType.Match_Info)
break;
if (m_Object is TournyMatch match && index >= 0 && index < match.Participants.Count)
m_From.SendGump(new TournamentBracketGump(m_From, m_Tournament,
TournyBracketGumpType.Participant_Info, null, 0, match.Participants[index]));
break;
}
}
}
}
public class TournamentBracketItem : Item
{
[Constructible]
public TournamentBracketItem() : base(3774)
{
Movable = false;
}
public TournamentBracketItem(Serial serial) : base(serial)
{
}
[CommandProperty(AccessLevel.GameMaster)]
public TournamentController Tournament{ get; set; }
public override string DefaultName => "tournament bracket";
public override void OnDoubleClick(Mobile from)
{
if (!from.InRange(GetWorldLocation(), 2))
{
from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that
}
else
{
Tournament tourny = Tournament?.Tournament;
if (tourny != null)
{
from.CloseGump(typeof(TournamentBracketGump));
from.SendGump(new TournamentBracketGump(from, tourny, TournyBracketGumpType.Index, null, 0, null));
/*if ( tourny.Stage == TournamentStage.Fighting && tourny.Pyramid.Levels.Count > 0 )
from.SendGump( new TournamentBracketGump( tourny, (PyramidLevel)tourny.Pyramid.Levels[tourny.Pyramid.Levels.Count - 1] ) );
else
from.SendGump( new TournamentBracketGump( tourny, 0 ) );*/
}
}
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write(0);
writer.Write(Tournament);
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch (version)
{
case 0:
{
Tournament = reader.ReadItem() as TournamentController;
break;
}
}
}
}
}