Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
using Server.Engines.Help;
|
||||
using Server.Misc;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.IO;
|
||||
using VitaNex.SuperGumps.UI;
|
||||
using VitaNex.Text;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public static partial class AntiAdverts
|
||||
{
|
||||
public const AccessLevel Access = AccessLevel.Administrator;
|
||||
|
||||
public static AntiAdvertsOptions CMOptions { get; private set; }
|
||||
|
||||
public static FileInfo ReportsFile => IOUtility.EnsureFile(VitaNexCore.SavesDirectory + "/AntiAdverts/Reports.bin");
|
||||
|
||||
public static List<AntiAdvertsReport> Reports { get; private set; }
|
||||
|
||||
public static event Action<AntiAdvertNotifyGump> OnBeforeSendGump;
|
||||
public static event Action<AntiAdvertNotifyGump> OnAfterSendGump;
|
||||
|
||||
private static void InvokeOnBeforeSendGump(AntiAdvertNotifyGump g)
|
||||
{
|
||||
if (OnBeforeSendGump != null)
|
||||
{
|
||||
OnBeforeSendGump(g);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeOnAfterSendGump(AntiAdvertNotifyGump g)
|
||||
{
|
||||
if (OnAfterSendGump != null)
|
||||
{
|
||||
OnAfterSendGump(g);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Detect(string text)
|
||||
{
|
||||
|
||||
return Detect(text, out var keyword);
|
||||
}
|
||||
|
||||
public static bool Detect(string text, out string keyword)
|
||||
{
|
||||
// Does the speech contain any forbidden key words?
|
||||
foreach (var kw in CMOptions.KeyWords)
|
||||
{
|
||||
if (CMOptions.SearchMode.Execute(text, kw, CMOptions.SearchCapsIgnore))
|
||||
{
|
||||
keyword = kw;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!kw.Contains(' '))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var kwr in CMOptions.WhitespaceAliases.Select(wa => kw.Replace(' ', wa))
|
||||
.Where(kwr => CMOptions.SearchMode.Execute(text, kwr, CMOptions.SearchCapsIgnore)))
|
||||
{
|
||||
keyword = kwr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
keyword = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void OnSpeech(SpeechEventArgs e)
|
||||
{
|
||||
if (e == null || !(e.Mobile is PlayerMobile) || e.Mobile.NetState == null || String.IsNullOrWhiteSpace(e.Speech) ||
|
||||
e.Mobile.AccessLevel > CMOptions.HandleAccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!Detect(e.Speech, out var detected))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var banned = Ban(e.Mobile, e.Speech);
|
||||
var jailed = Jail(e.Mobile, e.Speech);
|
||||
|
||||
ToConsole(e.Mobile, e.Speech, jailed, banned);
|
||||
ToLog(e.Mobile, e.Speech, jailed, banned);
|
||||
|
||||
SendPage(e.Mobile, e.Speech, jailed, banned);
|
||||
SendWarning(e.Mobile, e.Speech, jailed, banned);
|
||||
|
||||
if (CMOptions.Squelch)
|
||||
{
|
||||
e.Mobile.Squelched = true;
|
||||
}
|
||||
|
||||
if (CMOptions.Kick)
|
||||
{
|
||||
e.Mobile.Say("I've been kicked!");
|
||||
|
||||
if (e.Mobile.NetState != null)
|
||||
{
|
||||
e.Mobile.NetState.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reports.Insert(
|
||||
0,
|
||||
new AntiAdvertsReport(
|
||||
DateTime.Now,
|
||||
e.Mobile,
|
||||
GetDetectedString(false, e.Mobile, e.Speech, jailed, banned),
|
||||
e.Speech,
|
||||
jailed,
|
||||
banned));
|
||||
|
||||
Reports.TrimEndTo(100);
|
||||
}
|
||||
|
||||
private static string GetDetectedString(bool lines, Mobile m, string speech, bool jailed, bool banned)
|
||||
{
|
||||
if (m == null || m.Deleted || !m.Player || String.IsNullOrWhiteSpace(speech))
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
return String.Format(
|
||||
lines
|
||||
? "IP('{0}')\nAccount('{1}')\nChar('{2}')\nJail('{3}')\nBan('{4}')\nQuote(\"{5}\")"
|
||||
: "IP('{0}') Account('{1}') Char('{2}') Jail('{3}') Ban('{4}') Quote(\"{5}\")",
|
||||
m.NetState.Address,
|
||||
m.Account.Username,
|
||||
m.RawName,
|
||||
jailed ? "yes" : "no",
|
||||
banned ? "yes" : "no",
|
||||
speech);
|
||||
}
|
||||
|
||||
private static void ToConsole(Mobile m, string speech, bool jailed, bool banned)
|
||||
{
|
||||
if (m != null && !m.Deleted && m.Player && !String.IsNullOrWhiteSpace(speech) && CMOptions.ConsoleWrite)
|
||||
{
|
||||
Console.WriteLine("[Warning: Advertising Detected]: " + GetDetectedString(false, m, speech, jailed, banned));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ToLog(Mobile m, string speech, bool jailed, bool banned)
|
||||
{
|
||||
if (m != null && !m.Deleted && !String.IsNullOrWhiteSpace(speech) && CMOptions.LogEnabled)
|
||||
{
|
||||
String.Format(
|
||||
"[{0}]: {1}",
|
||||
DateTime.Now.TimeOfDay.ToSimpleString("h:m"),
|
||||
GetDetectedString(false, m, speech, jailed, banned))
|
||||
.Log("DetectedAdvertisers.log");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendPage(Mobile m, string speech, bool jailed, bool banned)
|
||||
{
|
||||
if (m == null || m.Deleted || !m.Player || String.IsNullOrWhiteSpace(speech) || !CMOptions.PageStaff)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entry = PageQueue.GetEntry(m);
|
||||
|
||||
if (entry == null || !entry.Message.StartsWith("[Warning: Advertising Detected]"))
|
||||
{
|
||||
PageQueue.Enqueue(
|
||||
new PageEntry(
|
||||
m,
|
||||
"[Warning: Advertising Detected]: " + GetDetectedString(true, m, speech, jailed, banned),
|
||||
PageType.Account));
|
||||
}
|
||||
}
|
||||
|
||||
private static void SendWarning(Mobile m, string speech, bool jailed, bool banned)
|
||||
{
|
||||
if (m == null || m.Deleted || !m.Player || String.IsNullOrWhiteSpace(speech))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string message;
|
||||
|
||||
if (CMOptions.NotifyStaff)
|
||||
{
|
||||
message = "[Warning: Advertising Detected]: " + GetDetectedString(true, m, speech, jailed, banned);
|
||||
|
||||
Notify.Notify.Broadcast<AntiAdvertNotifyGump>(
|
||||
message,
|
||||
false,
|
||||
1.0,
|
||||
3.0,
|
||||
Color.OrangeRed,
|
||||
InvokeOnBeforeSendGump,
|
||||
InvokeOnAfterSendGump,
|
||||
CMOptions.NotifyAccess);
|
||||
}
|
||||
|
||||
if (!CMOptions.NotifyPlayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message = String.Empty;
|
||||
|
||||
message += "A recent check shows that you may be trying to advertise on " + ServerList.ServerName + ".\n";
|
||||
message += "Advertising is not allowed.\n";
|
||||
|
||||
if (jailed)
|
||||
{
|
||||
message += "You have been jailed until a member of staff can review your case.\n";
|
||||
message += "If you are found to be innocent, you will be promptly released and free to adventure again.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
message += "A report has been submitted to the staff team for review.\n";
|
||||
}
|
||||
|
||||
message += "\nOffending Speech:\n" + speech;
|
||||
|
||||
new NoticeDialogGump(pm)
|
||||
{
|
||||
Width = 420,
|
||||
Height = 420,
|
||||
CanMove = false,
|
||||
CanDispose = false,
|
||||
BlockSpeech = true,
|
||||
RandomButtonID = true,
|
||||
Title = jailed ? "Jailed! Why?" : "Advertising Reported",
|
||||
Html = message
|
||||
}.Send();
|
||||
}
|
||||
|
||||
private static bool Jail(Mobile m, string speech)
|
||||
{
|
||||
return m != null && !m.Deleted && m.Player && !String.IsNullOrWhiteSpace(speech) && CMOptions.Jail &&
|
||||
CMOptions.JailPoint.MoveToWorld(m);
|
||||
}
|
||||
|
||||
private static bool Ban(Mobile m, string speech)
|
||||
{
|
||||
if (m == null || m.Deleted || !m.Player || String.IsNullOrWhiteSpace(speech) || !CMOptions.Ban)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var a = m.Account as Account;
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
a.Banned = true;
|
||||
a.SetBanTags(null, DateTime.UtcNow, TimeSpan.MaxValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
[CoreModule("Anti Adverts", "1.0.0.2")]
|
||||
public static partial class AntiAdverts
|
||||
{
|
||||
static AntiAdverts()
|
||||
{
|
||||
CMOptions = new AntiAdvertsOptions();
|
||||
|
||||
Reports = new List<AntiAdvertsReport>(100);
|
||||
}
|
||||
|
||||
private static void CMConfig()
|
||||
{
|
||||
EventSink.Speech += OnSpeech;
|
||||
}
|
||||
|
||||
private static void CMInvoke()
|
||||
{
|
||||
CommandUtility.Register("AntiAds", Access, e => new AntiAvertsReportsGump(e.Mobile).Send());
|
||||
}
|
||||
|
||||
private static void CMEnabled()
|
||||
{
|
||||
EventSink.Speech += OnSpeech;
|
||||
}
|
||||
|
||||
private static void CMDisabled()
|
||||
{
|
||||
EventSink.Speech -= OnSpeech;
|
||||
}
|
||||
|
||||
private static void CMSave()
|
||||
{
|
||||
VitaNexCore.TryCatch(() => ReportsFile.Serialize(SerializeReports), CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
private static void CMLoad()
|
||||
{
|
||||
VitaNexCore.TryCatch(() => ReportsFile.Deserialize(DeserializeReports), CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
private static void CMDisposed()
|
||||
{ }
|
||||
|
||||
private static void SerializeReports(GenericWriter writer)
|
||||
{
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.WriteBlockList(Reports, (w, r) => r.Serialize(w));
|
||||
}
|
||||
|
||||
private static void DeserializeReports(GenericReader reader)
|
||||
{
|
||||
reader.GetVersion();
|
||||
|
||||
reader.ReadBlockList(r => new AntiAdvertsReport(r), Reports);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAdvertsReport : IEquatable<AntiAdvertsReport>
|
||||
{
|
||||
public DateTime Date { get; private set; }
|
||||
public Mobile Mobile { get; private set; }
|
||||
|
||||
public string Report { get; set; }
|
||||
public string Speech { get; set; }
|
||||
public bool Jailed { get; set; }
|
||||
public bool Banned { get; set; }
|
||||
public bool Viewed { get; set; }
|
||||
|
||||
public AntiAdvertsReport(
|
||||
DateTime date,
|
||||
Mobile m,
|
||||
string report,
|
||||
string speech,
|
||||
bool jailed,
|
||||
bool banned,
|
||||
bool viewed = false)
|
||||
{
|
||||
Date = date;
|
||||
Mobile = m;
|
||||
Speech = speech;
|
||||
Report = report;
|
||||
Viewed = viewed;
|
||||
Jailed = jailed;
|
||||
Banned = banned;
|
||||
}
|
||||
|
||||
public AntiAdvertsReport(GenericReader reader)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format(
|
||||
"[{0}] {1}: {2}",
|
||||
Date.ToSimpleString("t@h:m@ m-d"),
|
||||
Mobile == null ? "-null-" : Mobile.RawName,
|
||||
Report);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = (Mobile != null ? Mobile.Serial.Value : 0);
|
||||
hashCode = (hashCode * 397) ^ Date.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ (Speech != null ? Speech.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ (Report != null ? Report.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ Jailed.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Banned.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is AntiAdvertsReport && Equals((AntiAdvertsReport)obj);
|
||||
}
|
||||
|
||||
public bool Equals(AntiAdvertsReport other)
|
||||
{
|
||||
return !ReferenceEquals(other, null) && Mobile == other.Mobile && Date == other.Date && Jailed == other.Jailed &&
|
||||
Banned == other.Banned && Speech == other.Speech && Report == other.Report;
|
||||
}
|
||||
|
||||
public void Serialize(GenericWriter writer)
|
||||
{
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Date);
|
||||
writer.Write(Mobile);
|
||||
writer.Write(Speech);
|
||||
writer.Write(Report);
|
||||
writer.Write(Viewed);
|
||||
writer.Write(Jailed);
|
||||
writer.Write(Banned);
|
||||
}
|
||||
|
||||
public void Deserialize(GenericReader reader)
|
||||
{
|
||||
reader.GetVersion();
|
||||
|
||||
Date = reader.ReadDateTime();
|
||||
Mobile = reader.ReadMobile<PlayerMobile>();
|
||||
Speech = reader.ReadString();
|
||||
Report = reader.ReadString();
|
||||
Viewed = reader.ReadBool();
|
||||
Jailed = reader.ReadBool();
|
||||
Banned = reader.ReadBool();
|
||||
}
|
||||
|
||||
public static bool operator ==(AntiAdvertsReport l, AntiAdvertsReport r)
|
||||
{
|
||||
return ReferenceEquals(l, null) ? ReferenceEquals(r, null) : l.Equals(r);
|
||||
}
|
||||
|
||||
public static bool operator !=(AntiAdvertsReport l, AntiAdvertsReport r)
|
||||
{
|
||||
return ReferenceEquals(l, null) ? !ReferenceEquals(r, null) : !l.Equals(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.Text;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAdvertsOptions : CoreModuleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Log the info in a log file?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool LogEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Write info to the console?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool ConsoleWrite { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Page staff with offense?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool PageStaff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Send a warning broadcast to staff?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool NotifyStaff { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Send a warning to offending player
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool NotifyPlayer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Send a warning broadcast to staff of this access and above.
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public AccessLevel NotifyAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// AccessLevels higher than this value will not have their speech handled.
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public AccessLevel HandleAccess { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Jail the offender?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool Jail { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location and map of the jail area.
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public MapPoint JailPoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Squelch the offender?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool Squelch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Kick the offender?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool Kick { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Ban the offender?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool Ban { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mode used to find matching key words in text.
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public StringSearchFlags SearchMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the search be case-insensitive?
|
||||
/// </summary>
|
||||
[CommandProperty(AntiAdverts.Access)]
|
||||
public bool SearchCapsIgnore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of common symbols used to replace whitespaces to avoid detection.
|
||||
/// </summary>
|
||||
public List<char> WhitespaceAliases { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A case-insensitive list of all disallowed keywords and phrases.
|
||||
/// Any whitespaces in keywords will also be tested with their aliases.
|
||||
/// </summary>
|
||||
public List<string> KeyWords { get; private set; }
|
||||
|
||||
public AntiAdvertsOptions()
|
||||
: base(typeof(AntiAdverts))
|
||||
{
|
||||
WhitespaceAliases = new List<char>();
|
||||
KeyWords = new List<string>();
|
||||
|
||||
EnsureDefaults();
|
||||
}
|
||||
|
||||
public AntiAdvertsOptions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public void EnsureDefaults()
|
||||
{
|
||||
LogEnabled = true;
|
||||
ConsoleWrite = true;
|
||||
|
||||
PageStaff = true;
|
||||
|
||||
NotifyStaff = true;
|
||||
NotifyAccess = AccessLevel.Counselor;
|
||||
|
||||
HandleAccess = AccessLevel.Player;
|
||||
|
||||
Jail = false;
|
||||
JailPoint = new MapPoint(Map.Felucca, new Point3D(5275, 1174, 0));
|
||||
|
||||
Squelch = false;
|
||||
Kick = false;
|
||||
Ban = false;
|
||||
|
||||
SearchMode = StringSearchFlags.Contains;
|
||||
SearchCapsIgnore = true;
|
||||
|
||||
WhitespaceAliases.AddRange(
|
||||
new[]
|
||||
{
|
||||
'_', ':', ';', '@', '#', '=', '-', '+', '*', '/', //
|
||||
'\\', '!', '"', '<27>', '$', '%', '^', '&', '"' //
|
||||
});
|
||||
|
||||
KeyWords.AddRange(
|
||||
new[]
|
||||
{
|
||||
"port: 2593", "port 2593", "port: 2595", "port 2595", "paypal", "no-ip", "joinuo", "uoisnotdead", "shard.li",
|
||||
"easyuo"
|
||||
});
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
EnsureDefaults();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
EnsureDefaults();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(2);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 2:
|
||||
writer.Write(NotifyPlayer);
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
writer.WriteFlag(SearchMode);
|
||||
writer.Write(SearchCapsIgnore);
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockList(WhitespaceAliases, (w, a) => w.Write(a));
|
||||
writer.WriteBlockList(KeyWords, (w, k) => w.Write(k));
|
||||
|
||||
writer.Write(LogEnabled);
|
||||
writer.Write(ConsoleWrite);
|
||||
|
||||
writer.Write(PageStaff);
|
||||
|
||||
writer.Write(NotifyStaff);
|
||||
writer.WriteFlag(NotifyAccess);
|
||||
|
||||
writer.Write(Jail);
|
||||
JailPoint.Serialize(writer);
|
||||
|
||||
writer.Write(Squelch);
|
||||
writer.Write(Kick);
|
||||
writer.Write(Ban);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 2:
|
||||
NotifyPlayer = reader.ReadBool();
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
SearchMode = reader.ReadFlag<StringSearchFlags>();
|
||||
SearchCapsIgnore = reader.ReadBool();
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
if (version < 1)
|
||||
{
|
||||
SearchMode = StringSearchFlags.Contains;
|
||||
SearchCapsIgnore = true;
|
||||
}
|
||||
|
||||
WhitespaceAliases = reader.ReadBlockList(r => r.ReadChar());
|
||||
KeyWords = reader.ReadBlockList(r => r.ReadString());
|
||||
|
||||
LogEnabled = reader.ReadBool();
|
||||
ConsoleWrite = reader.ReadBool();
|
||||
|
||||
PageStaff = reader.ReadBool();
|
||||
|
||||
NotifyStaff = reader.ReadBool();
|
||||
NotifyAccess = reader.ReadFlag<AccessLevel>();
|
||||
|
||||
Jail = reader.ReadBool();
|
||||
JailPoint = new MapPoint(reader);
|
||||
|
||||
Squelch = reader.ReadBool();
|
||||
Kick = reader.ReadBool();
|
||||
Ban = reader.ReadBool();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAdvertsEditAliasesGump : GenericListGump<char>
|
||||
{
|
||||
public string Input { get; set; }
|
||||
|
||||
public AntiAdvertsEditAliasesGump(Mobile user, Gump parent = null)
|
||||
: base(
|
||||
user,
|
||||
parent,
|
||||
list: AntiAdverts.CMOptions.WhitespaceAliases,
|
||||
title: "Anti-Adverts: Whitespace Aliases",
|
||||
emptyText: "No whitespace aliases to display.",
|
||||
canAdd: true,
|
||||
canClear: true,
|
||||
canRemove: true)
|
||||
{ }
|
||||
|
||||
public override string GetSearchKeyFor(char key)
|
||||
{
|
||||
return key.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
protected override bool OnBeforeListAdd()
|
||||
{
|
||||
if (Input != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Send(
|
||||
new InputDialogGump(
|
||||
User,
|
||||
Refresh(),
|
||||
title: "Add Whitespace Alias",
|
||||
html: "Write a single character to add it to this list.",
|
||||
limit: 1,
|
||||
callback: (b1, text) =>
|
||||
{
|
||||
Input = !String.IsNullOrWhiteSpace(text) ? text : String.Empty;
|
||||
HandleAdd();
|
||||
Input = null;
|
||||
}));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override List<char> GetExternalList()
|
||||
{
|
||||
return AntiAdverts.CMOptions.WhitespaceAliases;
|
||||
}
|
||||
|
||||
public override char GetListAddObject()
|
||||
{
|
||||
return Input.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAdvertsEditKeyWordsGump : GenericListGump<string>
|
||||
{
|
||||
public string Input { get; set; }
|
||||
|
||||
public AntiAdvertsEditKeyWordsGump(Mobile user, Gump parent = null)
|
||||
: base(
|
||||
user,
|
||||
parent,
|
||||
list: AntiAdverts.CMOptions.KeyWords,
|
||||
title: "Anti-Adverts: Key Words",
|
||||
emptyText: "No key words to display.",
|
||||
canAdd: true,
|
||||
canClear: true,
|
||||
canRemove: true)
|
||||
{ }
|
||||
|
||||
public override string GetSearchKeyFor(string key)
|
||||
{
|
||||
return key ?? String.Empty;
|
||||
}
|
||||
|
||||
protected override bool OnBeforeListAdd()
|
||||
{
|
||||
if (Input != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Send(
|
||||
new InputDialogGump(
|
||||
User,
|
||||
Refresh(),
|
||||
title: "Add Key Word",
|
||||
html: "Write a phrase to add it to this list.",
|
||||
callback: (b1, text) =>
|
||||
{
|
||||
Input = !String.IsNullOrWhiteSpace(text) ? text : null;
|
||||
HandleAdd();
|
||||
Input = null;
|
||||
}));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override List<string> GetExternalList()
|
||||
{
|
||||
return AntiAdverts.CMOptions.KeyWords;
|
||||
}
|
||||
|
||||
public override string GetListAddObject()
|
||||
{
|
||||
return Input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
|
||||
using VitaNex.Notify;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAdvertNotifyGump : NotifyGump
|
||||
{
|
||||
private static void InitSettings(NotifySettings settings)
|
||||
{
|
||||
settings.CanIgnore = true;
|
||||
settings.Access = AntiAdverts.Access;
|
||||
settings.Desc = "Advertising Reports";
|
||||
}
|
||||
|
||||
public AntiAdvertNotifyGump(Mobile user, string html)
|
||||
: base(user, html)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AntiAdverts
|
||||
{
|
||||
public sealed class AntiAvertsReportsGump : ListGump<AntiAdvertsReport>
|
||||
{
|
||||
public AntiAvertsReportsGump(Mobile user)
|
||||
: base(user)
|
||||
{
|
||||
Title = "Anti-Advert Reports";
|
||||
EmptyText = "No reports to display.";
|
||||
|
||||
Sorted = true;
|
||||
CanSearch = true;
|
||||
CanMove = false;
|
||||
}
|
||||
|
||||
protected override void CompileList(List<AntiAdvertsReport> list)
|
||||
{
|
||||
list.Clear();
|
||||
list.AddRange(AntiAdverts.Reports);
|
||||
|
||||
base.CompileList(list);
|
||||
}
|
||||
|
||||
protected override void CompileMenuOptions(MenuGumpOptions list)
|
||||
{
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"Options",
|
||||
() =>
|
||||
{
|
||||
Refresh();
|
||||
User.SendGump(new PropertiesGump(User, AntiAdverts.CMOptions));
|
||||
},
|
||||
HighlightHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry("Key Words", () => new AntiAdvertsEditKeyWordsGump(User, this).Send(), HighlightHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry("Whitespace Aliases", () => new AntiAdvertsEditAliasesGump(User, this).Send(), HighlightHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"Mark All: Viewed",
|
||||
() =>
|
||||
{
|
||||
AntiAdverts.Reports.ForEach(t => t.Viewed = true);
|
||||
|
||||
User.SendMessage("All reports have been marked as viewed.");
|
||||
Refresh(true);
|
||||
},
|
||||
TextHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"Mark All: Not Viewed",
|
||||
() =>
|
||||
{
|
||||
AntiAdverts.Reports.ForEach(t => t.Viewed = false);
|
||||
|
||||
User.SendMessage("All reports have been marked as not viewed.");
|
||||
Refresh(true);
|
||||
},
|
||||
TextHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"Delete All",
|
||||
() =>
|
||||
{
|
||||
AntiAdverts.Reports.Free(true);
|
||||
|
||||
User.SendMessage("All reports have been deleted.");
|
||||
Refresh(true);
|
||||
},
|
||||
ErrorHue));
|
||||
|
||||
list.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"Delete Old",
|
||||
() =>
|
||||
{
|
||||
var expire = DateTime.Now - TimeSpan.FromDays(7);
|
||||
|
||||
AntiAdverts.Reports.RemoveAll(t => t.Date <= expire);
|
||||
AntiAdverts.Reports.Free(false);
|
||||
|
||||
User.SendMessage("All old reports have been deleted.");
|
||||
Refresh(true);
|
||||
},
|
||||
ErrorHue));
|
||||
|
||||
base.CompileMenuOptions(list);
|
||||
}
|
||||
|
||||
protected override void SelectEntry(GumpButton button, AntiAdvertsReport entry)
|
||||
{
|
||||
base.SelectEntry(button, entry);
|
||||
|
||||
var opts = new MenuGumpOptions();
|
||||
|
||||
opts.AppendEntry(
|
||||
new ListGumpEntry(
|
||||
"View",
|
||||
() =>
|
||||
{
|
||||
entry.Viewed = true;
|
||||
|
||||
new NoticeDialogGump(User, Refresh(true))
|
||||
{
|
||||
Title = "Anti-Advert Report",
|
||||
Html = entry.ToString(),
|
||||
Modal = false,
|
||||
CanMove = false
|
||||
}.Send();
|
||||
},
|
||||
HighlightHue));
|
||||
|
||||
opts.AppendEntry(
|
||||
!entry.Viewed
|
||||
? new ListGumpEntry("Mark Viewed", () => entry.Viewed = true)
|
||||
: new ListGumpEntry("Mark Not Viewed", () => entry.Viewed = false));
|
||||
|
||||
opts.AppendEntry(new ListGumpEntry("Delete", () => AntiAdverts.Reports.Remove(entry), ErrorHue));
|
||||
|
||||
new MenuGump(User, Refresh(), opts, button).Send();
|
||||
}
|
||||
|
||||
protected override int GetLabelHue(int index, int pageIndex, AntiAdvertsReport entry)
|
||||
{
|
||||
return entry != null ? entry == Selected ? HighlightHue : !entry.Viewed ? TextHue : ErrorHue : ErrorHue;
|
||||
}
|
||||
|
||||
protected override string GetLabelText(int index, int pageIndex, AntiAdvertsReport entry)
|
||||
{
|
||||
return entry != null ? entry.ToString() : String.Empty;
|
||||
}
|
||||
|
||||
public override string GetSearchKeyFor(AntiAdvertsReport key)
|
||||
{
|
||||
return key != null ? key.ToString() : String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
271
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Arcade.cs
Normal file
271
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Arcade.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Commands;
|
||||
|
||||
using VitaNex.IO;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
[CoreModule("Games Arcade", "1.0.0.0")]
|
||||
public static class Arcade
|
||||
{
|
||||
public const AccessLevel Access = AccessLevel.Administrator;
|
||||
|
||||
public static ArcadeOptions CMOptions { get; private set; }
|
||||
|
||||
public static BinaryDataStore<Type, IGame> Games { get; private set; }
|
||||
|
||||
public static BinaryDataStore<Mobile, ArcadeProfile> Profiles { get; private set; }
|
||||
|
||||
public static Type[] GameTypes { get; private set; }
|
||||
|
||||
static Arcade()
|
||||
{
|
||||
CMOptions = new ArcadeOptions();
|
||||
|
||||
Games = new BinaryDataStore<Type, IGame>(VitaNexCore.SavesDirectory + "/Arcade", "Games")
|
||||
{
|
||||
Async = true,
|
||||
OnSerialize = SerializeGames,
|
||||
OnDeserialize = DeserializeGames
|
||||
};
|
||||
|
||||
Profiles = new BinaryDataStore<Mobile, ArcadeProfile>(VitaNexCore.SavesDirectory + "/Arcade", "Profiles")
|
||||
{
|
||||
Async = true,
|
||||
OnSerialize = SerializeProfiles,
|
||||
OnDeserialize = DeserializeProfiles
|
||||
};
|
||||
|
||||
GameTypes = typeof(IGame).GetConstructableChildren();
|
||||
|
||||
foreach (var t in GameTypes)
|
||||
{
|
||||
var g = CreateGame(t);
|
||||
|
||||
if (g != null)
|
||||
{
|
||||
Games[t] = g;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CMConfig()
|
||||
{
|
||||
CommandUtility.Register("Arcade", AccessLevel.Player, HandleCommand);
|
||||
CommandUtility.RegisterAlias("Arcade", "Games");
|
||||
}
|
||||
|
||||
private static void CMInvoke()
|
||||
{ }
|
||||
|
||||
private static void CMSave()
|
||||
{
|
||||
Games.Export();
|
||||
}
|
||||
|
||||
private static void CMLoad()
|
||||
{
|
||||
Games.Import();
|
||||
}
|
||||
|
||||
private static void HandleCommand(CommandEventArgs e)
|
||||
{
|
||||
if (CMOptions.ModuleEnabled)
|
||||
{
|
||||
new ArcadeUI(e.Mobile).Send();
|
||||
}
|
||||
}
|
||||
|
||||
private static IGame CreateGame(Type t)
|
||||
{
|
||||
try
|
||||
{
|
||||
return t.CreateInstanceSafe<IGame>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static ArcadeProfile EnsureProfile(Mobile m)
|
||||
{
|
||||
if (m == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var p = Profiles.GetValue(m);
|
||||
|
||||
if (p == null || p.Owner != m)
|
||||
{
|
||||
if (!m.Player || m.Deleted)
|
||||
{
|
||||
Profiles.Remove(m);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.Clear();
|
||||
p = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Profiles[m] = p = new ArcadeProfile(m);
|
||||
}
|
||||
}
|
||||
else if (!m.Player || m.Deleted)
|
||||
{
|
||||
Profiles.Remove(m);
|
||||
|
||||
p.Clear();
|
||||
p = null;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public static bool OpenGame(Type t, Mobile m)
|
||||
{
|
||||
return VitaNexCore.TryCatchGet(
|
||||
() =>
|
||||
{
|
||||
var g = Games.GetValue(t);
|
||||
|
||||
return g != null && g.Open(m);
|
||||
},
|
||||
CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
public static bool OpenGame<T>(Mobile m)
|
||||
where T : IGame
|
||||
{
|
||||
return OpenGame(typeof(T), m);
|
||||
}
|
||||
|
||||
public static void CloseGame(Mobile m, Type t)
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
() =>
|
||||
{
|
||||
var g = Games.GetValue(t);
|
||||
|
||||
if (g != null)
|
||||
{
|
||||
g.Close(m);
|
||||
}
|
||||
},
|
||||
CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
private static bool SerializeGames(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockDictionary(
|
||||
Games,
|
||||
(w, t, g) =>
|
||||
{
|
||||
w.WriteType(t);
|
||||
g.Serialize(w);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DeserializeGames(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var t = r.ReadType();
|
||||
var g = Games.GetValue(t) ?? CreateGame(t);
|
||||
|
||||
g.Deserialize(r);
|
||||
|
||||
return new KeyValuePair<Type, IGame>(t, g);
|
||||
},
|
||||
Games);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SerializeProfiles(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockDictionary(
|
||||
Profiles,
|
||||
(w, m, p) =>
|
||||
{
|
||||
w.Write(m);
|
||||
p.Serialize(w);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DeserializeProfiles(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var m = r.ReadMobile();
|
||||
var p = new ArcadeProfile(r);
|
||||
|
||||
return new KeyValuePair<Mobile, ArcadeProfile>(m, p);
|
||||
},
|
||||
Profiles);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public sealed class TrapSweeper : Game<TrapSweeperEngine>
|
||||
{
|
||||
private readonly IconDefinition _Icon = IconDefinition.FromGump(24013, 1258);
|
||||
|
||||
public override IconDefinition Icon => _Icon;
|
||||
|
||||
public override string Name => "Trap Sweeper";
|
||||
|
||||
public override string Desc => "Sweep the castle for traps!";
|
||||
|
||||
public override string Help => _Help;
|
||||
|
||||
private static readonly string _Help = String.Concat(
|
||||
"<BIG>Rules & Basics</BIG>",
|
||||
"<BR>",
|
||||
"<BR><BIG>Objective</BIG>",
|
||||
"<BR>Find the empty tiles while avoiding the traps.",
|
||||
"<BR>The faster you clear the floor, the better your score.",
|
||||
"<BR>",
|
||||
"<BR><BIG>Floors</BIG>",
|
||||
"<BR>There are three floors to choose from, each more difficult than the last.",
|
||||
"<BR>",
|
||||
"<BR><B>* Rookie:</B> Easy mode, small floor.",
|
||||
"<BR><B>* Guard:</B> Normal mode, medium floor.",
|
||||
"<BR><B>* Knight:</B> Expert mode, large floor.",
|
||||
"<BR><B>* Random:</B> Generates a random floor.",
|
||||
"<BR>",
|
||||
"<BR><BIG>Rules</BIG>",
|
||||
"<BR>Reveal a trap, you die.",
|
||||
"<BR>Reveal an empty tile, you continue sweeping.",
|
||||
"<BR>Reveal a bonus tile, you continue and collect a reward when you win.",
|
||||
"<BR>Reveal a number tile, you continue and it tells you how many traps lay hidden in the eight surrounding tiles.",
|
||||
"<BR><I>These numbers will help you decide which tiles are safe to reveal.</I>",
|
||||
"<BR>",
|
||||
"<BR><BIG>Tips</BIG>",
|
||||
"<BR>Mark the traps!",
|
||||
"<BR>If you think a tile hides a trap, select Mark and click it, this will highlight the tile.",
|
||||
"<BR>You can unhighlight a tile by selecting Mark and clicking it again.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,789 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public class TrapSweeperEngine : GameEngine<TrapSweeper, TrapSweeperUI>
|
||||
{
|
||||
public const int MinWidth = 10;
|
||||
public const int MaxWidth = 22;
|
||||
|
||||
public const int MinHeight = 10;
|
||||
public const int MaxHeight = 15;
|
||||
|
||||
public const int MinDensity = 10;
|
||||
public const int MaxDensity = 30;
|
||||
|
||||
public const int MinBonusDensity = 1;
|
||||
public const int MaxBonusDensity = 3;
|
||||
|
||||
public const int MinPoints = 100;
|
||||
public const int MaxPoints = 500;
|
||||
|
||||
private Grid<TrapSweeperTile> _Grid;
|
||||
|
||||
public int Width => _Grid != null ? _Grid.Width : 0;
|
||||
public int Height => _Grid != null ? _Grid.Height : 0;
|
||||
public int Capacity => _Grid != null ? _Grid.Capacity : 0;
|
||||
public int Count => _Grid != null ? _Grid.Count : 0;
|
||||
|
||||
public TrapSweeperState State { get; private set; }
|
||||
public TrapSweeperMode Mode { get; private set; }
|
||||
|
||||
public DateTime Started { get; private set; }
|
||||
public DateTime Ended { get; private set; }
|
||||
|
||||
public bool Mark { get; private set; }
|
||||
|
||||
public int Blanks { get; private set; }
|
||||
public int Traps { get; private set; }
|
||||
public int Bonuses { get; private set; }
|
||||
|
||||
public int Visible { get; private set; }
|
||||
public int Marked { get; private set; }
|
||||
|
||||
public int Rewards { get; private set; }
|
||||
|
||||
public TrapSweeperEngine(TrapSweeper game, Mobile user)
|
||||
: base(game, user)
|
||||
{
|
||||
_Grid = new Grid<TrapSweeperTile>();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
Visible = 0;
|
||||
Marked = 0;
|
||||
|
||||
Rewards = 0;
|
||||
|
||||
Mark = false;
|
||||
}
|
||||
|
||||
public IEnumerable<TrapSweeperTile> AllTiles()
|
||||
{
|
||||
return _Grid ?? _Grid.Ensure();
|
||||
}
|
||||
|
||||
public IEnumerable<TrapSweeperTile> FindTiles(int x, int y, int w, int h)
|
||||
{
|
||||
return _Grid == null ? _Grid.Ensure() : _Grid.FindCells(x, y, w, h);
|
||||
}
|
||||
|
||||
public IEnumerable<T> AllTiles<T>()
|
||||
where T : TrapSweeperTile
|
||||
{
|
||||
return (_Grid ?? _Grid.Ensure()).OfType<T>();
|
||||
}
|
||||
|
||||
public IEnumerable<T> FindTiles<T>(int x, int y, int w, int h)
|
||||
where T : TrapSweeperTile
|
||||
{
|
||||
return (_Grid == null ? _Grid.Ensure() : _Grid.FindCells(x, y, w, h)).OfType<T>();
|
||||
}
|
||||
|
||||
public void Generate(int w, int h, int d, int b, int p)
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
w = Math.Max(MinWidth, Math.Min(MaxWidth, w));
|
||||
h = Math.Max(MinHeight, Math.Min(MaxHeight, h));
|
||||
d = Math.Max(MinDensity, Math.Min(MaxDensity, d));
|
||||
b = Math.Max(MinBonusDensity, Math.Min(MaxBonusDensity, b));
|
||||
p = Math.Max(MinPoints, Math.Min(MaxPoints, p));
|
||||
|
||||
if (_Grid == null)
|
||||
{
|
||||
_Grid = new Grid<TrapSweeperTile>(w, h);
|
||||
}
|
||||
else if (_Grid.Width != w || _Grid.Height != h)
|
||||
{
|
||||
_Grid.Resize(w, h);
|
||||
}
|
||||
|
||||
_Grid.SetAllContent((x, y) => null);
|
||||
|
||||
var q = new List<Point2D>(_Grid.Capacity);
|
||||
|
||||
_Grid.ForEach((x, y, t) => q.Add(new Point2D(x, y)));
|
||||
|
||||
q.Shuffle();
|
||||
|
||||
var traps = (int)Math.Floor(q.Count * (d / 100.0));
|
||||
var bonus = (int)Math.Floor((q.Count - traps) * (b / 100.0));
|
||||
|
||||
Parallel.ForEach(
|
||||
q,
|
||||
t =>
|
||||
{
|
||||
if (traps > 0)
|
||||
{
|
||||
_Grid[t.X, t.Y] = new TrapSweeperTileTrap(this, t.X, t.Y);
|
||||
Interlocked.Decrement(ref traps);
|
||||
}
|
||||
else if (bonus > 0)
|
||||
{
|
||||
_Grid[t.X, t.Y] = new TrapSweeperTileBonus(this, t.X, t.Y);
|
||||
Interlocked.Decrement(ref bonus);
|
||||
}
|
||||
else
|
||||
{
|
||||
_Grid[t.X, t.Y] = new TrapSweeperTileBlank(this, t.X, t.Y);
|
||||
}
|
||||
});
|
||||
|
||||
q.Free(true);
|
||||
|
||||
Blanks = Traps = Bonuses = 0;
|
||||
|
||||
foreach (var t in AllTiles())
|
||||
{
|
||||
if (t is TrapSweeperTileBlank)
|
||||
{
|
||||
++Blanks;
|
||||
|
||||
t.Points = p;
|
||||
}
|
||||
else if (t is TrapSweeperTileTrap)
|
||||
{
|
||||
++Traps;
|
||||
|
||||
t.Points = 0;
|
||||
}
|
||||
else if (t is TrapSweeperTileBonus)
|
||||
{
|
||||
++Bonuses;
|
||||
|
||||
t.Points = p * 2;
|
||||
}
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void GenerateEasy()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Generate(MinWidth, MinHeight, MinDensity, MinBonusDensity, MinPoints);
|
||||
}
|
||||
|
||||
public void GenerateNormal()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
const int w = MinWidth + ((MaxWidth - MinWidth) / 2);
|
||||
const int h = MinHeight + ((MaxHeight - MinHeight) / 2);
|
||||
const int d = MinDensity + ((MaxDensity - MinDensity) / 2);
|
||||
const int b = MinBonusDensity + ((MaxBonusDensity - MinBonusDensity) / 2);
|
||||
const int p = MinPoints + ((MaxPoints - MinPoints) / 2);
|
||||
|
||||
Generate(w, h, d, b, p);
|
||||
}
|
||||
|
||||
public void GenerateHard()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Generate(MaxWidth, MaxHeight, MaxDensity, MaxBonusDensity, MaxPoints);
|
||||
}
|
||||
|
||||
public void GenerateRandom()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Generate(
|
||||
Utility.RandomMinMax(MinWidth, MaxWidth),
|
||||
Utility.RandomMinMax(MinHeight, MaxHeight),
|
||||
Utility.RandomMinMax(MinDensity, MaxDensity),
|
||||
Utility.RandomMinMax(MinBonusDensity, MaxBonusDensity),
|
||||
Utility.RandomMinMax(MinPoints, MaxPoints));
|
||||
}
|
||||
|
||||
public void DoCollect()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Give Rewards
|
||||
|
||||
DoMenu();
|
||||
}
|
||||
|
||||
public void DoMenu()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
UI.CanDispose = true;
|
||||
|
||||
if (State == TrapSweeperState.Play)
|
||||
{
|
||||
State = TrapSweeperState.Menu;
|
||||
DoEnd(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
State = TrapSweeperState.Menu;
|
||||
UI.Refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoMode(TrapSweeperMode opt)
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Mode = opt;
|
||||
|
||||
UI.Refresh(true);
|
||||
}
|
||||
|
||||
public void DoMark()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Mark = !Mark;
|
||||
|
||||
UI.Refresh(true);
|
||||
}
|
||||
|
||||
public void DoPlay()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
UI.CanDispose = false;
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case TrapSweeperMode.Easy:
|
||||
GenerateEasy();
|
||||
break;
|
||||
case TrapSweeperMode.Normal:
|
||||
GenerateNormal();
|
||||
break;
|
||||
case TrapSweeperMode.Hard:
|
||||
GenerateHard();
|
||||
break;
|
||||
case TrapSweeperMode.Random:
|
||||
GenerateRandom();
|
||||
break;
|
||||
}
|
||||
|
||||
DoStart();
|
||||
}
|
||||
|
||||
public void DoQuit()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
UI.CanDispose = true;
|
||||
|
||||
if (State == TrapSweeperState.Play)
|
||||
{
|
||||
State = TrapSweeperState.Menu;
|
||||
DoEnd(false);
|
||||
}
|
||||
else if (State != TrapSweeperState.Menu)
|
||||
{
|
||||
State = TrapSweeperState.Menu;
|
||||
UI.Refresh(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
State = TrapSweeperState.Menu;
|
||||
UI.Close(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void DoStart()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
UI.CanDispose = false;
|
||||
|
||||
State = TrapSweeperState.Play;
|
||||
Started = DateTime.UtcNow;
|
||||
|
||||
UI.Refresh(true);
|
||||
}
|
||||
|
||||
public void DoEnd(bool win)
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
UI.CanDispose = true;
|
||||
|
||||
State = win ? TrapSweeperState.Win : TrapSweeperState.Lose;
|
||||
Ended = DateTime.UtcNow;
|
||||
|
||||
if (win)
|
||||
{
|
||||
var factor = 1.0;
|
||||
|
||||
var time = Ended - Started;
|
||||
|
||||
if (time.TotalMinutes > 0)
|
||||
{
|
||||
double threshold, multiplier;
|
||||
|
||||
switch (Mode)
|
||||
{
|
||||
case TrapSweeperMode.Easy:
|
||||
{
|
||||
threshold = 10.0;
|
||||
multiplier = 0.33;
|
||||
}
|
||||
break;
|
||||
case TrapSweeperMode.Normal:
|
||||
{
|
||||
threshold = 10.0;
|
||||
multiplier = 0.66;
|
||||
}
|
||||
break;
|
||||
case TrapSweeperMode.Hard:
|
||||
{
|
||||
threshold = 10.0;
|
||||
multiplier = 1.00;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
threshold = Utility.RandomMinMax(10.0, 30.0);
|
||||
multiplier = Utility.RandomMinMax(0.33, 1.00);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (time.TotalMinutes <= threshold)
|
||||
{
|
||||
factor += (1.0 - (time.TotalMinutes / threshold)) * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
OffsetPoints(Points * factor, true);
|
||||
|
||||
Log("Victories", 1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
OffsetPoints(-Points, true);
|
||||
|
||||
Log("Defeats", 1, true);
|
||||
}
|
||||
|
||||
UI.Refresh(true);
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
base.OnDispose();
|
||||
|
||||
if (_Grid != null)
|
||||
{
|
||||
_Grid.ForEach(
|
||||
(x, y, t) =>
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
t.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
_Grid.Free(true);
|
||||
_Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class TrapSweeperTile : IDisposable
|
||||
{
|
||||
public virtual int HiddenID => 9026;
|
||||
public virtual int MarkID => 9026;
|
||||
public virtual int ClickID => 9021;
|
||||
public virtual int DisplayID => 9021;
|
||||
|
||||
public virtual int Hue => 0;
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public TrapSweeperEngine Engine { get; private set; }
|
||||
|
||||
public int X { get; private set; }
|
||||
public int Y { get; private set; }
|
||||
|
||||
public int Points { get; set; }
|
||||
|
||||
private bool _Marked;
|
||||
|
||||
public bool Marked
|
||||
{
|
||||
get => _Marked;
|
||||
set
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_Marked && value)
|
||||
{
|
||||
if (Engine.Marked < Engine.Traps)
|
||||
{
|
||||
_Marked = true;
|
||||
OnMarked();
|
||||
}
|
||||
}
|
||||
else if (_Marked && !value)
|
||||
{
|
||||
_Marked = false;
|
||||
OnUnmarked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _Visible;
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _Visible;
|
||||
set
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_Visible && value)
|
||||
{
|
||||
_Visible = true;
|
||||
OnReveal();
|
||||
}
|
||||
else if (_Visible && !value)
|
||||
{
|
||||
_Visible = false;
|
||||
OnHide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TrapSweeperTile(TrapSweeperEngine g, int x, int y)
|
||||
{
|
||||
Engine = g;
|
||||
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
~TrapSweeperTile()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
return !IsDisposed && Engine != null && Engine.Validate();
|
||||
}
|
||||
|
||||
public void Click()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Engine.Mark)
|
||||
{
|
||||
ToggleMark();
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleVisibility();
|
||||
}
|
||||
|
||||
Engine.UI.Refresh(true);
|
||||
}
|
||||
|
||||
protected void ToggleMark()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Visible)
|
||||
{
|
||||
Marked = !Marked;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ToggleVisibility()
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
Marked = false;
|
||||
Visible = !Visible;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
|
||||
OnDispose();
|
||||
|
||||
if (Engine != null)
|
||||
{
|
||||
if (Engine._Grid != null)
|
||||
{
|
||||
Engine._Grid[X, Y] = null;
|
||||
}
|
||||
|
||||
Engine = null;
|
||||
}
|
||||
|
||||
X = Y = -1;
|
||||
}
|
||||
|
||||
protected virtual void OnHide()
|
||||
{
|
||||
--Engine.Visible;
|
||||
|
||||
Engine.OffsetPoints(-Points, true);
|
||||
}
|
||||
|
||||
protected virtual void OnReveal()
|
||||
{
|
||||
++Engine.Visible;
|
||||
|
||||
Engine.OffsetPoints(Points, true);
|
||||
}
|
||||
|
||||
protected virtual void OnMarked()
|
||||
{
|
||||
++Engine.Marked;
|
||||
}
|
||||
|
||||
protected virtual void OnUnmarked()
|
||||
{
|
||||
--Engine.Marked;
|
||||
}
|
||||
|
||||
protected virtual void OnDispose()
|
||||
{
|
||||
if (Engine == null || Engine.IsDisposed)
|
||||
{
|
||||
_Visible = _Marked = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_Visible)
|
||||
{
|
||||
--Engine.Visible;
|
||||
}
|
||||
|
||||
if (_Marked)
|
||||
{
|
||||
--Engine.Marked;
|
||||
}
|
||||
|
||||
_Visible = _Marked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TrapSweeperTileBlank : TrapSweeperTile
|
||||
{
|
||||
private int _Traps = -1;
|
||||
|
||||
public int Traps
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Validate())
|
||||
{
|
||||
Dispose();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_Traps < 0)
|
||||
{
|
||||
_Traps = Engine.FindTiles<TrapSweeperTileTrap>(X - 1, Y - 1, 3, 3).Count();
|
||||
}
|
||||
|
||||
return _Traps;
|
||||
}
|
||||
}
|
||||
|
||||
public override int DisplayID => Traps > 0 ? 2225 + (Traps - 1) : 9021;
|
||||
|
||||
public TrapSweeperTileBlank(TrapSweeperEngine g, int x, int y)
|
||||
: base(g, x, y)
|
||||
{ }
|
||||
|
||||
protected override void OnReveal()
|
||||
{
|
||||
base.OnReveal();
|
||||
|
||||
if (Engine.AllTiles<TrapSweeperTileBlank>().All(t => t.Visible))
|
||||
{
|
||||
Engine.DoEnd(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.IncreasePoints(Points, true);
|
||||
|
||||
if (Traps > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var p = 0.0;
|
||||
|
||||
foreach (var t in Engine.FindTiles<TrapSweeperTileBlank>(X - 1, Y - 1, 3, 3).Where(t => t != this && !t.Visible))
|
||||
{
|
||||
t.Visible = true;
|
||||
p += t.Points;
|
||||
}
|
||||
|
||||
Engine.OffsetPoints(p, true);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TrapSweeperTileTrap : TrapSweeperTile
|
||||
{
|
||||
public override int DisplayID => 9020;
|
||||
public override int Hue => 34;
|
||||
|
||||
public TrapSweeperTileTrap(TrapSweeperEngine g, int x, int y)
|
||||
: base(g, x, y)
|
||||
{ }
|
||||
|
||||
/*protected override void OnMarked()
|
||||
{
|
||||
base.OnMarked();
|
||||
|
||||
if (!Engine.AllTiles<SweeperTileTrap>().All(t => t.Marked))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.Rewards += Engine.AllTiles<SweeperTileBonus>().Count(t => !t.Visible);
|
||||
|
||||
Engine.DoEnd(true);
|
||||
}*/
|
||||
|
||||
protected override void OnReveal()
|
||||
{
|
||||
base.OnReveal();
|
||||
|
||||
Engine.DecreasePoints(Points, true);
|
||||
|
||||
Engine.DoEnd(false);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TrapSweeperTileBonus : TrapSweeperTile
|
||||
{
|
||||
public override int DisplayID => 9027;
|
||||
public override int Hue => 85;
|
||||
|
||||
public TrapSweeperTileBonus(TrapSweeperEngine g, int x, int y)
|
||||
: base(g, x, y)
|
||||
{ }
|
||||
|
||||
protected override void OnReveal()
|
||||
{
|
||||
base.OnReveal();
|
||||
|
||||
++Engine.Rewards;
|
||||
}
|
||||
|
||||
protected override void OnHide()
|
||||
{
|
||||
base.OnHide();
|
||||
|
||||
--Engine.Rewards;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public enum TrapSweeperMode
|
||||
{
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
Random
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public enum TrapSweeperState
|
||||
{
|
||||
Menu,
|
||||
Play,
|
||||
Win,
|
||||
Lose
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public class TrapSweeperUI : GameUI<TrapSweeperEngine>
|
||||
{
|
||||
public TrapSweeperUI(TrapSweeperEngine engine)
|
||||
: base(engine)
|
||||
{
|
||||
HighlightHue = 1258;
|
||||
}
|
||||
|
||||
protected override void CompileLayout(SuperGumpLayout layout)
|
||||
{
|
||||
base.CompileLayout(layout);
|
||||
|
||||
if (Engine == null || Engine.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Engine.State)
|
||||
{
|
||||
case TrapSweeperState.Menu:
|
||||
CompileMenuLayout(layout);
|
||||
break;
|
||||
default:
|
||||
CompilePlayLayout(layout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CompileMenuLayout(SuperGumpLayout layout)
|
||||
{
|
||||
layout.Add(
|
||||
"window/menu/start",
|
||||
() =>
|
||||
{
|
||||
AddImage(354, 256, 4501, HighlightHue);
|
||||
AddButton(351, 254, 4501, 4501, b => Engine.DoPlay());
|
||||
|
||||
AddHtml(
|
||||
140,
|
||||
300,
|
||||
435,
|
||||
40,
|
||||
"START SWEEPING!".WrapUOHtmlBold().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Gold, false),
|
||||
false,
|
||||
false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/menu/option/easy",
|
||||
() =>
|
||||
{
|
||||
var s = false;
|
||||
|
||||
// EASY
|
||||
if (Engine.Mode != TrapSweeperMode.Easy)
|
||||
{
|
||||
AddImage(155, 330, 1417);
|
||||
AddButton(164, 339, 5575, 5576, b => Engine.DoMode(TrapSweeperMode.Easy));
|
||||
AddButton(145, 420, 5403, 5403, b => Engine.DoMode(TrapSweeperMode.Easy));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(155, 330, 1417, HighlightHue);
|
||||
AddImage(164, 339, 5576);
|
||||
|
||||
s = true;
|
||||
}
|
||||
|
||||
var text = "ROOKIE";
|
||||
|
||||
text = s ? text.WrapUOHtmlTag("U") : text;
|
||||
text = text.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Green, false);
|
||||
|
||||
AddBackground(145, 415, 100, 30, 9350);
|
||||
AddHtml(145, 420, 100, 40, text, false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/menu/option/normal",
|
||||
() =>
|
||||
{
|
||||
var s = false;
|
||||
|
||||
// NORMAL
|
||||
if (Engine.Mode != TrapSweeperMode.Normal)
|
||||
{
|
||||
AddImage(262, 330, 1417);
|
||||
AddButton(271, 339, 5587, 5588, b => Engine.DoMode(TrapSweeperMode.Normal));
|
||||
AddButton(252, 420, 5403, 5403, b => Engine.DoMode(TrapSweeperMode.Normal));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(262, 330, 1417, HighlightHue);
|
||||
AddImage(271, 339, 5588);
|
||||
|
||||
s = true;
|
||||
}
|
||||
|
||||
var text = "GUARD";
|
||||
|
||||
text = s ? text.WrapUOHtmlTag("U") : text;
|
||||
text = text.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Yellow, false);
|
||||
|
||||
AddBackground(252, 415, 100, 30, 9350);
|
||||
AddHtml(252, 420, 100, 40, text, false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/menu/option/hard",
|
||||
() =>
|
||||
{
|
||||
var s = false;
|
||||
|
||||
// HARD
|
||||
if (Engine.Mode != TrapSweeperMode.Hard)
|
||||
{
|
||||
AddImage(368, 330, 1417);
|
||||
AddButton(377, 339, 5547, 5548, b => Engine.DoMode(TrapSweeperMode.Hard));
|
||||
AddButton(358, 420, 5403, 5403, b => Engine.DoMode(TrapSweeperMode.Hard));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(368, 330, 1417, HighlightHue);
|
||||
AddImage(377, 339, 5548);
|
||||
|
||||
s = true;
|
||||
}
|
||||
|
||||
var text = "KNIGHT";
|
||||
|
||||
text = s ? text.WrapUOHtmlTag("U") : text;
|
||||
text = text.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Red, false);
|
||||
|
||||
AddBackground(358, 415, 100, 30, 9350);
|
||||
AddHtml(358, 420, 100, 40, text, false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/menu/option/random",
|
||||
() =>
|
||||
{
|
||||
var s = false;
|
||||
|
||||
// RANDOM
|
||||
if (Engine.Mode != TrapSweeperMode.Random)
|
||||
{
|
||||
AddImage(475, 330, 1417);
|
||||
AddButton(484, 339, 5583, 5584, b => Engine.DoMode(TrapSweeperMode.Random));
|
||||
AddButton(465, 420, 5403, 5403, b => Engine.DoMode(TrapSweeperMode.Random));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(475, 330, 1417, HighlightHue);
|
||||
AddImage(484, 339, 5584);
|
||||
|
||||
s = true;
|
||||
}
|
||||
|
||||
var text = "RANDOM";
|
||||
|
||||
text = s ? text.WrapUOHtmlTag("U") : text;
|
||||
text = text.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Blue, false);
|
||||
|
||||
AddBackground(465, 415, 100, 30, 9350);
|
||||
AddHtml(465, 420, 100, 40, text, false, false);
|
||||
});
|
||||
}
|
||||
|
||||
private void CompilePlayLayout(SuperGumpLayout layout)
|
||||
{
|
||||
layout.Add(
|
||||
"window/play",
|
||||
() =>
|
||||
{
|
||||
AddBackground(133, 100, 450, 350, 9200);
|
||||
AddBackground(142, 130, 435, 315, 9300);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/play/info",
|
||||
() =>
|
||||
{
|
||||
if (Engine.State == TrapSweeperState.Lose)
|
||||
{
|
||||
AddImage(325, 55, 7034, 34);
|
||||
AddImage(270, 32, 50562);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(325, 55, 7034);
|
||||
}
|
||||
|
||||
AddImage(230, 60, 30082, HighlightHue);
|
||||
AddImage(315, 45, 30061, HighlightHue);
|
||||
AddImage(390, 60, 30080, HighlightHue);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/play/info/traps",
|
||||
() =>
|
||||
{
|
||||
AddImage(260, 50, 20999);
|
||||
AddBackground(185, 50, 75, 44, 9300);
|
||||
|
||||
var count = Engine.Traps - Engine.Marked;
|
||||
|
||||
AddHtml(185, 65, 75, 40, count.ToString("#,0").WrapUOHtmlCenter(), false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/play/info/time",
|
||||
() =>
|
||||
{
|
||||
AddImage(415, 50, 23000);
|
||||
AddBackground(460, 50, 75, 44, 9300);
|
||||
|
||||
TimeSpan time;
|
||||
|
||||
switch (Engine.State)
|
||||
{
|
||||
case TrapSweeperState.Lose:
|
||||
time = Engine.Ended - Engine.Started;
|
||||
break;
|
||||
default:
|
||||
time = DateTime.UtcNow - Engine.Started;
|
||||
break;
|
||||
}
|
||||
|
||||
AddHtml(460, 65, 75, 40, time.ToSimpleString("h:m:s").WrapUOHtmlCenter(), false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/play/game/grid",
|
||||
() =>
|
||||
{
|
||||
var w = Engine.Width * 19;
|
||||
var h = Engine.Height * 20;
|
||||
|
||||
var xo = 148 + ((418 - w) / 2);
|
||||
var yo = 137 + ((300 - h) / 2);
|
||||
|
||||
foreach (var t in Engine.AllTiles())
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Engine.State == TrapSweeperState.Play && !t.Visible)
|
||||
{
|
||||
AddButton(xo + (t.X * 19), yo + (t.Y * 20), t.Marked ? t.MarkID : t.HiddenID, t.ClickID, b => t.Click());
|
||||
}
|
||||
else
|
||||
{
|
||||
AddImage(xo + (t.X * 19), yo + (t.Y * 20), t.DisplayID, t.Hue);
|
||||
}
|
||||
|
||||
AddImage(xo + (t.X * 19), yo + (t.Y * 20), 9028, t.Marked ? HighlightHue : 0);
|
||||
}
|
||||
});
|
||||
|
||||
switch (Engine.State)
|
||||
{
|
||||
case TrapSweeperState.Play:
|
||||
{
|
||||
layout.Add(
|
||||
"window/play/mark",
|
||||
() =>
|
||||
{
|
||||
var text = "MARK";
|
||||
|
||||
text = text.WrapUOHtmlBold().WrapUOHtmlCenter().WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
|
||||
AddHtml(45, 355, 80, 40, text, false, false);
|
||||
AddButton(50, 380, 7031, 7031, b => Engine.DoMark());
|
||||
|
||||
if (Engine.Mark)
|
||||
{
|
||||
AddImage(50, 380, 7031, HighlightHue);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case TrapSweeperState.Win:
|
||||
{
|
||||
layout.Add("window/play/results", () => AddAlphaRegion(142, 130, 435, 315));
|
||||
|
||||
layout.Add(
|
||||
"window/play/collect",
|
||||
() =>
|
||||
{
|
||||
var text = "COLLECT";
|
||||
|
||||
text = text.WrapUOHtmlBold().WrapUOHtmlCenter().WrapUOHtmlColor(Color.LawnGreen, false);
|
||||
|
||||
AddHtml(45, 355, 80, 40, text, false, false);
|
||||
AddButton(50, 380, 7012, 7012, b => Engine.DoCollect());
|
||||
AddImage(50, 380, 7012, 85);
|
||||
});
|
||||
}
|
||||
break;
|
||||
case TrapSweeperState.Lose:
|
||||
{
|
||||
layout.Add(
|
||||
"window/play/menu",
|
||||
() =>
|
||||
{
|
||||
var text = "RESURRECT";
|
||||
|
||||
text = text.WrapUOHtmlBold().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Orange, false);
|
||||
|
||||
AddHtml(45, 355, 80, 40, text, false, false);
|
||||
AddButton(50, 380, 7007, 7007, b => Engine.DoMenu());
|
||||
AddImage(50, 380, 7007, HighlightHue);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/play/blood",
|
||||
() =>
|
||||
{
|
||||
AddItem(65, 73, 7572);
|
||||
AddItem(273, 59, 7574);
|
||||
AddItem(348, 58, 4655);
|
||||
AddItem(599, 37, 7573);
|
||||
AddItem(585, 310, 4652);
|
||||
AddItem(603, 327, 4653);
|
||||
AddItem(594, 450, 4650);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnQuit()
|
||||
{
|
||||
base.OnQuit();
|
||||
|
||||
if (Engine.State == TrapSweeperState.Win)
|
||||
{
|
||||
Engine.DoCollect();
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.DoQuit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
178
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Games/UI/GameUI.cs
Normal file
178
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Games/UI/GameUI.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public abstract class GameUI<TEngine> : SuperGump, IGameUI
|
||||
where TEngine : class, IGameEngine
|
||||
{
|
||||
public bool IsDisposing { get; private set; }
|
||||
|
||||
public TEngine Engine { get; private set; }
|
||||
|
||||
public GameUI(TEngine engine)
|
||||
: base(engine.User)
|
||||
{
|
||||
Engine = engine;
|
||||
|
||||
RandomButtonID = true;
|
||||
ForceRecompile = true;
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
return !IsDisposed && !IsDisposing && Engine != null && !Engine.IsDisposed && User != null && !User.Deleted;
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
if (IsDisposed || IsDisposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDisposing = true;
|
||||
|
||||
base.OnDispose();
|
||||
}
|
||||
|
||||
protected override void OnDisposed()
|
||||
{
|
||||
base.OnDisposed();
|
||||
|
||||
IsDisposing = false;
|
||||
}
|
||||
|
||||
protected override void OnClosed(bool all)
|
||||
{
|
||||
base.OnClosed(all);
|
||||
|
||||
if (!all)
|
||||
{
|
||||
OnQuit();
|
||||
}
|
||||
else
|
||||
{
|
||||
Engine.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CompileLayout(SuperGumpLayout layout)
|
||||
{
|
||||
base.CompileLayout(layout);
|
||||
|
||||
layout.Add(
|
||||
"window",
|
||||
() =>
|
||||
{
|
||||
AddBackground(0, 0, 650, 490, 2620);
|
||||
|
||||
AddImage(5, 5, 9001);
|
||||
AddImage(5, 5, 9002, 901);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/title",
|
||||
() =>
|
||||
{
|
||||
var title = GetTitle();
|
||||
|
||||
AddImage(10, 10, 2440, 901);
|
||||
AddHtml(10, 12, 166, 40, title, false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"window/score",
|
||||
() =>
|
||||
{
|
||||
var points = GetPoints();
|
||||
|
||||
AddImage(180, 10, 2440, 901);
|
||||
AddHtml(180, 12, 166, 40, points, false, false);
|
||||
});
|
||||
|
||||
layout.Add("window/quit", () => { AddButton(560, 5, 5514, 5515, Close); });
|
||||
}
|
||||
|
||||
protected virtual void OnQuit()
|
||||
{ }
|
||||
|
||||
public virtual string GetPoints()
|
||||
{
|
||||
if (Engine == null)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var p = String.Format("Points: {0:#,0.##}", Engine.Points);
|
||||
|
||||
var t = new StringBuilder();
|
||||
|
||||
var c1 = Color.Gold;
|
||||
var c2 = Color.PaleGoldenrod;
|
||||
|
||||
for (var i = 0; i < p.Length; i++)
|
||||
{
|
||||
if (!Char.IsWhiteSpace(p, i))
|
||||
{
|
||||
t.Append(p[i].ToString().WrapUOHtmlColor(c1.Interpolate(c2, i / (p.Length - 1.0)), false));
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Append(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return t.ToString().WrapUOHtmlBig().WrapUOHtmlCenter();
|
||||
}
|
||||
|
||||
public virtual string GetTitle()
|
||||
{
|
||||
if (Engine == null || Engine.Game == null)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var n = Engine.Game.Name.Trim();
|
||||
|
||||
var t = new StringBuilder();
|
||||
|
||||
var c1 = Color.Gold;
|
||||
var c2 = Color.PaleGoldenrod;
|
||||
|
||||
for (var i = 0; i < n.Length; i++)
|
||||
{
|
||||
if (!Char.IsWhiteSpace(n, i))
|
||||
{
|
||||
t.Append(n[i].ToString().WrapUOHtmlColor(c1.Interpolate(c2, i / (n.Length - 1.0)), false));
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Append(n[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return t.ToString().WrapUOHtmlBig().WrapUOHtmlCenter();
|
||||
}
|
||||
|
||||
#region Explicit Impl
|
||||
IGameEngine IGameUI.Engine => Engine;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public interface IGame
|
||||
{
|
||||
Type EngineType { get; }
|
||||
|
||||
IconDefinition Icon { get; }
|
||||
|
||||
string Name { get; }
|
||||
string Desc { get; }
|
||||
string Help { get; }
|
||||
|
||||
bool Enabled { get; set; }
|
||||
|
||||
IEnumerable<IGameEngine> Sessions { get; }
|
||||
|
||||
int SessionCount { get; }
|
||||
|
||||
IGameEngine this[Mobile user] { get; set; }
|
||||
|
||||
GameStatistics Statistics { get; }
|
||||
|
||||
void Enable();
|
||||
void Disable();
|
||||
|
||||
bool Validate(Mobile user);
|
||||
bool Open(Mobile user);
|
||||
void Close(Mobile user);
|
||||
void Reset(Mobile user);
|
||||
|
||||
void Log(string context, double value, bool offset);
|
||||
void LogIncrease(string context, double value);
|
||||
void LogDecrease(string context, double value);
|
||||
|
||||
void Serialize(GenericWriter writer);
|
||||
void Deserialize(GenericReader reader);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public interface IGameEngine : IDisposable
|
||||
{
|
||||
Type UIType { get; }
|
||||
|
||||
bool IsDisposed { get; }
|
||||
bool IsDisposing { get; }
|
||||
|
||||
Mobile User { get; }
|
||||
|
||||
IGame Game { get; }
|
||||
IGameUI UI { get; }
|
||||
|
||||
ArcadeProfile Profile { get; }
|
||||
|
||||
GameStatistics Statistics { get; }
|
||||
|
||||
double Points { get; }
|
||||
|
||||
bool Validate();
|
||||
|
||||
bool Open();
|
||||
void Close();
|
||||
void Reset();
|
||||
|
||||
void LogStatistics();
|
||||
|
||||
void Log(string context, double value, bool offset);
|
||||
void LogIncrease(string context, double value);
|
||||
void LogDecrease(string context, double value);
|
||||
|
||||
void OffsetPoints(double value, bool log);
|
||||
void IncreasePoints(double value, bool log);
|
||||
void DecreasePoints(double value, bool log);
|
||||
|
||||
void Serialize(GenericWriter writer);
|
||||
void Deserialize(GenericReader reader);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public interface IGameUI : IDisposable
|
||||
{
|
||||
bool IsDisposed { get; }
|
||||
bool IsDisposing { get; }
|
||||
|
||||
IGameEngine Engine { get; }
|
||||
|
||||
Mobile User { get; }
|
||||
|
||||
bool Validate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public sealed class ArcadeProfile : PropertyObject, IEnumerable<KeyValuePair<string, GameStatistics>>
|
||||
{
|
||||
private Dictionary<string, GameStatistics> _Statistics = new Dictionary<string, GameStatistics>();
|
||||
|
||||
public GameStatistics this[string key]
|
||||
{
|
||||
get => _Statistics.GetValue(key) ?? (_Statistics[key] = new GameStatistics());
|
||||
set => _Statistics[key] = value;
|
||||
}
|
||||
|
||||
public Dictionary<string, GameStatistics>.KeyCollection Categories => _Statistics.Keys;
|
||||
|
||||
public Mobile Owner { get; private set; }
|
||||
|
||||
public int Credits { get; set; }
|
||||
|
||||
public ArcadeProfile(Mobile owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
|
||||
public ArcadeProfile(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
foreach (var v in _Statistics.Values)
|
||||
{
|
||||
v.Clear();
|
||||
}
|
||||
|
||||
_Statistics.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
foreach (var v in _Statistics.Values)
|
||||
{
|
||||
v.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(IGame g, string context, double value, bool offset)
|
||||
{
|
||||
if (offset)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
LogIncrease(g, context, value);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
LogDecrease(g, context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this[g.Name][context] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void LogIncrease(IGame g, string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
this[g.Name][context] += value;
|
||||
}
|
||||
|
||||
public void LogDecrease(IGame g, string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
this[g.Name][context] -= value;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, GameStatistics>> GetEnumerator()
|
||||
{
|
||||
return _Statistics.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
writer.Write(Owner);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Credits);
|
||||
|
||||
writer.WriteBlockDictionary(
|
||||
_Statistics,
|
||||
(w, k, v) =>
|
||||
{
|
||||
w.Write(k);
|
||||
v.Serialize(w);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
Owner = reader.ReadMobile();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Credits = reader.ReadInt();
|
||||
|
||||
_Statistics = reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var k = r.ReadString();
|
||||
var v = new GameStatistics(r);
|
||||
|
||||
return new KeyValuePair<string, GameStatistics>(k, v);
|
||||
},
|
||||
_Statistics);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
343
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Objects/Game.cs
Normal file
343
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/Objects/Game.cs
Normal file
@@ -0,0 +1,343 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public abstract class Game<TEngine> : IGame, IEnumerable<TEngine>
|
||||
where TEngine : class, IGameEngine
|
||||
{
|
||||
private readonly Type _EngineType = typeof(TEngine);
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public Type EngineType => _EngineType;
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public abstract IconDefinition Icon { get; }
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public abstract string Name { get; }
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public abstract string Desc { get; }
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public abstract string Help { get; }
|
||||
|
||||
private bool _Enabled;
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public bool Enabled
|
||||
{
|
||||
get => _Enabled;
|
||||
set
|
||||
{
|
||||
if (_Enabled && !value)
|
||||
{
|
||||
_Enabled = false;
|
||||
|
||||
OnDisabled();
|
||||
Flush();
|
||||
}
|
||||
else if (!_Enabled && value)
|
||||
{
|
||||
_Enabled = true;
|
||||
|
||||
OnEnabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<TEngine> _Sessions;
|
||||
|
||||
public IEnumerable<TEngine> Sessions
|
||||
{
|
||||
get
|
||||
{
|
||||
var c = _Sessions.Count;
|
||||
|
||||
while (--c >= 0)
|
||||
{
|
||||
if (!_Sessions.InBounds(c))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var e = _Sessions[c];
|
||||
|
||||
if (e != null && !e.IsDisposed && !e.IsDisposing)
|
||||
{
|
||||
yield return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public int SessionCount => _Sessions.Count;
|
||||
|
||||
public TEngine this[Mobile user]
|
||||
{
|
||||
get => _Sessions.Find(e => e != null && e.User == user);
|
||||
private set
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var e = this[user];
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (e == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
e.Dispose();
|
||||
|
||||
_Sessions.Remove(e);
|
||||
}
|
||||
|
||||
_Sessions.Update(value);
|
||||
}
|
||||
else if (e != null)
|
||||
{
|
||||
e.Dispose();
|
||||
|
||||
_Sessions.Remove(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(Arcade.Access)]
|
||||
public GameStatistics Statistics { get; private set; }
|
||||
|
||||
public Game()
|
||||
{
|
||||
_Sessions = new List<TEngine>();
|
||||
|
||||
Statistics = new GameStatistics();
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
foreach (var e in Sessions)
|
||||
{
|
||||
e.Dispose();
|
||||
}
|
||||
|
||||
_Sessions.Clear();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
protected virtual void OnEnabled()
|
||||
{ }
|
||||
|
||||
protected virtual void OnDisabled()
|
||||
{ }
|
||||
|
||||
protected TEngine EnsureSession(Mobile user)
|
||||
{
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var e = this[user];
|
||||
|
||||
if (e == null || !e.IsDisposing || e.IsDisposed)
|
||||
{
|
||||
if (user.Deleted || !user.IsOnline())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
this[user] = e = EngineType.CreateInstanceSafe<TEngine>(this, user);
|
||||
}
|
||||
else if (user.Deleted || !user.IsOnline())
|
||||
{
|
||||
this[user] = null;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
public bool Validate(Mobile user)
|
||||
{
|
||||
var e = EnsureSession(user);
|
||||
|
||||
return e != null && e.Validate();
|
||||
}
|
||||
|
||||
public bool Open(Mobile user)
|
||||
{
|
||||
var e = EnsureSession(user);
|
||||
|
||||
if (e == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!e.Open() || !e.Validate())
|
||||
{
|
||||
e.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Close(Mobile user)
|
||||
{
|
||||
this[user] = null;
|
||||
}
|
||||
|
||||
public void Reset(Mobile user)
|
||||
{
|
||||
var e = EnsureSession(user);
|
||||
|
||||
if (e != null && e.Validate())
|
||||
{
|
||||
e.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(string context, double value, bool offset)
|
||||
{
|
||||
if (offset)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
LogIncrease(context, value);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
LogDecrease(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Statistics[context] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void LogIncrease(string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Statistics[context] += value;
|
||||
}
|
||||
|
||||
public void LogDecrease(string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Statistics[context] -= value;
|
||||
}
|
||||
|
||||
public IEnumerator<TEngine> GetEnumerator()
|
||||
{
|
||||
return _Sessions.GetEnumerator();
|
||||
}
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.Write(_Enabled);
|
||||
|
||||
Statistics.Serialize(writer);
|
||||
|
||||
writer.WriteBlockList(
|
||||
_Sessions,
|
||||
(w, e) =>
|
||||
{
|
||||
w.Write(e.User);
|
||||
e.Serialize(w);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
_Enabled = reader.ReadBool();
|
||||
|
||||
Statistics.Deserialize(reader);
|
||||
|
||||
_Sessions = reader.ReadBlockList(
|
||||
r =>
|
||||
{
|
||||
var e = EnsureSession(r.ReadMobile());
|
||||
|
||||
e.Deserialize(r);
|
||||
|
||||
return e;
|
||||
},
|
||||
_Sessions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#region Explicit Impl
|
||||
IEnumerable<IGameEngine> IGame.Sessions => Sessions;
|
||||
|
||||
IGameEngine IGame.this[Mobile user]
|
||||
{
|
||||
get => this[user];
|
||||
set
|
||||
{
|
||||
if (value == null || value is TEngine)
|
||||
{
|
||||
this[user] = (TEngine)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public abstract class GameEngine<TGame, TGump> : IGameEngine
|
||||
where TGame : class, IGame
|
||||
where TGump : SuperGump, IGameUI
|
||||
{
|
||||
private readonly Type _UIType = typeof(TGump);
|
||||
|
||||
public Type UIType => _UIType;
|
||||
|
||||
public bool IsDisposing { get; private set; }
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public Mobile User { get; private set; }
|
||||
|
||||
public TGame Game { get; private set; }
|
||||
public TGump UI { get; private set; }
|
||||
|
||||
public ArcadeProfile Profile => Arcade.EnsureProfile(User);
|
||||
|
||||
public GameStatistics Statistics { get; private set; }
|
||||
|
||||
public double PointsTotal
|
||||
{
|
||||
get => Statistics["Points Total"];
|
||||
private set => Statistics["Points Total"] = value;
|
||||
}
|
||||
|
||||
public double PointsGained
|
||||
{
|
||||
get => Statistics["Points Gained"];
|
||||
private set => Statistics["Points Gained"] = value;
|
||||
}
|
||||
|
||||
public double PointsLost
|
||||
{
|
||||
get => Statistics["Points Lost"];
|
||||
private set => Statistics["Points Lost"] = value;
|
||||
}
|
||||
|
||||
public double Points { get; private set; }
|
||||
|
||||
public GameEngine(TGame game, Mobile user)
|
||||
{
|
||||
Game = game;
|
||||
User = user;
|
||||
|
||||
Statistics = new GameStatistics();
|
||||
}
|
||||
|
||||
~GameEngine()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected bool EnsureUI()
|
||||
{
|
||||
if (IsDisposed || IsDisposing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (User == null || User.Deleted)
|
||||
{
|
||||
if (UI != null)
|
||||
{
|
||||
UI.Close(true);
|
||||
UI = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UI != null && UI.User != User)
|
||||
{
|
||||
UI.Close(true);
|
||||
UI = null;
|
||||
}
|
||||
|
||||
if (UI == null || UI.IsDisposed || UI.IsDisposing)
|
||||
{
|
||||
UI = UIType.CreateInstanceSafe<TGump>(this);
|
||||
}
|
||||
|
||||
return UI != null && UI.Validate();
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
{
|
||||
return !IsDisposed && !IsDisposing && UI != null && UI.Validate();
|
||||
}
|
||||
|
||||
public virtual bool Open()
|
||||
{
|
||||
if (!EnsureUI() || !Validate())
|
||||
{
|
||||
Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
UI.Refresh(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Close()
|
||||
{
|
||||
if (UI != null && !UI.IsDisposed)
|
||||
{
|
||||
UI.Close(true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
Points = 0;
|
||||
|
||||
Statistics.Reset();
|
||||
}
|
||||
|
||||
public void LogStatistics()
|
||||
{
|
||||
var p = Profile;
|
||||
|
||||
foreach (var kv in Statistics)
|
||||
{
|
||||
Game.Log(kv.Key, kv.Value, true);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.Log(Game, kv.Key, kv.Value, true);
|
||||
}
|
||||
}
|
||||
|
||||
Statistics.Reset();
|
||||
}
|
||||
|
||||
public void Log(string context, double value, bool offset)
|
||||
{
|
||||
if (offset)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
LogIncrease(context, value);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
LogDecrease(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Statistics[context] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void LogIncrease(string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Statistics[context] += value;
|
||||
}
|
||||
|
||||
public void LogDecrease(string context, double value)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Statistics[context] -= value;
|
||||
}
|
||||
|
||||
public void OffsetPoints(double value, bool log)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
IncreasePoints(value, log);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
DecreasePoints(value, log);
|
||||
}
|
||||
}
|
||||
|
||||
public void IncreasePoints(double value, bool log)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Points += value;
|
||||
PointsTotal += value;
|
||||
|
||||
if (log)
|
||||
{
|
||||
PointsGained += value;
|
||||
}
|
||||
}
|
||||
|
||||
public void DecreasePoints(double value, bool log)
|
||||
{
|
||||
value = Math.Abs(value);
|
||||
|
||||
Points -= value;
|
||||
PointsTotal -= value;
|
||||
|
||||
if (log)
|
||||
{
|
||||
PointsLost += value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed || IsDisposing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDisposing = true;
|
||||
|
||||
LogStatistics();
|
||||
|
||||
Close();
|
||||
|
||||
if (UI == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (UI.IsOpen || UI.Hidden)
|
||||
{
|
||||
UI.Close(true);
|
||||
}
|
||||
|
||||
UI = null;
|
||||
|
||||
OnDispose();
|
||||
|
||||
if (Game != null && User != null)
|
||||
{
|
||||
Game[User] = null;
|
||||
}
|
||||
|
||||
Statistics.Clear();
|
||||
Statistics = null;
|
||||
|
||||
Game = null;
|
||||
User = null;
|
||||
|
||||
IsDisposed = true;
|
||||
|
||||
OnDisposed();
|
||||
|
||||
IsDisposing = false;
|
||||
}
|
||||
|
||||
protected virtual void OnDispose()
|
||||
{ }
|
||||
|
||||
protected virtual void OnDisposed()
|
||||
{ }
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Points);
|
||||
|
||||
Statistics.Serialize(writer);
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
reader.GetVersion();
|
||||
|
||||
Points = reader.ReadDouble();
|
||||
|
||||
Statistics.Deserialize(reader);
|
||||
}
|
||||
|
||||
#region Explicit Impl
|
||||
IGame IGameEngine.Game => Game;
|
||||
IGameUI IGameEngine.UI => UI;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public sealed class GameStatistics : PropertyObject, IEnumerable<KeyValuePair<string, double>>
|
||||
{
|
||||
private readonly Dictionary<string, double> _Statistics = new Dictionary<string, double>();
|
||||
|
||||
public double this[string key] { get => _Statistics.GetValue(key); set => _Statistics[key] = value; }
|
||||
|
||||
public Dictionary<string, double>.KeyCollection Entries => _Statistics.Keys;
|
||||
|
||||
public GameStatistics()
|
||||
{ }
|
||||
|
||||
public GameStatistics(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_Statistics.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
_Statistics.Keys.ForEach(k => _Statistics[k] = 0);
|
||||
}
|
||||
|
||||
public bool Remove(string key)
|
||||
{
|
||||
return key != null && _Statistics.Remove(key);
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<string, double>> GetEnumerator()
|
||||
{
|
||||
return _Statistics.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.WriteDictionary(
|
||||
_Statistics,
|
||||
(w, k, v) =>
|
||||
{
|
||||
w.Write(k);
|
||||
w.Write(v);
|
||||
});
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
reader.ReadDictionary(
|
||||
r =>
|
||||
{
|
||||
var k = r.ReadString();
|
||||
var v = r.ReadDouble();
|
||||
|
||||
return new KeyValuePair<string, double>(k, v);
|
||||
},
|
||||
_Statistics);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public sealed class ArcadeOptions : CoreModuleOptions
|
||||
{
|
||||
public ArcadeOptions()
|
||||
: base(typeof(Arcade))
|
||||
{ }
|
||||
|
||||
/*
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
}
|
||||
*/
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
493
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/UI/ArcadeUI.cs
Normal file
493
Scripts/SubSystem/VitaNex/Core/Modules/Arcade/UI/ArcadeUI.cs
Normal file
@@ -0,0 +1,493 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.Games
|
||||
{
|
||||
public sealed class ArcadeUI : SuperGumpList<IGame>
|
||||
{
|
||||
public ArcadeProfile Profile { get; private set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
|
||||
public int Margin { get; set; }
|
||||
public int Padding { get; set; }
|
||||
|
||||
public int MenuSize { get; set; }
|
||||
public int IconSize { get; set; }
|
||||
|
||||
public int Rows { get; private set; }
|
||||
public int Cols { get; private set; }
|
||||
|
||||
public IGame SelectedGame { get; private set; }
|
||||
|
||||
public ArcadeUI(Mobile user)
|
||||
: base(user)
|
||||
{
|
||||
Profile = Arcade.EnsureProfile(User);
|
||||
|
||||
Sorted = true;
|
||||
|
||||
EnsureDefaults();
|
||||
}
|
||||
|
||||
public void EnsureDefaults()
|
||||
{
|
||||
Width = 800;
|
||||
Height = 600;
|
||||
|
||||
Margin = 30;
|
||||
Padding = 15;
|
||||
|
||||
MenuSize = 250;
|
||||
IconSize = 120;
|
||||
|
||||
InvalidateGrid();
|
||||
}
|
||||
|
||||
public void InvalidateGrid()
|
||||
{
|
||||
Width = Math.Max(300, Math.Min(1024, Width));
|
||||
Height = Math.Max(300, Math.Min(768, Height));
|
||||
|
||||
MenuSize = Math.Max(200, Math.Min(400, MenuSize));
|
||||
IconSize = Math.Max(100, Math.Min(250, IconSize));
|
||||
|
||||
Rows = (int)Math.Floor(((Width - MenuSize) - (Padding * 2)) / (double)IconSize);
|
||||
Cols = (int)Math.Floor((Height - (Padding * 2)) / (double)IconSize);
|
||||
|
||||
EntriesPerPage = Rows * Cols;
|
||||
}
|
||||
|
||||
protected override void Compile()
|
||||
{
|
||||
if (Profile == null || Profile.Owner != User)
|
||||
{
|
||||
Profile = Arcade.EnsureProfile(User);
|
||||
}
|
||||
|
||||
InvalidateGrid();
|
||||
|
||||
base.Compile();
|
||||
}
|
||||
|
||||
protected override void CompileList(List<IGame> list)
|
||||
{
|
||||
list.Clear();
|
||||
list.AddRange(Arcade.Games.Values);
|
||||
|
||||
base.CompileList(list);
|
||||
}
|
||||
|
||||
public override int SortCompare(IGame a, IGame b)
|
||||
{
|
||||
var res = 0;
|
||||
|
||||
if (a.CompareNull(b, ref res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return Insensitive.Compare(a.Name, b.Name);
|
||||
}
|
||||
|
||||
protected override void CompileLayout(SuperGumpLayout layout)
|
||||
{
|
||||
base.CompileLayout(layout);
|
||||
|
||||
layout.Add(
|
||||
"body",
|
||||
() =>
|
||||
{
|
||||
AddBackground(0, 0, Width + (Margin * 2), Height + (Margin * 2), 2600);
|
||||
AddBackground(Margin, Margin, MenuSize, Height, 9250);
|
||||
AddBackground(Margin + MenuSize, Margin, Width - MenuSize, Height, 9250);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"title",
|
||||
() =>
|
||||
{
|
||||
var x = Margin + MenuSize + Padding;
|
||||
var y = Margin + Padding;
|
||||
var w = Width - (MenuSize + (Padding * 2));
|
||||
|
||||
var title = "Games Arcade";
|
||||
|
||||
title = title.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
AddHtml(x, y, w, 40, title, false, false);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"menu",
|
||||
() =>
|
||||
{
|
||||
var x = Margin + Padding;
|
||||
var y = Margin + Padding;
|
||||
var w = MenuSize - (Padding * 2);
|
||||
var h = Height - (Padding * 2);
|
||||
|
||||
CompileMenuLayout(x, y, w, h);
|
||||
});
|
||||
|
||||
var games = GetListRange();
|
||||
|
||||
if (games != null)
|
||||
{
|
||||
var width = Cols * IconSize;
|
||||
var height = Rows * IconSize;
|
||||
|
||||
var initX = Margin + MenuSize;
|
||||
var initY = Margin + Padding;
|
||||
|
||||
if (width < Width - MenuSize)
|
||||
{
|
||||
initX += ((Width - MenuSize) - width) / 2;
|
||||
}
|
||||
|
||||
if (height < Height)
|
||||
{
|
||||
initY += (Height - height) / 2;
|
||||
}
|
||||
|
||||
var xOffset = initX;
|
||||
var yOffset = initY;
|
||||
|
||||
var index = 0;
|
||||
|
||||
var col = 0;
|
||||
var row = 0;
|
||||
|
||||
foreach (var game in games.Values)
|
||||
{
|
||||
CompileGameLayout(layout, index++, xOffset, yOffset, game);
|
||||
|
||||
xOffset += IconSize;
|
||||
|
||||
if (++col % Cols == 0)
|
||||
{
|
||||
xOffset = initX;
|
||||
yOffset += IconSize;
|
||||
|
||||
if (++row % Rows == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (col < Cols || row < Rows)
|
||||
{
|
||||
while (index < EntriesPerPage)
|
||||
{
|
||||
CompileGameLayout(layout, index++, xOffset, yOffset, null);
|
||||
|
||||
xOffset += IconSize;
|
||||
|
||||
if (++col % Cols == 0)
|
||||
{
|
||||
xOffset = initX;
|
||||
yOffset += IconSize;
|
||||
|
||||
if (++row % Rows == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CompileGameLayout(SuperGumpLayout layout, int index, int x, int y, IGame game)
|
||||
{
|
||||
layout.Add("games/" + index, () => CompileGameLayout(x, y, IconSize, IconSize, game));
|
||||
}
|
||||
|
||||
private void CompileGameLayout(int x, int y, int w, int h, IGame game)
|
||||
{
|
||||
if (game != null)
|
||||
{
|
||||
AddTileButton(x + Margin, y + Margin, w - (Margin * 2), h - (Margin * 2), b => SelectGame(game));
|
||||
}
|
||||
|
||||
AddBackground(x, y, w, h, 2600);
|
||||
|
||||
if (game == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
y += Margin;
|
||||
h -= Margin;
|
||||
|
||||
if (game.Icon != null && !game.Icon.IsEmpty)
|
||||
{
|
||||
var s = game.Icon.Size;
|
||||
|
||||
if (game.Enabled)
|
||||
{
|
||||
game.Icon.AddToGump(this, x + ((w - s.Width) / 2), y);
|
||||
}
|
||||
else
|
||||
{
|
||||
game.Icon.AddToGump(this, x + ((w - s.Width) / 2), y, 900);
|
||||
}
|
||||
|
||||
y += s.Height + 5;
|
||||
h -= s.Height + 5;
|
||||
}
|
||||
|
||||
var text = game.Name.WrapUOHtmlBig().WrapUOHtmlCenter().WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
AddHtml(x, y, w, h, text, false, false);
|
||||
}
|
||||
|
||||
private void CompileMenuLayout(int x, int y, int w, int h)
|
||||
{
|
||||
if (SelectedGame == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddHtml(x, y, w, h - 95, GetDescription(SelectedGame), false, false);
|
||||
|
||||
y += h - 85;
|
||||
|
||||
var help = "Help";
|
||||
|
||||
help = help.WrapUOHtmlCenter();
|
||||
|
||||
if (SupportsUltimaStore)
|
||||
{
|
||||
help = help.WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
AddButton(x + ((w - 126) / 2), y, 40019, 40029, b => SelectHelp(SelectedGame));
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, help, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
help = help.WrapUOHtmlBig();
|
||||
|
||||
AddHtmlButton(x + ((w - 126) / 2), y, 126, 25, b => SelectHelp(SelectedGame), help, Color.White, Color.Empty);
|
||||
}
|
||||
|
||||
y += 25;
|
||||
|
||||
var play = "Play";
|
||||
|
||||
play = play.WrapUOHtmlCenter();
|
||||
|
||||
if (SupportsUltimaStore)
|
||||
{
|
||||
if (SelectedGame.Enabled || User.AccessLevel >= Arcade.Access)
|
||||
{
|
||||
play = play.WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
AddButton(x + ((w - 126) / 2), y, 40019, 40029, b => PlayGame(SelectedGame));
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, play, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
play = play.WrapUOHtmlColor(Color.Gray, false);
|
||||
|
||||
AddImage(x + ((w - 126) / 2), y, 40019, 900);
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, play, false, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SelectedGame.Enabled)
|
||||
{
|
||||
play = play.WrapUOHtmlBig();
|
||||
|
||||
AddHtmlButton(x + ((w - 126) / 2), y, 126, 25, b => PlayGame(SelectedGame), play, Color.White, Color.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
play = play.WrapUOHtmlColor(Color.Gray, false);
|
||||
|
||||
AddRectangle(x + ((w - 126) / 2), y, 126, 25, Color.Black, true);
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, play, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (User.AccessLevel < Arcade.Access)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
y += 25;
|
||||
|
||||
var label = SelectedGame.Enabled ? "Enabled" : "Disabled";
|
||||
|
||||
label = label.WrapUOHtmlCenter();
|
||||
|
||||
if (SupportsUltimaStore)
|
||||
{
|
||||
if (SelectedGame.Enabled)
|
||||
{
|
||||
label = label.WrapUOHtmlColor(Color.PaleGreen, false);
|
||||
|
||||
AddButton(x + ((w - 126) / 2), y, 40019, 40029, b => DisableGame(SelectedGame));
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, label, false, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
label = label.WrapUOHtmlColor(Color.Gray, false);
|
||||
|
||||
AddButton(x + ((w - 126) / 2), y, 40019, 40029, b => EnableGame(SelectedGame));
|
||||
AddHtml(x + ((w - 126) / 2), y + 2, 126, 40, label, false, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SelectedGame.Enabled)
|
||||
{
|
||||
label = label.WrapUOHtmlBig();
|
||||
|
||||
AddHtmlButton(
|
||||
x + ((w - 126) / 2),
|
||||
y,
|
||||
126,
|
||||
25,
|
||||
b => DisableGame(SelectedGame),
|
||||
label,
|
||||
Color.PaleGreen,
|
||||
Color.Black);
|
||||
}
|
||||
else
|
||||
{
|
||||
label = label.WrapUOHtmlBig();
|
||||
|
||||
AddHtmlButton(x + ((w - 126) / 2), y, 126, 25, b => EnableGame(SelectedGame), label, Color.Gray, Color.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDescription(IGame game)
|
||||
{
|
||||
if (game == null)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var desc = new StringBuilder();
|
||||
|
||||
desc.Append("<CENTER>");
|
||||
desc.AppendLine(game.Name.WrapUOHtmlBig().WrapUOHtmlColor(Color.Gold, false));
|
||||
desc.AppendLine(String.Empty.WrapUOHtmlColor(Color.White, false));
|
||||
desc.AppendLine(game.Desc);
|
||||
desc.Append("</CENTER>");
|
||||
|
||||
return desc.ToString();
|
||||
}
|
||||
|
||||
public void EnableGame(IGame game)
|
||||
{
|
||||
if (game == null)
|
||||
{
|
||||
Refresh(true);
|
||||
return;
|
||||
}
|
||||
|
||||
game.Enabled = true;
|
||||
|
||||
Refresh(true);
|
||||
}
|
||||
|
||||
public void DisableGame(IGame game)
|
||||
{
|
||||
if (game == null)
|
||||
{
|
||||
Refresh(true);
|
||||
return;
|
||||
}
|
||||
|
||||
game.Enabled = false;
|
||||
|
||||
Refresh(true);
|
||||
}
|
||||
|
||||
public void PlayGame(IGame game)
|
||||
{
|
||||
if (game == null || !game.Open(User))
|
||||
{
|
||||
Refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectGame(IGame game)
|
||||
{
|
||||
SelectedGame = SelectedGame == game ? null : game;
|
||||
|
||||
Refresh(true);
|
||||
}
|
||||
|
||||
public void SelectHelp(IGame game)
|
||||
{
|
||||
if (game == null)
|
||||
{
|
||||
Refresh(true);
|
||||
return;
|
||||
}
|
||||
|
||||
new NoticeDialogGump(User, Refresh())
|
||||
{
|
||||
Title = game.Name,
|
||||
Html = game.Help,
|
||||
Width = 500,
|
||||
Height = 400,
|
||||
HtmlColor = Color.White,
|
||||
AcceptHandler = Refresh,
|
||||
CancelHandler = Refresh
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected override void OnLayoutApplied()
|
||||
{
|
||||
base.OnLayoutApplied();
|
||||
|
||||
if (!SupportsUltimaStore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var e in Entries.OfType<GumpBackground>())
|
||||
{
|
||||
switch (e.GumpID)
|
||||
{
|
||||
case 2600:
|
||||
e.GumpID = 39925;
|
||||
break;
|
||||
case 9250:
|
||||
e.GumpID = 40000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
486
Scripts/SubSystem/VitaNex/Core/Modules/AutoDonate/AutoDonate.cs
Normal file
486
Scripts/SubSystem/VitaNex/Core/Modules/AutoDonate/AutoDonate.cs
Normal file
@@ -0,0 +1,486 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
|
||||
using VitaNex.IO;
|
||||
using VitaNex.SuperGumps;
|
||||
using VitaNex.Web;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public static partial class AutoDonate
|
||||
{
|
||||
public const AccessLevel Access = AccessLevel.Owner;
|
||||
|
||||
public static DonationOptions CMOptions { get; private set; }
|
||||
|
||||
public static BinaryDataStore<string, DonationTransaction> Transactions { get; private set; }
|
||||
|
||||
public static BinaryDirectoryDataStore<IAccount, DonationProfile> Profiles { get; private set; }
|
||||
|
||||
private static readonly string[] _AcceptedTypes =
|
||||
{"cart", "express_checkout", "recurring_payment", "send_money", "subscr_payment", "virtual_terminal", "web_accept"};
|
||||
|
||||
private static void OnLogin(LoginEventArgs e)
|
||||
{
|
||||
SpotCheck(e.Mobile);
|
||||
}
|
||||
|
||||
public static void SpotCheck(IAccount a)
|
||||
{
|
||||
if (a != null)
|
||||
{
|
||||
SpotCheck(a.FindMobiles().FirstOrDefault(p => p.IsOnline()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void SpotCheck(Mobile user)
|
||||
{
|
||||
if (user == null || user.Deleted || !user.Alive || !user.IsOnline() || Profiles.Status != DataStoreStatus.Idle ||
|
||||
!CMOptions.ModuleEnabled || CMOptions.CurrencyType == null || CMOptions.CurrencyPrice <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var profile = FindProfile(user.Account);
|
||||
|
||||
if (profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.Processed.Any())
|
||||
{
|
||||
var message = String.Format("Hey, {0}!\nYou have unclaimed donation credit!", user.RawName);
|
||||
|
||||
user.SendNotification(
|
||||
message,
|
||||
false,
|
||||
1.0,
|
||||
3.0,
|
||||
Color.LawnGreen,
|
||||
ui =>
|
||||
{
|
||||
ui.CanClose = false;
|
||||
ui.CanDispose = false;
|
||||
|
||||
ui.AddOption(
|
||||
"Claim!",
|
||||
b =>
|
||||
{
|
||||
CheckDonate(user);
|
||||
ui.Close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleWebForm(WebAPIContext context)
|
||||
{
|
||||
if (!CMOptions.WebForm.Enabled)
|
||||
{
|
||||
context.Response.Data = String.Empty;
|
||||
context.Response.Status = HttpStatusCode.NoContent;
|
||||
return;
|
||||
}
|
||||
|
||||
context.Response.Data = CMOptions.WebForm.Generate();
|
||||
context.Response.ContentType = "html";
|
||||
}
|
||||
|
||||
private static void HandleAccountCheck(WebAPIContext context)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(context.Request.Queries["username"]))
|
||||
{
|
||||
var acc = Accounts.GetAccount(context.Request.Queries["username"]);
|
||||
|
||||
context.Response.Data = acc != null ? "VALID" : "INVALID";
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Data = "INVALID";
|
||||
}
|
||||
|
||||
context.Response.ContentType = "txt";
|
||||
}
|
||||
|
||||
private static void HandleIPN(WebAPIContext context)
|
||||
{
|
||||
var test = context.Request.Queries["test"] != null || Insensitive.Contains(context.Request.Data, "test_ipn=1");
|
||||
|
||||
var endpoint = test ? "ipnpb.sandbox." : "ipnpb.";
|
||||
|
||||
var paypal = String.Format("https://{0}paypal.com/cgi-bin/webscr", endpoint);
|
||||
|
||||
WebAPI.BeginRequest(paypal, "cmd=_notify-validate&" + context.Request.Data, BeginVerification, EndVerification);
|
||||
}
|
||||
|
||||
private static void BeginVerification(HttpWebRequest webReq, string state)
|
||||
{
|
||||
webReq.Method = "POST";
|
||||
webReq.ContentType = "application/x-www-form-urlencoded";
|
||||
webReq.SetContent(state, ResolveEncoding(state));
|
||||
}
|
||||
|
||||
private static void EndVerification(HttpWebRequest webReq, string state, HttpWebResponse webRes)
|
||||
{
|
||||
var content = webRes.GetContent();
|
||||
|
||||
File.AppendAllLines("IPN.log", new[] { "\n\nREQUEST:\n", state, "\n\nRESPONSE:\n", content });
|
||||
|
||||
if (Insensitive.Contains(content, "VERIFIED"))
|
||||
{
|
||||
using (var queries = new WebAPIQueries(state))
|
||||
{
|
||||
ProcessTransaction(queries);
|
||||
}
|
||||
|
||||
RefreshAdminUI();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessTransaction(WebAPIQueries queries)
|
||||
{
|
||||
var id = queries["txn_id"];
|
||||
|
||||
if (String.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = queries["txn_type"];
|
||||
|
||||
if (String.IsNullOrWhiteSpace(type) || !type.EqualsAny(true, _AcceptedTypes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var status = queries["payment_status"];
|
||||
|
||||
if (String.IsNullOrWhiteSpace(status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TransactionState state;
|
||||
|
||||
switch (status.Trim().ToUpper())
|
||||
{
|
||||
case "PENDING":
|
||||
case "PROCESSED":
|
||||
case "CREATED":
|
||||
state = TransactionState.Pending;
|
||||
break;
|
||||
case "COMPLETED":
|
||||
state = TransactionState.Processed;
|
||||
break;
|
||||
default:
|
||||
state = TransactionState.Voided;
|
||||
break;
|
||||
}
|
||||
|
||||
ExtractCart(queries, out var credit, out var value);
|
||||
|
||||
var custom = queries["custom"] ?? String.Empty;
|
||||
|
||||
var trans = Transactions.GetValue(id);
|
||||
|
||||
var create = trans == null;
|
||||
|
||||
if (create)
|
||||
{
|
||||
var email = queries["payer_email"] ?? String.Empty;
|
||||
var notes = queries["payer_note"] ?? String.Empty;
|
||||
var extra = queries["extra_info"] ?? String.Empty;
|
||||
|
||||
var a = Accounts.GetAccount(custom) ?? CMOptions.FallbackAccount;
|
||||
|
||||
Transactions[id] = trans = new DonationTransaction(id, a, email, value, credit, notes, extra);
|
||||
|
||||
var profile = EnsureProfile(a);
|
||||
|
||||
if (profile == null)
|
||||
{
|
||||
state = TransactionState.Voided;
|
||||
trans.Extra += "{VOID: NO PROFILE}";
|
||||
}
|
||||
else
|
||||
{
|
||||
profile.Add(trans);
|
||||
}
|
||||
}
|
||||
|
||||
if (!VerifyValue(queries, "business", CMOptions.Business) &&
|
||||
!VerifyValue(queries, "receiver_email", CMOptions.Business) &&
|
||||
!VerifyValue(queries, "receiver_id", CMOptions.Business))
|
||||
{
|
||||
state = TransactionState.Voided;
|
||||
trans.Extra += "{VOID: UNEXPECTED BUSINESS}";
|
||||
}
|
||||
|
||||
if (trans.Total != value)
|
||||
{
|
||||
state = TransactionState.Voided;
|
||||
trans.Extra += "{VOID: TOTAL CHANGED}";
|
||||
}
|
||||
|
||||
if (queries["test"] != null || queries["test_ipn"] != null)
|
||||
{
|
||||
state = TransactionState.Voided;
|
||||
trans.Extra += "{VOID: TESTING}";
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TransactionState.Processed:
|
||||
trans.Process();
|
||||
break;
|
||||
case TransactionState.Voided:
|
||||
trans.Void();
|
||||
break;
|
||||
}
|
||||
|
||||
if (create && trans.IsPending)
|
||||
{
|
||||
DonationEvents.InvokeTransPending(trans);
|
||||
}
|
||||
|
||||
SpotCheck(trans.Account);
|
||||
}
|
||||
|
||||
private static bool VerifyValue(WebAPIQueries queries, string key, string val)
|
||||
{
|
||||
return !String.IsNullOrWhiteSpace(queries[key]) && Insensitive.Equals(queries[key], val);
|
||||
}
|
||||
|
||||
private static void ExtractCart(WebAPIQueries queries, out long credit, out double value)
|
||||
{
|
||||
var isCart = Insensitive.Equals(queries["txn_type"], "cart");
|
||||
|
||||
const string totalKey = "quantity";
|
||||
|
||||
var grossKey = "mc_gross";
|
||||
|
||||
if (isCart)
|
||||
{
|
||||
grossKey += '_';
|
||||
}
|
||||
|
||||
credit = 0;
|
||||
value = 0;
|
||||
|
||||
foreach (var kv in queries)
|
||||
{
|
||||
var i = kv.Key.IndexOf("item_number", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (i < 0 || !Insensitive.Equals(kv.Value, CMOptions.CurrencyType.TypeName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var k = "0";
|
||||
|
||||
if (i + 11 < kv.Key.Length)
|
||||
{
|
||||
k = kv.Key.Substring(i + 11);
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(k))
|
||||
{
|
||||
k = "0";
|
||||
}
|
||||
|
||||
if (!Int32.TryParse(k, out i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var subTotal = Math.Max(0, Int64.Parse(queries[totalKey + i.ToString("#")] ?? "0"));
|
||||
var subGross = Math.Max(0, Double.Parse(queries[grossKey + i.ToString("#")] ?? "0", CultureInfo.InvariantCulture));
|
||||
|
||||
if (subTotal <= (isCart ? 0 : 1))
|
||||
{
|
||||
subTotal = (long)(subGross / CMOptions.CurrencyPrice);
|
||||
}
|
||||
else
|
||||
{
|
||||
var expGross = subTotal * CMOptions.CurrencyPrice;
|
||||
|
||||
if (Math.Abs(expGross - subGross) > CMOptions.CurrencyPrice)
|
||||
{
|
||||
subGross = expGross;
|
||||
}
|
||||
}
|
||||
|
||||
credit += subTotal;
|
||||
value += subGross;
|
||||
}
|
||||
}
|
||||
|
||||
private static Encoding ResolveEncoding(string state)
|
||||
{
|
||||
Encoding enc = null;
|
||||
|
||||
var efb = Encoding.UTF8.EncoderFallback;
|
||||
var dfb = Encoding.UTF8.DecoderFallback;
|
||||
|
||||
try
|
||||
{
|
||||
if (Insensitive.Contains(state, "charset="))
|
||||
{
|
||||
var start = state.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) + 8;
|
||||
var count = state.IndexOf('&', start) - start;
|
||||
var value = state.Substring(start, count);
|
||||
|
||||
if (Insensitive.StartsWith(value, "windows-"))
|
||||
{
|
||||
if (Int32.TryParse(value.Substring(8), out var id))
|
||||
{
|
||||
enc = Encoding.GetEncoding(id, efb, dfb);
|
||||
}
|
||||
}
|
||||
|
||||
if (enc == null)
|
||||
{
|
||||
enc = Encoding.GetEncoding(value, efb, dfb);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
enc = null;
|
||||
}
|
||||
|
||||
return enc ?? Encoding.UTF8;
|
||||
}
|
||||
|
||||
public static DonationProfile FindProfile(IAccount a)
|
||||
{
|
||||
return Profiles.GetValue(a);
|
||||
}
|
||||
|
||||
public static DonationProfile EnsureProfile(IAccount a)
|
||||
{
|
||||
var p = Profiles.GetValue(a);
|
||||
|
||||
if (p == null && a != null)
|
||||
{
|
||||
Profiles[a] = p = new DonationProfile(a);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
public static void CheckDonate(Mobile user, bool message = true)
|
||||
{
|
||||
if (user == null || user.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CMOptions.ModuleEnabled)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("The donation exchange is currently unavailable, please try again later.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.Alive)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("You must be alive to do that!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Profiles.Status != DataStoreStatus.Idle)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("The donation exchange is busy, please try again in a few moments.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CMOptions.CurrencyType == null || CMOptions.CurrencyPrice <= 0)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("Currency conversion is currently disabled, contact a member of staff to handle your donations.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var profile = FindProfile(user.Account);
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
var count = profile.Visible.Count();
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("There are no current donation records for your account.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
{
|
||||
user.SendMessage("Thank you for your donation{0}, {1}!", count != 1 ? "s" : "", user.RawName);
|
||||
}
|
||||
|
||||
DonationProfileUI.DisplayTo(user, profile, profile.Visible.FirstOrDefault());
|
||||
}
|
||||
}
|
||||
else if (message)
|
||||
{
|
||||
user.SendMessage("There are no current donation records for your account.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckConfig(Mobile user)
|
||||
{
|
||||
if (user != null && !user.Deleted && user.AccessLevel >= Access)
|
||||
{
|
||||
(SuperGump.GetInstance<DonationAdminUI>(user) ?? new DonationAdminUI(user)).Refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RefreshAdminUI()
|
||||
{
|
||||
foreach (var g in SuperGump.GlobalInstances.Values.OfType<DonationAdminUI>())
|
||||
{
|
||||
g.Refresh(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
|
||||
using VitaNex.IO;
|
||||
using VitaNex.Web;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
[CoreModule("Auto Donate", "3.1.0.0", true, TaskPriority.Highest)]
|
||||
public static partial class AutoDonate
|
||||
{
|
||||
static AutoDonate()
|
||||
{
|
||||
CMOptions = new DonationOptions();
|
||||
|
||||
Transactions = new BinaryDataStore<string, DonationTransaction>(
|
||||
VitaNexCore.SavesDirectory + "/AutoDonate",
|
||||
"Transactions")
|
||||
{
|
||||
OnSerialize = SerializeTransactions,
|
||||
OnDeserialize = DeserializeTransactions
|
||||
};
|
||||
|
||||
Profiles = new BinaryDirectoryDataStore<IAccount, DonationProfile>(
|
||||
VitaNexCore.SavesDirectory + "/AutoDonate",
|
||||
"Profiles",
|
||||
"pro")
|
||||
{
|
||||
OnSerialize = SerializeProfile,
|
||||
OnDeserialize = DeserializeProfile
|
||||
};
|
||||
}
|
||||
|
||||
private static void CMConfig()
|
||||
{
|
||||
CommandUtility.Register("CheckDonate", AccessLevel.Player, e => CheckDonate(e.Mobile));
|
||||
CommandUtility.Register("DonateConfig", Access, e => CheckConfig(e.Mobile));
|
||||
|
||||
CommandUtility.RegisterAlias("DonateConfig", "DonateAdmin");
|
||||
|
||||
EventSink.Login += OnLogin;
|
||||
|
||||
WebAPI.Register("/donate/ipn", HandleIPN);
|
||||
WebAPI.Register("/donate/acc", HandleAccountCheck);
|
||||
WebAPI.Register("/donate/form", HandleWebForm);
|
||||
}
|
||||
|
||||
private static void CMEnabled()
|
||||
{
|
||||
WebAPI.Register("/donate/ipn", HandleIPN);
|
||||
WebAPI.Register("/donate/acc", HandleAccountCheck);
|
||||
WebAPI.Register("/donate/form", HandleWebForm);
|
||||
}
|
||||
|
||||
private static void CMDisabled()
|
||||
{
|
||||
WebAPI.Unregister("/donate/ipn");
|
||||
WebAPI.Unregister("/donate/acc");
|
||||
WebAPI.Unregister("/donate/form");
|
||||
}
|
||||
|
||||
private static void CMSave()
|
||||
{
|
||||
Transactions.Export();
|
||||
Profiles.Export();
|
||||
}
|
||||
|
||||
private static void CMLoad()
|
||||
{
|
||||
Transactions.Import();
|
||||
Profiles.Import();
|
||||
}
|
||||
|
||||
private static void CMInvoke()
|
||||
{
|
||||
var owner = Accounts.GetAccounts().FirstOrDefault(ac => ac.AccessLevel == AccessLevel.Owner);
|
||||
|
||||
foreach (var trans in Transactions.Values)
|
||||
{
|
||||
if (trans.Account == null && owner != null)
|
||||
{
|
||||
trans.SetAccount(owner);
|
||||
}
|
||||
|
||||
if (trans.Account == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var p = EnsureProfile(trans.Account);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.Transactions[trans.ID] = trans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SerializeTransactions(GenericWriter writer)
|
||||
{
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.WriteBlockDictionary(Transactions, (w, k, v) => v.Serialize(w));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DeserializeTransactions(GenericReader reader)
|
||||
{
|
||||
reader.GetVersion();
|
||||
|
||||
reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var t = new DonationTransaction(r);
|
||||
|
||||
return new KeyValuePair<string, DonationTransaction>(t.ID, t);
|
||||
},
|
||||
Transactions);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SerializeProfile(GenericWriter writer, IAccount key, DonationProfile val)
|
||||
{
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
writer.WriteBlock(
|
||||
w =>
|
||||
{
|
||||
w.Write(key);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
val.Serialize(w);
|
||||
break;
|
||||
case 0:
|
||||
w.WriteType(val, t => val.Serialize(w));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Tuple<IAccount, DonationProfile> DeserializeProfile(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
return reader.ReadBlock(r =>
|
||||
{
|
||||
var key = r.ReadAccount();
|
||||
|
||||
DonationProfile val = null;
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
val = new DonationProfile(r);
|
||||
break;
|
||||
case 0:
|
||||
val = r.ReadTypeCreate<DonationProfile>(r);
|
||||
break;
|
||||
}
|
||||
|
||||
if (key == null && val != null && val.Account != null)
|
||||
{
|
||||
key = val.Account;
|
||||
}
|
||||
|
||||
return Tuple.Create(key, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Items;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public class DonationGiftBook : RedBook
|
||||
{
|
||||
[Constructable]
|
||||
public DonationGiftBook(Mobile author, string text)
|
||||
: base("A Gift", author.RawName, GetPageCount(text), false)
|
||||
{
|
||||
Hue = 0x89B;
|
||||
SetText(text);
|
||||
}
|
||||
|
||||
public DonationGiftBook(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
private static int GetPageCount(string text)
|
||||
{
|
||||
ParseFactors(text.Split(' '), out var wordCount, out var wordsPerLine, out var linesPerPage, out var index, out var pageCount);
|
||||
|
||||
return pageCount;
|
||||
}
|
||||
|
||||
private void SetText(string text)
|
||||
{
|
||||
var words = text.Split(' ');
|
||||
|
||||
ParseFactors(words, out var wordCount, out var wordsPerLine, out var linesPerPage, out var index, out var pageCount);
|
||||
|
||||
for (var currentPage = 0; currentPage < pageCount; currentPage++)
|
||||
{
|
||||
for (var currentLine = 0; currentLine < linesPerPage; currentLine++)
|
||||
{
|
||||
Pages[currentPage] = new BookPageInfo(new string[linesPerPage]);
|
||||
var line = new StringBuilder();
|
||||
|
||||
for (var currentWord = 0; currentWord < wordsPerLine; currentWord++)
|
||||
{
|
||||
if (index >= wordCount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
line.AppendFormat(" {0}", words[index]);
|
||||
index++;
|
||||
}
|
||||
|
||||
Pages[currentPage].Lines[currentLine] = line.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ParseFactors(
|
||||
string[] words,
|
||||
out int wordCount,
|
||||
out int wordsPerLine,
|
||||
out int linesPerPage,
|
||||
out int index,
|
||||
out int pageCount)
|
||||
{
|
||||
wordCount = words.Length;
|
||||
wordsPerLine = 5;
|
||||
linesPerPage = 8;
|
||||
index = 0;
|
||||
pageCount = wordCount / (wordsPerLine * linesPerPage);
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
|
||||
using VitaNex.Crypto;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public sealed class DonationProfile : IEnumerable<DonationTransaction>, IEquatable<DonationProfile>
|
||||
{
|
||||
public delegate double TierCalculation(DonationProfile p, int tier, double factor);
|
||||
|
||||
public static TierCalculation ComputeNextTier = (p, t, f) => (t + 1) * f;
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public CryptoHashCode UID { get; private set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public IAccount Account { get; private set; }
|
||||
|
||||
public Dictionary<string, DonationTransaction> Transactions { get; private set; }
|
||||
|
||||
public DonationTransaction this[string id] => Transactions.GetValue(id);
|
||||
|
||||
public IEnumerable<DonationTransaction> Pending => Find(TransactionState.Pending);
|
||||
public IEnumerable<DonationTransaction> Processed => Find(TransactionState.Processed);
|
||||
public IEnumerable<DonationTransaction> Claimed => Find(TransactionState.Claimed);
|
||||
public IEnumerable<DonationTransaction> Voided => Find(TransactionState.Voided);
|
||||
|
||||
public IEnumerable<DonationTransaction> Visible => Transactions.Values.Where(t => !t.Hidden);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long TotalCredit => Claimed.Aggregate(0L, (c, t) => c + t.CreditTotal);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TotalValue => Claimed.Aggregate(0.0, (c, t) => c + t.Total);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public int Tier
|
||||
{
|
||||
get
|
||||
{
|
||||
var tier = 0;
|
||||
|
||||
if (AutoDonate.CMOptions.TierFactor <= 0.0)
|
||||
{
|
||||
return tier;
|
||||
}
|
||||
|
||||
double total = TotalValue, factor = AutoDonate.CMOptions.TierFactor, req;
|
||||
|
||||
while (total > 0)
|
||||
{
|
||||
req = ComputeNextTier(this, tier, factor);
|
||||
|
||||
if (req <= 0 || total < req)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
total -= req;
|
||||
|
||||
++tier;
|
||||
}
|
||||
|
||||
return tier;
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long Credit { get; set; }
|
||||
|
||||
public DonationProfile(IAccount account)
|
||||
{
|
||||
Transactions = new Dictionary<string, DonationTransaction>();
|
||||
|
||||
Account = account;
|
||||
|
||||
UID = new CryptoHashCode(CryptoHashType.MD5, Account.Username);
|
||||
}
|
||||
|
||||
public DonationProfile(GenericReader reader)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public IEnumerable<DonationTransaction> Find(TransactionState state)
|
||||
{
|
||||
return Transactions.Values.Where(trans => trans != null && trans.State == state);
|
||||
}
|
||||
|
||||
public DonationTransaction Find(string id)
|
||||
{
|
||||
return Transactions.GetValue(id);
|
||||
}
|
||||
|
||||
public bool Contains(DonationTransaction trans)
|
||||
{
|
||||
return Transactions.ContainsKey(trans.ID);
|
||||
}
|
||||
|
||||
public void Add(DonationTransaction trans)
|
||||
{
|
||||
if (trans != null)
|
||||
{
|
||||
AutoDonate.Transactions[trans.ID] = Transactions[trans.ID] = trans;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(DonationTransaction trans)
|
||||
{
|
||||
return Transactions.Remove(trans.ID);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return Transactions.Values.GetEnumerator();
|
||||
}
|
||||
|
||||
public IEnumerator<DonationTransaction> GetEnumerator()
|
||||
{
|
||||
return Transactions.Values.GetEnumerator();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return UID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is DonationProfile && Equals((DonationProfile)obj);
|
||||
}
|
||||
|
||||
public bool Equals(DonationProfile other)
|
||||
{
|
||||
return !ReferenceEquals(other, null) && (ReferenceEquals(other, this) || UID.Equals(other.UID));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return UID.ToString();
|
||||
}
|
||||
|
||||
public void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Account);
|
||||
writer.Write(Credit);
|
||||
|
||||
writer.WriteDictionary(
|
||||
Transactions,
|
||||
(w, k, v) =>
|
||||
{
|
||||
if (v == null)
|
||||
{
|
||||
w.Write(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Write(true);
|
||||
|
||||
if (version > 0)
|
||||
{
|
||||
w.Write(v.ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
v.Serialize(w);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 0:
|
||||
{
|
||||
Account = reader.ReadAccount();
|
||||
Credit = reader.ReadLong();
|
||||
|
||||
Transactions = reader.ReadDictionary(
|
||||
r =>
|
||||
{
|
||||
string k = null;
|
||||
DonationTransaction v = null;
|
||||
|
||||
if (r.ReadBool())
|
||||
{
|
||||
if (version > 0)
|
||||
{
|
||||
k = r.ReadString();
|
||||
v = AutoDonate.Transactions.GetValue(k);
|
||||
}
|
||||
else
|
||||
{
|
||||
v = new DonationTransaction(r);
|
||||
k = v.ID;
|
||||
|
||||
AutoDonate.Transactions[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return new KeyValuePair<string, DonationTransaction>(k, v);
|
||||
},
|
||||
Transactions);
|
||||
|
||||
if (version < 1) // Gifts
|
||||
{
|
||||
reader.ReadDictionary(
|
||||
() =>
|
||||
{
|
||||
var k = reader.ReadString();
|
||||
var v = reader.ReadString();
|
||||
return new KeyValuePair<string, string>(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DonationProfile l, DonationProfile r)
|
||||
{
|
||||
return ReferenceEquals(l, null) ? ReferenceEquals(r, null) : l.Equals(r);
|
||||
}
|
||||
|
||||
public static bool operator !=(DonationProfile l, DonationProfile r)
|
||||
{
|
||||
return ReferenceEquals(l, null) ? !ReferenceEquals(r, null) : !l.Equals(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
using Server.Misc;
|
||||
|
||||
using VitaNex.IO;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
[PropertyObject]
|
||||
public sealed class DonationTransaction : IEquatable<DonationTransaction>, IComparable<DonationTransaction>
|
||||
{
|
||||
private static void Protect(Item item)
|
||||
{
|
||||
var flags = ScriptCompiler.FindTypeByFullName("Server.Items.ItemFlags");
|
||||
|
||||
if (flags != null)
|
||||
{
|
||||
flags.InvokeMethod("SetStealable", item, false);
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public string ID { get; private set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public IAccount Account { get; private set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public bool Deleted { get; private set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public int Version { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public TimeStamp Time { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public string Email { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public double Total { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access, true)]
|
||||
public long Bonus { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long Credit { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Notes { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Extra { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public TimeStamp DeliveryTime { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string DeliveredTo { get; set; }
|
||||
|
||||
private TransactionState _State;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public TransactionState State
|
||||
{
|
||||
get => _State;
|
||||
set
|
||||
{
|
||||
if (_State == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var old = _State;
|
||||
|
||||
_State = value;
|
||||
|
||||
OnStateChanged(old);
|
||||
}
|
||||
}
|
||||
|
||||
public TransactionState InternalState { get => State; set => _State = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public bool Hidden => (IsClaimed || IsVoided) && !AutoDonate.CMOptions.ShowHistory;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long CreditTotal => Credit + Bonus;
|
||||
|
||||
public bool IsClaimed => State == TransactionState.Claimed;
|
||||
public bool IsPending => State == TransactionState.Pending;
|
||||
public bool IsProcessed => State == TransactionState.Processed;
|
||||
public bool IsVoided => State == TransactionState.Voided;
|
||||
|
||||
public string FullPath => String.Format("{0}|{1}|{2}", Time.Value.Year, Time.Value.GetMonth(), ID);
|
||||
|
||||
public DonationTransaction(
|
||||
string id,
|
||||
IAccount account,
|
||||
string email,
|
||||
double total,
|
||||
long credit,
|
||||
string notes,
|
||||
string extra)
|
||||
{
|
||||
Version = 0;
|
||||
Time = TimeStamp.Now;
|
||||
|
||||
ID = id;
|
||||
|
||||
Account = account;
|
||||
Email = email;
|
||||
Total = total;
|
||||
|
||||
Credit = credit;
|
||||
Bonus = 0;
|
||||
|
||||
Notes = notes;
|
||||
Extra = extra;
|
||||
|
||||
_State = TransactionState.Pending;
|
||||
}
|
||||
|
||||
public DonationTransaction(GenericReader reader)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
private void OnStateChanged(TransactionState oldState)
|
||||
{
|
||||
DonationEvents.InvokeStateChanged(this, oldState);
|
||||
}
|
||||
|
||||
public bool Void()
|
||||
{
|
||||
if (State == TransactionState.Voided)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((State = TransactionState.Voided) == TransactionState.Voided)
|
||||
{
|
||||
++Version;
|
||||
|
||||
DonationEvents.InvokeTransVoided(this);
|
||||
|
||||
LogToFile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Process()
|
||||
{
|
||||
if (State == TransactionState.Processed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State != TransactionState.Pending)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((State = TransactionState.Processed) == TransactionState.Processed)
|
||||
{
|
||||
++Version;
|
||||
|
||||
DonationEvents.InvokeTransProcessed(this);
|
||||
|
||||
LogToFile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Claim(Mobile m)
|
||||
{
|
||||
if (State == TransactionState.Claimed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State != TransactionState.Processed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((State = TransactionState.Claimed) == TransactionState.Claimed)
|
||||
{
|
||||
Deliver(m);
|
||||
DeliveredTo = m.RawName;
|
||||
DeliveryTime = TimeStamp.Now;
|
||||
|
||||
Extra += "{SHARD: " + ServerList.ServerName + "}";
|
||||
|
||||
++Version;
|
||||
|
||||
DonationEvents.InvokeTransClaimed(this, m);
|
||||
|
||||
LogToFile();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public long GetCredit(DonationProfile dp, out long credit, out long bonus)
|
||||
{
|
||||
return GetCredit(dp, false, out credit, out bonus);
|
||||
}
|
||||
|
||||
private long GetCredit(DonationProfile dp, bool delivering, out long credit, out long bonus)
|
||||
{
|
||||
if (!delivering && State != TransactionState.Processed)
|
||||
{
|
||||
return (credit = Credit) + (bonus = Bonus);
|
||||
}
|
||||
|
||||
var total = DonationEvents.InvokeTransExchange(this, dp);
|
||||
|
||||
if (AutoDonate.CMOptions.CreditBonus > 0)
|
||||
{
|
||||
total += (long)Math.Floor(Credit * AutoDonate.CMOptions.CreditBonus);
|
||||
}
|
||||
|
||||
bonus = Math.Max(0, total - Credit);
|
||||
credit = Math.Max(0, total - bonus);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private void Deliver(Mobile m)
|
||||
{
|
||||
if (m == null || m.Account == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Account != m.Account)
|
||||
{
|
||||
SetAccount(m.Account);
|
||||
}
|
||||
|
||||
var dp = AutoDonate.EnsureProfile(Account);
|
||||
|
||||
if (dp == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var total = GetCredit(dp, true, out var credit, out var bonus);
|
||||
|
||||
Credit = credit;
|
||||
Bonus = bonus;
|
||||
|
||||
var bag = DonationEvents.InvokeTransPack(this, dp);
|
||||
|
||||
if (bag == null || bag.Deleted)
|
||||
{
|
||||
dp.Credit += total;
|
||||
return;
|
||||
}
|
||||
|
||||
Protect(bag);
|
||||
|
||||
while (credit > 0)
|
||||
{
|
||||
var cur = AutoDonate.CMOptions.CurrencyType.CreateInstance();
|
||||
|
||||
if (cur == null)
|
||||
{
|
||||
bag.Delete();
|
||||
break;
|
||||
}
|
||||
|
||||
Protect(cur);
|
||||
|
||||
if (cur.Stackable)
|
||||
{
|
||||
cur.Amount = (int)Math.Min(credit, 60000);
|
||||
}
|
||||
|
||||
credit -= cur.Amount;
|
||||
|
||||
bag.DropItem(cur);
|
||||
}
|
||||
|
||||
if (bag.Deleted)
|
||||
{
|
||||
dp.Credit += total;
|
||||
return;
|
||||
}
|
||||
|
||||
while (bonus > 0)
|
||||
{
|
||||
var cur = AutoDonate.CMOptions.CurrencyType.CreateInstance();
|
||||
|
||||
if (cur == null)
|
||||
{
|
||||
bag.Delete();
|
||||
break;
|
||||
}
|
||||
|
||||
Protect(cur);
|
||||
|
||||
cur.Name = String.Format("{0} [Bonus]", cur.ResolveName(m));
|
||||
|
||||
if (cur.Stackable)
|
||||
{
|
||||
cur.Amount = (int)Math.Min(bonus, 60000);
|
||||
}
|
||||
|
||||
bonus -= cur.Amount;
|
||||
|
||||
bag.DropItem(cur);
|
||||
}
|
||||
|
||||
if (bag.Deleted || bag.GiveTo(m, GiveFlags.PackBankDelete) == GiveFlags.Delete)
|
||||
{
|
||||
dp.Credit += total;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAccount(IAccount acc)
|
||||
{
|
||||
if (Account == acc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
++Version;
|
||||
|
||||
Extra += "{ACCOUNT CHANGED FROM '" + Account + "' TO '" + acc + "'}";
|
||||
|
||||
var profile = AutoDonate.FindProfile(Account);
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
profile.Remove(this);
|
||||
}
|
||||
|
||||
Account = acc;
|
||||
|
||||
profile = AutoDonate.EnsureProfile(Account);
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
profile.Add(this);
|
||||
}
|
||||
|
||||
LogToFile();
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Deleted = true;
|
||||
|
||||
AutoDonate.Transactions.Remove(ID);
|
||||
|
||||
DonationEvents.InvokeTransactionDeleted(this);
|
||||
|
||||
DeliveredTo = null;
|
||||
SetAccount(null);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is DonationTransaction && Equals((DonationTransaction)obj);
|
||||
}
|
||||
|
||||
public bool Equals(DonationTransaction other)
|
||||
{
|
||||
return other != null && String.Equals(ID, other.ID);
|
||||
}
|
||||
|
||||
public int CompareTo(DonationTransaction other)
|
||||
{
|
||||
var res = 0;
|
||||
|
||||
if (this.CompareNull(other, ref res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return TimeStamp.Compare(Time, other.Time);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void LogToFile()
|
||||
{
|
||||
var file = IOUtility.EnsureFile(VitaNexCore.LogsDirectory + "/Donations/" + ID + ".log");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(new string('*', 80));
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("{0}: {1}", file.Exists ? "UPDATED" : "CREATED", DateTime.Now);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("ID: {0}", ID);
|
||||
sb.AppendLine("State: {0}", State);
|
||||
sb.AppendLine("Time: {0}", Time.Value);
|
||||
sb.AppendLine("Version: {0:#,0}", Version);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Account: {0}", Account);
|
||||
sb.AppendLine("Email: {0}", Email);
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Total: {0}{1} {2}", AutoDonate.CMOptions.MoneySymbol, Total, AutoDonate.CMOptions.MoneyAbbr);
|
||||
sb.AppendLine("Credit: {0:#,0} {1}", Credit, AutoDonate.CMOptions.CurrencyName);
|
||||
sb.AppendLine("Bonus: {0:#,0} {1}", Bonus, AutoDonate.CMOptions.CurrencyName);
|
||||
|
||||
if (DeliveredTo != null)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Delivered: {0}", DeliveryTime.Value);
|
||||
sb.AppendLine("Recipient: {0}", DeliveredTo);
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(Notes))
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Notes:");
|
||||
sb.AppendLine(Notes);
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(Extra))
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("Extra:");
|
||||
sb.AppendLine(Extra);
|
||||
}
|
||||
|
||||
sb.Log(file);
|
||||
}
|
||||
|
||||
public void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(3);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
writer.Write(Deleted);
|
||||
goto case 2;
|
||||
case 2:
|
||||
case 1:
|
||||
writer.Write(Bonus);
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.Write(ID);
|
||||
writer.WriteFlag(_State);
|
||||
writer.Write(Account);
|
||||
writer.Write(Email);
|
||||
writer.Write(Total);
|
||||
writer.Write(Credit);
|
||||
writer.Write(Time);
|
||||
writer.Write(Version);
|
||||
|
||||
writer.Write(Notes);
|
||||
writer.Write(Extra);
|
||||
|
||||
writer.Write(DeliveredTo);
|
||||
writer.Write(DeliveryTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
Deleted = reader.ReadBool();
|
||||
goto case 2;
|
||||
case 2:
|
||||
case 1:
|
||||
Bonus = reader.ReadLong();
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
ID = reader.ReadString();
|
||||
_State = reader.ReadFlag<TransactionState>();
|
||||
Account = reader.ReadAccount();
|
||||
Email = reader.ReadString();
|
||||
Total = reader.ReadDouble();
|
||||
Credit = reader.ReadLong();
|
||||
|
||||
Time = version > 0 ? reader.ReadTimeStamp() : reader.ReadDouble();
|
||||
|
||||
Version = reader.ReadInt();
|
||||
|
||||
if (version < 1)
|
||||
{
|
||||
reader.ReadInt(); // InternalVersion
|
||||
}
|
||||
|
||||
Notes = reader.ReadString();
|
||||
Extra = reader.ReadString();
|
||||
|
||||
if (version > 1)
|
||||
{
|
||||
DeliveredTo = reader.ReadString();
|
||||
DeliveryTime = reader.ReadTimeStamp();
|
||||
}
|
||||
else if (version > 0)
|
||||
{
|
||||
var m = reader.ReadMobile();
|
||||
|
||||
DeliveredTo = m != null ? m.RawName : null;
|
||||
DeliveryTime = reader.ReadTimeStamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.ReadMobile(); // DeliverFrom
|
||||
|
||||
var m = reader.ReadMobile();
|
||||
|
||||
DeliveredTo = m != null ? m.RawName : null;
|
||||
DeliveryTime = reader.ReadDouble();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public enum TransactionState
|
||||
{
|
||||
Voided = 0,
|
||||
Pending,
|
||||
Processed,
|
||||
Claimed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.IO;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
[PropertyObject]
|
||||
public sealed class DonationStatistics
|
||||
{
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public DataStoreStatus ProfileStatus => AutoDonate.Profiles.Status;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public int ProfileCount => AutoDonate.Profiles.Count;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TotalIncome => ProfileCount <= 0 ? 0 : AutoDonate.Profiles.Values.Sum(p => p.TotalValue);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long TotalCredit => ProfileCount <= 0 ? 0 : AutoDonate.Profiles.Values.Sum(p => p.TotalCredit);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public int TierMin => ProfileCount <= 0 ? 0 : AutoDonate.Profiles.Values.Min(p => p.Tier);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public int TierMax => ProfileCount <= 0 ? 0 : AutoDonate.Profiles.Values.Max(p => p.Tier);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TierAverage => ProfileCount <= 0 ? 0 : AutoDonate.Profiles.Values.Average(p => p.Tier);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public DataStoreStatus TransStatus => AutoDonate.Transactions.Status;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public int TransCount => AutoDonate.Transactions.Count;
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TransMin => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Min(p => p.Total);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TransMax => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Max(p => p.Total);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TransAverage => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Average(t => t.Total);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long CreditMin => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Min(p => p.Credit);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public long CreditMax => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Max(p => p.Credit);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double CreditAverage => TransCount <= 0 ? 0 : AutoDonate.Transactions.Values.Average(t => t.Credit);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Donation Statistics";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public sealed class DonationOptions : CoreModuleOptions
|
||||
{
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public DonationStatistics Info { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public bool ShowHistory { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public char MoneySymbol { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double TierFactor { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double CreditBonus { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public DonationWebFormOptions WebForm { get; set; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Business { get => WebForm.Business; set => WebForm.Business = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string MoneyAbbr { get => WebForm.Currency; set => WebForm.Currency = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double CurrencyPrice { get => WebForm.ItemValue; set => WebForm.ItemValue = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string CurrencyName { get => WebForm.ItemName; set => WebForm.ItemName = value; }
|
||||
|
||||
private ItemTypeSelectProperty _CurrencyType = new ItemTypeSelectProperty();
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public ItemTypeSelectProperty CurrencyType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_CurrencyType.TypeName != WebForm.ItemType)
|
||||
{
|
||||
_CurrencyType.TypeName = WebForm.ItemType;
|
||||
}
|
||||
|
||||
return _CurrencyType;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
_CurrencyType = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_CurrencyType.TypeName = String.Empty;
|
||||
}
|
||||
|
||||
WebForm.ItemType = _CurrencyType.TypeName;
|
||||
}
|
||||
}
|
||||
|
||||
private IAccount _FallbackAccount;
|
||||
|
||||
public IAccount FallbackAccount
|
||||
{
|
||||
get
|
||||
{
|
||||
ValidateFallbackAccount(ref _FallbackAccount);
|
||||
return _FallbackAccount;
|
||||
}
|
||||
set
|
||||
{
|
||||
_FallbackAccount = value;
|
||||
ValidateFallbackAccount(ref _FallbackAccount);
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string FallbackUsername
|
||||
{
|
||||
get => FallbackAccount.Username;
|
||||
set => FallbackAccount = Accounts.GetAccount(value);
|
||||
}
|
||||
|
||||
private static void ValidateFallbackAccount(ref IAccount acc)
|
||||
{
|
||||
if (acc == null)
|
||||
{
|
||||
acc = Accounts.GetAccounts().FirstOrDefault(a => a.AccessLevel == AccessLevel.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public DonationOptions()
|
||||
: base(typeof(AutoDonate))
|
||||
{
|
||||
WebForm = new DonationWebFormOptions();
|
||||
|
||||
MoneySymbol = '$';
|
||||
ShowHistory = false;
|
||||
TierFactor = 0.0;
|
||||
CreditBonus = 0.0;
|
||||
|
||||
Info = new DonationStatistics();
|
||||
}
|
||||
|
||||
public DonationOptions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
MoneySymbol = ' ';
|
||||
ShowHistory = false;
|
||||
TierFactor = 0.0;
|
||||
CreditBonus = 0.0;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
MoneySymbol = '$';
|
||||
ShowHistory = false;
|
||||
TierFactor = 100.0;
|
||||
CreditBonus = 0.0;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(4);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 4:
|
||||
writer.Write(FallbackAccount);
|
||||
goto case 3;
|
||||
case 3:
|
||||
writer.Write(CreditBonus);
|
||||
goto case 2;
|
||||
case 2:
|
||||
WebForm.Serialize(writer);
|
||||
goto case 1;
|
||||
case 1:
|
||||
writer.Write(TierFactor);
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.Write(ShowHistory);
|
||||
writer.Write(MoneySymbol);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
WebForm = new DonationWebFormOptions();
|
||||
}
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 4:
|
||||
FallbackAccount = reader.ReadAccount();
|
||||
goto case 3;
|
||||
case 3:
|
||||
CreditBonus = reader.ReadDouble();
|
||||
goto case 2;
|
||||
case 2:
|
||||
WebForm = new DonationWebFormOptions(reader);
|
||||
goto case 1;
|
||||
case 1:
|
||||
TierFactor = reader.ReadDouble();
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
if (version < 2)
|
||||
{
|
||||
#region MySQL
|
||||
reader.ReadInt();
|
||||
reader.ReadInt();
|
||||
reader.ReadString();
|
||||
reader.ReadShort();
|
||||
reader.ReadString();
|
||||
reader.ReadString();
|
||||
reader.ReadString();
|
||||
reader.ReadByte();
|
||||
reader.ReadInt();
|
||||
reader.ReadString();
|
||||
reader.ReadString();
|
||||
#endregion
|
||||
|
||||
_CurrencyType = new ItemTypeSelectProperty(reader); // CurrencyType
|
||||
|
||||
reader.ReadString(); // TableName
|
||||
}
|
||||
|
||||
ShowHistory = reader.ReadBool();
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
CurrencyPrice = reader.ReadDouble(); // UnitPrice
|
||||
}
|
||||
|
||||
MoneySymbol = reader.ReadChar();
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
MoneyAbbr = reader.ReadString(); // MoneyAbbr
|
||||
reader.ReadBool(); // GiftingEnabled
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Info = new DonationStatistics();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.Text;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public sealed class DonationWebFormOptions : PropertyObject
|
||||
{
|
||||
private static readonly Dictionary<string, object> _DefOptions = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{"test", false},
|
||||
{"command", "_donations"},
|
||||
{"business", String.Empty},
|
||||
{"notifyUrl", String.Empty},
|
||||
{"returnUrl", String.Empty},
|
||||
{"verifyUrl", String.Empty},
|
||||
{"currency", "USD"},
|
||||
{"itemName", "Gold Coins"},
|
||||
{"itemType", "Gold"},
|
||||
{"itemValue", 1.00},
|
||||
{"amountDef", 25.00},
|
||||
{"amountMin", 5.00},
|
||||
{"amountMax", 500.00},
|
||||
{"amountInc", 1.00},
|
||||
{"buttonName", "Donate"},
|
||||
{"bannerUrl", String.Empty},
|
||||
{"bannerImg", String.Empty},
|
||||
{"shard", String.Empty}
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, object> _Options = new Dictionary<string, object>(_DefOptions, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public bool Test { get => (bool)_Options["test"]; set => _Options["test"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Command { get => (string)_Options["command"]; set => _Options["command"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Business { get => (string)_Options["business"]; set => _Options["business"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string NotifyUrl { get => (string)_Options["notifyUrl"]; set => _Options["notifyUrl"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string ReturnUrl { get => (string)_Options["returnUrl"]; set => _Options["returnUrl"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string VerifyUrl { get => (string)_Options["verifyUrl"]; set => _Options["verifyUrl"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Currency { get => (string)_Options["currency"]; set => _Options["currency"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string ItemName { get => (string)_Options["itemName"]; set => _Options["itemName"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string ItemType { get => (string)_Options["itemType"]; set => _Options["itemType"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double ItemValue { get => (double)_Options["itemValue"]; set => _Options["itemValue"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double AmountDef { get => (double)_Options["amountDef"]; set => _Options["amountDef"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double AmountMin { get => (double)_Options["amountMin"]; set => _Options["amountMin"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double AmountMax { get => (double)_Options["amountMax"]; set => _Options["amountMax"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public double AmountInc { get => (double)_Options["amountInc"]; set => _Options["amountInc"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string ButtonName { get => (string)_Options["buttonName"]; set => _Options["buttonName"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string BannerUrl { get => (string)_Options["bannerUrl"]; set => _Options["bannerUrl"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string BannerImg { get => (string)_Options["bannerImg"]; set => _Options["bannerImg"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public string Shard { get => (string)_Options["shard"]; set => _Options["shard"] = value; }
|
||||
|
||||
[CommandProperty(AutoDonate.Access)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public Action<StringBuilder> GenerationHandler { get; set; }
|
||||
|
||||
public DonationWebFormOptions()
|
||||
{ }
|
||||
|
||||
public DonationWebFormOptions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
foreach (var kv in _DefOptions)
|
||||
{
|
||||
_Options[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
foreach (var kv in _DefOptions)
|
||||
{
|
||||
_Options[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetJsonOptions()
|
||||
{
|
||||
return Json.Encode(_Options);
|
||||
}
|
||||
|
||||
public void SetJsonOptions(string json)
|
||||
{
|
||||
if (!Json.Decode(json, out var obj, out var e))
|
||||
{
|
||||
e.ToConsole();
|
||||
return;
|
||||
}
|
||||
|
||||
if (obj is Dictionary<string, object>)
|
||||
{
|
||||
foreach (var kv in (Dictionary<string, object>)obj)
|
||||
{
|
||||
_Options[kv.Key] = kv.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Generate()
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
html.AppendLine("<!DOCTYPE html>");
|
||||
html.AppendLine("<html>");
|
||||
html.AppendLine("\t<head>");
|
||||
html.AppendLine("\t\t<title>{0} - Donate</title>", Shard);
|
||||
html.AppendLine("\t\t<link rel='stylesheet' type='text/css' href='http://www.vita-nex.com/js/inc/index.css' />");
|
||||
html.AppendLine("\t\t<script type='text/javascript' src='http://www.vita-nex.com/js/mod/donateForm.js'></script>");
|
||||
html.AppendLine("\t</head>");
|
||||
html.AppendLine("\t<body>");
|
||||
html.AppendLine("\t\t<div id='index'>");
|
||||
html.AppendLine("\t\t\t<form class='donate-form' data-options='{0}'></form>", GetJsonOptions());
|
||||
html.AppendLine("\t\t</div>");
|
||||
html.AppendLine("\t</body>");
|
||||
html.AppendLine("</html>");
|
||||
|
||||
if (GenerationHandler != null)
|
||||
{
|
||||
GenerationHandler(html);
|
||||
}
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Enabled);
|
||||
|
||||
writer.Write(GetJsonOptions());
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Enabled = reader.ReadBool();
|
||||
|
||||
SetJsonOptions(reader.ReadString());
|
||||
}
|
||||
}
|
||||
}
|
||||
1893
Scripts/SubSystem/VitaNex/Core/Modules/AutoDonate/UI/AdminUI.cs
Normal file
1893
Scripts/SubSystem/VitaNex/Core/Modules/AutoDonate/UI/AdminUI.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,825 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
using Server.Gumps;
|
||||
|
||||
using VitaNex.Collections;
|
||||
using VitaNex.SuperGumps;
|
||||
using VitaNex.SuperGumps.UI;
|
||||
using VitaNex.Text;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public class DonationProfileUI : TreeGump
|
||||
{
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile, DonationTransaction trans)
|
||||
{
|
||||
DisplayTo(user, profile, false, trans);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile, bool refreshOnly, DonationTransaction trans)
|
||||
{
|
||||
var node = trans.IsPending
|
||||
? ("Transactions|Pending|" + trans.ID)
|
||||
: trans.IsProcessed //
|
||||
? ("Transactions|Claim|" + trans.ID)
|
||||
: !trans.Hidden //
|
||||
? ("History|" + trans.FullPath)
|
||||
: "History";
|
||||
|
||||
DisplayTo(user, profile, refreshOnly, node);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user)
|
||||
{
|
||||
DisplayTo(user, null);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile)
|
||||
{
|
||||
DisplayTo(user, profile, String.Empty);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile, string node)
|
||||
{
|
||||
DisplayTo(user, profile, false, node);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile, bool refreshOnly)
|
||||
{
|
||||
DisplayTo(user, profile, refreshOnly, String.Empty);
|
||||
}
|
||||
|
||||
public static void DisplayTo(Mobile user, DonationProfile profile, bool refreshOnly, string node)
|
||||
{
|
||||
var info = EnumerateInstances<DonationProfileUI>(user).FirstOrDefault(g => g != null && !g.IsDisposed && g.IsOpen);
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
if (refreshOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info = new DonationProfileUI(user, profile);
|
||||
}
|
||||
else if (profile != null)
|
||||
{
|
||||
info.Profile = profile;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(node))
|
||||
{
|
||||
info.SelectedNode = node;
|
||||
}
|
||||
|
||||
info.Refresh(true);
|
||||
}
|
||||
|
||||
private bool _Admin;
|
||||
|
||||
private int _DonationTier;
|
||||
private int _DonationCount;
|
||||
private double _DonationValue;
|
||||
private long _DonationCredit;
|
||||
|
||||
private int[,] _Indicies;
|
||||
private List<DonationTransaction> _Transactions;
|
||||
|
||||
public DonationProfile Profile { get; set; }
|
||||
|
||||
public DonationProfileUI(Mobile user, DonationProfile profile = null)
|
||||
: base(user, null, null, null, null, null, "Donations")
|
||||
{
|
||||
_Indicies = new int[6, 2];
|
||||
|
||||
Profile = profile;
|
||||
|
||||
CanMove = true;
|
||||
CanClose = true;
|
||||
CanDispose = true;
|
||||
CanResize = false;
|
||||
|
||||
Width = 900;
|
||||
Height = 500;
|
||||
|
||||
ForceRecompile = true;
|
||||
}
|
||||
|
||||
public override void AssignCollections()
|
||||
{
|
||||
base.AssignCollections();
|
||||
|
||||
if (_Transactions == null)
|
||||
{
|
||||
ObjectPool.Acquire(out _Transactions);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void MainButtonHandler(GumpButton b)
|
||||
{
|
||||
if (_Admin)
|
||||
{
|
||||
new DonationAdminUI(User, Hide()).Send();
|
||||
return;
|
||||
}
|
||||
|
||||
base.MainButtonHandler(b);
|
||||
}
|
||||
|
||||
protected override void Compile()
|
||||
{
|
||||
_Admin = User.AccessLevel >= AutoDonate.Access;
|
||||
|
||||
if (Profile == null || Profile.Account == null || (!_Admin && User.AccessLevel <= Profile.Account.AccessLevel &&
|
||||
!Profile.Account.IsSharedWith(User.Account)))
|
||||
{
|
||||
Profile = AutoDonate.EnsureProfile(User.Account);
|
||||
}
|
||||
|
||||
if (Profile != null)
|
||||
{
|
||||
_DonationTier = Profile.Tier;
|
||||
_DonationValue = Profile.TotalValue;
|
||||
_DonationCredit = Profile.TotalCredit;
|
||||
_DonationCount = _Admin ? Profile.Transactions.Count : Profile.Visible.Count();
|
||||
|
||||
Title = "Donations: " + Profile.Account;
|
||||
}
|
||||
|
||||
base.Compile();
|
||||
}
|
||||
|
||||
protected override void CompileNodes(Dictionary<TreeGumpNode, Action<Rectangle, int, TreeGumpNode>> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
list["Transactions"] = CompileTransactions;
|
||||
list["Transactions|Pending"] = CompileTransactions;
|
||||
list["Transactions|Claim"] = CompileTransactions;
|
||||
|
||||
var trans = _Admin ? Profile.Transactions.Values : Profile.Visible;
|
||||
|
||||
foreach (var t in trans)
|
||||
{
|
||||
if (t.IsPending)
|
||||
{
|
||||
list["Transactions|Pending|" + t.ID] = CompileTransaction;
|
||||
}
|
||||
else if (t.IsProcessed)
|
||||
{
|
||||
list["Transactions|Claim|" + t.ID] = CompileTransaction;
|
||||
}
|
||||
|
||||
TreeGumpNode n = t.FullPath;
|
||||
|
||||
list["History|" + n.FullName] = CompileTransaction;
|
||||
|
||||
foreach (var p in n.GetParents())
|
||||
{
|
||||
list["History|" + p] = CompileTransactions;
|
||||
}
|
||||
}
|
||||
|
||||
base.CompileNodes(list);
|
||||
}
|
||||
|
||||
protected override void CompileNodeLayout(
|
||||
SuperGumpLayout layout,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int h,
|
||||
int index,
|
||||
TreeGumpNode node)
|
||||
{
|
||||
base.CompileNodeLayout(layout, x, y, w, h, index, node);
|
||||
|
||||
if (Nodes == null || !Nodes.ContainsKey(node))
|
||||
{
|
||||
CompileEmptyNodeLayout(layout, x, y, w, h, index, node);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CompileEmptyNodeLayout(
|
||||
SuperGumpLayout layout,
|
||||
int x,
|
||||
int y,
|
||||
int w,
|
||||
int h,
|
||||
int index,
|
||||
TreeGumpNode node)
|
||||
{
|
||||
base.CompileEmptyNodeLayout(layout, x, y, w, h, index, node);
|
||||
|
||||
layout.Add("node/page/" + index, () => CompileOverview(new Rectangle(x, y, w, h), index, node));
|
||||
}
|
||||
|
||||
protected virtual void CompileOverview(Rectangle bounds, int index, TreeGumpNode node)
|
||||
{
|
||||
var info = new StringBuilder();
|
||||
|
||||
info.AppendLine(
|
||||
"Welcome to the Donation Exchange, {0}!",
|
||||
User.RawName.WrapUOHtmlColor(User.GetNotorietyColor(), HtmlColor));
|
||||
info.AppendLine();
|
||||
info.AppendLine("Select a category on the left to browse your transactions.");
|
||||
info.AppendLine();
|
||||
info.AppendLine("MY EXCHANGE".WrapUOHtmlBold().WrapUOHtmlColor(Color.Gold, false));
|
||||
info.AppendLine();
|
||||
|
||||
GetProfileOverview(info);
|
||||
|
||||
AddHtml(
|
||||
bounds.X + 5,
|
||||
bounds.Y,
|
||||
bounds.Width - 5,
|
||||
bounds.Height,
|
||||
info.ToString().WrapUOHtmlColor(HtmlColor),
|
||||
false,
|
||||
true);
|
||||
}
|
||||
|
||||
protected virtual void CompileTransactions(Rectangle b, int index, TreeGumpNode node)
|
||||
{
|
||||
_Transactions.Clear();
|
||||
|
||||
int idx;
|
||||
|
||||
var trans = _Admin ? Profile.Transactions.Values : Profile.Visible;
|
||||
|
||||
switch (node.Name)
|
||||
{
|
||||
case "Transactions":
|
||||
idx = 0;
|
||||
break;
|
||||
case "Claim":
|
||||
{
|
||||
trans = trans.Where(t => t.IsProcessed);
|
||||
|
||||
idx = 1;
|
||||
}
|
||||
break;
|
||||
case "Pending":
|
||||
{
|
||||
trans = trans.Where(t => t.IsPending);
|
||||
|
||||
idx = 2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
switch (node.Depth)
|
||||
{
|
||||
default: // History
|
||||
idx = 3;
|
||||
break;
|
||||
case 1: // Year
|
||||
{
|
||||
var y = Utility.ToInt32(node.Name);
|
||||
|
||||
trans = trans.Where(t => t.Time.Value.Year == y);
|
||||
|
||||
idx = 4;
|
||||
}
|
||||
break;
|
||||
case 2: // Month
|
||||
{
|
||||
var y = Utility.ToInt32(node.Parent.Name);
|
||||
var m = (Months)Enum.Parse(typeof(Months), node.Name);
|
||||
|
||||
trans = trans.Where(t => t.Time.Value.Year == y);
|
||||
trans = trans.Where(t => t.Time.Value.GetMonth() == m);
|
||||
|
||||
idx = 5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
_Transactions.AddRange(trans);
|
||||
_Transactions.Sort();
|
||||
|
||||
_Indicies[idx, 1] = _Transactions.Count;
|
||||
_Indicies[idx, 0] = Math.Max(0, Math.Min(_Indicies[idx, 1] - 1, _Indicies[idx, 0]));
|
||||
|
||||
_Transactions.TrimStart(_Indicies[idx, 0]);
|
||||
_Transactions.TrimEndTo((b.Height / 24) - 1);
|
||||
|
||||
// ID | Date | Recipient | Value | Credit | State
|
||||
var cols = new[] { -1, -1, -1, 80, 80, 80 };
|
||||
|
||||
AddTable(b.X, b.Y, b.Width - 25, b.Height, true, cols, _Transactions, 24, Color.Empty, 0, RenderTransaction);
|
||||
|
||||
_Transactions.Clear();
|
||||
|
||||
AddBackground(b.X + (b.Width - 25), b.Y, 28, b.Height, SupportsUltimaStore ? 40000 : 9260);
|
||||
|
||||
AddScrollbarV(
|
||||
b.X + (b.Width - 24),
|
||||
b.Y,
|
||||
b.Height,
|
||||
_Indicies[idx, 1],
|
||||
_Indicies[idx, 0],
|
||||
p =>
|
||||
{
|
||||
--_Indicies[idx, 0];
|
||||
Refresh(true);
|
||||
},
|
||||
n =>
|
||||
{
|
||||
++_Indicies[idx, 0];
|
||||
Refresh(true);
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void RenderTransaction(int x, int y, int w, int h, DonationTransaction t, int r, int c)
|
||||
{
|
||||
var bgCol = r % 2 != 0 ? Color.DarkSlateGray : Color.Black;
|
||||
var fgCol = r % 2 != 0 ? Color.White : Color.WhiteSmoke;
|
||||
|
||||
ApplyPadding(ref x, ref y, ref w, ref h, 2);
|
||||
|
||||
if (r < 0) // headers
|
||||
{
|
||||
var label = String.Empty;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case -1:
|
||||
AddHtml(x, y, w, h, label, fgCol, bgCol);
|
||||
break;
|
||||
case 0:
|
||||
label = "ID";
|
||||
goto case -1;
|
||||
case 1:
|
||||
label = "Date";
|
||||
goto case -1;
|
||||
case 2:
|
||||
label = "Recipient";
|
||||
goto case -1;
|
||||
case 3:
|
||||
label = AutoDonate.CMOptions.MoneySymbol + AutoDonate.CMOptions.MoneyAbbr;
|
||||
goto case -1;
|
||||
case 4:
|
||||
label = AutoDonate.CMOptions.CurrencyName;
|
||||
goto case -1;
|
||||
case 5:
|
||||
label = "Status";
|
||||
goto case -1;
|
||||
}
|
||||
}
|
||||
else if (t != null)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 0: // ID
|
||||
AddHtml(x, y, w, h, t.ID, fgCol, bgCol);
|
||||
break;
|
||||
case 1: // Date
|
||||
{
|
||||
AddHtml(x, y, w, h, t.Time.Value.ToSimpleString("m/d/y"), fgCol, bgCol);
|
||||
AddTooltip(t.Time.Value.ToSimpleString());
|
||||
}
|
||||
break;
|
||||
case 2: // Recipient
|
||||
{
|
||||
AddHtml(x, y, w, h, t.DeliveredTo ?? String.Empty, fgCol, bgCol);
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(t.DeliveredTo))
|
||||
{
|
||||
AddTooltip(t.DeliveryTime.Value.ToSimpleString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: // Value
|
||||
AddHtml(x, y, w, h, AutoDonate.CMOptions.MoneySymbol + t.Total.ToString("#,0.00"), fgCol, bgCol);
|
||||
break;
|
||||
case 4: // Credit
|
||||
{
|
||||
if (t.Bonus > 0)
|
||||
{
|
||||
AddHtml(x, y, w, h, String.Format("{0:#,0} +{1:#,0}", t.Credit, t.Bonus), fgCol, bgCol);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHtml(x, y, w, h, t.Credit.ToString("#,0"), fgCol, bgCol);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 5: // Status
|
||||
{
|
||||
string node;
|
||||
|
||||
if (t.IsPending)
|
||||
{
|
||||
node = "Transactions|Pending|" + t.ID;
|
||||
}
|
||||
else if (t.IsProcessed)
|
||||
{
|
||||
node = "Transactions|Claim|" + t.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = "History|" + t.FullPath;
|
||||
}
|
||||
|
||||
var label = String.Empty;
|
||||
var color = fgCol;
|
||||
|
||||
switch (t.State)
|
||||
{
|
||||
case TransactionState.Voided:
|
||||
{
|
||||
label = UniGlyph.CircleX.ToString();
|
||||
color = Color.IndianRed;
|
||||
}
|
||||
break;
|
||||
case TransactionState.Pending:
|
||||
{
|
||||
label = UniGlyph.Coffee.ToString();
|
||||
color = Color.Yellow;
|
||||
}
|
||||
break;
|
||||
case TransactionState.Processed:
|
||||
{
|
||||
label = UniGlyph.StarEmpty.ToString();
|
||||
color = Color.SkyBlue;
|
||||
}
|
||||
break;
|
||||
case TransactionState.Claimed:
|
||||
{
|
||||
label = UniGlyph.StarFill.ToString();
|
||||
color = Color.LawnGreen;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
label = label.WrapUOHtmlColor(color, fgCol);
|
||||
label += " " + t.State.ToString(true);
|
||||
|
||||
AddHtmlButton(x, y, w, h, b => SelectNode(node), label, fgCol, bgCol);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CompileTransaction(Rectangle b, int index, TreeGumpNode node)
|
||||
{
|
||||
var trans = Profile[node.Name];
|
||||
|
||||
if (trans == null)
|
||||
{
|
||||
CompileOverview(b, index, node);
|
||||
return;
|
||||
}
|
||||
|
||||
var cpHeight = _Admin ? 60 : 30;
|
||||
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetTransactionOverview(trans, html, true, true, true);
|
||||
|
||||
AddHtml(
|
||||
b.X + 5,
|
||||
b.Y,
|
||||
b.Width - 5,
|
||||
b.Height - cpHeight,
|
||||
html.ToString().WrapUOHtmlColor(Color.White, false),
|
||||
false,
|
||||
true);
|
||||
|
||||
var bw = b.Width;
|
||||
var bh = cpHeight;
|
||||
|
||||
if (_Admin)
|
||||
{
|
||||
bh /= 2;
|
||||
}
|
||||
|
||||
switch (trans.State)
|
||||
{
|
||||
case TransactionState.Voided:
|
||||
{
|
||||
AddHtmlButton(
|
||||
b.X,
|
||||
b.Y + (b.Height - bh),
|
||||
bw,
|
||||
bh,
|
||||
o => OnVoidedTransaction(trans),
|
||||
"[VOIDED]",
|
||||
Color.OrangeRed,
|
||||
Color.Black,
|
||||
Color.OrangeRed,
|
||||
2);
|
||||
}
|
||||
break;
|
||||
case TransactionState.Pending:
|
||||
{
|
||||
AddHtmlButton(
|
||||
b.X,
|
||||
b.Y + (b.Height - bh),
|
||||
bw,
|
||||
bh,
|
||||
o => OnPendingTransaction(trans),
|
||||
"[PENDING]",
|
||||
Color.Yellow,
|
||||
Color.Black,
|
||||
Color.Yellow,
|
||||
2);
|
||||
}
|
||||
break;
|
||||
case TransactionState.Processed:
|
||||
{
|
||||
AddHtmlButton(
|
||||
b.X,
|
||||
b.Y + (b.Height - bh),
|
||||
bw,
|
||||
bh,
|
||||
o => OnClaimTransaction(trans),
|
||||
"[CLAIM]",
|
||||
Color.SkyBlue,
|
||||
Color.Black,
|
||||
Color.SkyBlue,
|
||||
2);
|
||||
}
|
||||
break;
|
||||
case TransactionState.Claimed:
|
||||
{
|
||||
AddHtmlButton(
|
||||
b.X,
|
||||
b.Y + (b.Height - bh),
|
||||
bw,
|
||||
bh,
|
||||
o => OnClaimedTransaction(trans),
|
||||
"[CLAIMED]",
|
||||
Color.LawnGreen,
|
||||
Color.Black,
|
||||
Color.LawnGreen,
|
||||
2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (_Admin)
|
||||
{
|
||||
bw /= 2;
|
||||
|
||||
AddHtmlButton(
|
||||
b.X,
|
||||
b.Y + (b.Height - (bh * 2)),
|
||||
bw,
|
||||
bh,
|
||||
o => OnTransactionEdit(trans),
|
||||
"[EDIT]",
|
||||
Color.Gold,
|
||||
Color.Black,
|
||||
Color.Gold,
|
||||
2);
|
||||
|
||||
AddHtmlButton(
|
||||
b.X + bw,
|
||||
b.Y + (b.Height - (bh * 2)),
|
||||
bw,
|
||||
bh,
|
||||
o => OnTransactionTransfer(trans),
|
||||
"[TRANSFER]",
|
||||
Color.Gold,
|
||||
Color.Black,
|
||||
Color.Gold,
|
||||
2);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void GetTransactionOverview(
|
||||
DonationTransaction trans,
|
||||
StringBuilder info,
|
||||
bool details,
|
||||
bool exchange,
|
||||
bool status)
|
||||
{
|
||||
if (details)
|
||||
{
|
||||
info.AppendLine();
|
||||
info.AppendLine("Details");
|
||||
info.AppendLine();
|
||||
info.AppendLine("ID: {0}", trans.ID);
|
||||
info.AppendLine("Date: {0}", trans.Time.Value);
|
||||
|
||||
if (_Admin)
|
||||
{
|
||||
info.AppendLine();
|
||||
info.AppendLine("Notes: {0}", trans.Notes);
|
||||
info.AppendLine();
|
||||
info.AppendLine("Extra: {0}", trans.Extra);
|
||||
}
|
||||
}
|
||||
|
||||
if (exchange)
|
||||
{
|
||||
info.AppendLine();
|
||||
info.AppendLine("Exchange");
|
||||
info.AppendLine();
|
||||
info.AppendLine(
|
||||
"Value: {0}{1:#,0.00} {2}",
|
||||
AutoDonate.CMOptions.MoneySymbol,
|
||||
trans.Total,
|
||||
AutoDonate.CMOptions.MoneyAbbr);
|
||||
|
||||
var total = trans.GetCredit(Profile, out var credit, out var bonus);
|
||||
|
||||
info.AppendLine("Credit: {0:#,0} {1}", credit, AutoDonate.CMOptions.CurrencyName);
|
||||
info.AppendLine("Bonus: {0:#,0} {1}", bonus, AutoDonate.CMOptions.CurrencyName);
|
||||
info.AppendLine("Total: {0:#,0} {1}", total, AutoDonate.CMOptions.CurrencyName);
|
||||
}
|
||||
|
||||
if (status)
|
||||
{
|
||||
info.AppendLine();
|
||||
info.AppendLine("Status");
|
||||
info.AppendLine();
|
||||
info.AppendLine("State: {0}", trans.State);
|
||||
|
||||
switch (trans.State)
|
||||
{
|
||||
case TransactionState.Voided:
|
||||
info.AppendLine("Transaction has been voided.");
|
||||
break;
|
||||
case TransactionState.Pending:
|
||||
info.AppendLine("Transaction is pending verification.");
|
||||
break;
|
||||
case TransactionState.Processed:
|
||||
info.AppendLine("Transaction is complete and can be claimed.");
|
||||
break;
|
||||
case TransactionState.Claimed:
|
||||
{
|
||||
info.AppendLine("Transaction has been delivered.");
|
||||
info.AppendLine();
|
||||
info.AppendLine("Date: {0}", trans.DeliveryTime.Value);
|
||||
|
||||
if (trans.DeliveredTo != null)
|
||||
{
|
||||
info.AppendLine("Recipient: {0}", trans.DeliveredTo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetProfileOverview(StringBuilder info)
|
||||
{
|
||||
var ms = AutoDonate.CMOptions.MoneySymbol;
|
||||
var ma = AutoDonate.CMOptions.MoneyAbbr;
|
||||
var cn = AutoDonate.CMOptions.CurrencyName;
|
||||
|
||||
var val = _DonationTier.ToString("#,0").WrapUOHtmlColor(Color.LawnGreen, HtmlColor);
|
||||
info.AppendLine("Donation Tier: {0}", val);
|
||||
|
||||
val = _DonationValue.ToString("#,0.00").WrapUOHtmlColor(Color.LawnGreen, HtmlColor);
|
||||
info.AppendLine("Donations Total: {0}{1} {2}", ms, val, ma);
|
||||
|
||||
val = _DonationCredit.ToString("#,0").WrapUOHtmlColor(Color.LawnGreen, HtmlColor);
|
||||
info.AppendLine("Donations Claimed: {0} {1}", val, cn);
|
||||
}
|
||||
|
||||
protected virtual void OnTransactionEdit(DonationTransaction trans)
|
||||
{
|
||||
Refresh();
|
||||
|
||||
if (_Admin)
|
||||
{
|
||||
User.SendGump(new PropertiesGump(User, trans));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnTransactionTransfer(DonationTransaction trans)
|
||||
{
|
||||
new InputDialogGump(User, Refresh())
|
||||
{
|
||||
Title = "Transfer Transaction",
|
||||
Html = "Enter the account name of the recipient for the transfer.",
|
||||
InputText = trans.Account != null ? trans.Account.Username : String.Empty,
|
||||
Callback = (b, a) =>
|
||||
{
|
||||
if (User.AccessLevel >= AutoDonate.Access)
|
||||
{
|
||||
var acc = Accounts.GetAccount(a);
|
||||
|
||||
if (acc == null)
|
||||
{
|
||||
User.SendMessage(34, "The account '{0}' does not exist.", a);
|
||||
}
|
||||
else if (trans.Account == acc)
|
||||
{
|
||||
User.SendMessage(34, "The transaction is already bound to '{0}'", a);
|
||||
}
|
||||
else
|
||||
{
|
||||
trans.SetAccount(acc);
|
||||
User.SendMessage(85, "The transaction has been transferred to '{0}'", a);
|
||||
}
|
||||
}
|
||||
|
||||
Refresh(true);
|
||||
}
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected virtual void OnVoidedTransaction(DonationTransaction trans)
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetTransactionOverview(trans, html, false, false, true);
|
||||
|
||||
new NoticeDialogGump(User, Refresh())
|
||||
{
|
||||
Title = "Transaction Voided",
|
||||
Html = html.ToString()
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected virtual void OnPendingTransaction(DonationTransaction trans)
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetTransactionOverview(trans, html, false, false, true);
|
||||
|
||||
new NoticeDialogGump(User, Refresh())
|
||||
{
|
||||
Title = "Transaction Pending",
|
||||
Html = html.ToString()
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected virtual void OnClaimTransaction(DonationTransaction trans)
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetTransactionOverview(trans, html, false, true, false);
|
||||
|
||||
html.AppendLine();
|
||||
html.AppendLine("Click OK to claim this transaction!");
|
||||
|
||||
new ConfirmDialogGump(User, Refresh())
|
||||
{
|
||||
Title = "Reward Claim",
|
||||
Html = html.ToString(),
|
||||
AcceptHandler = b =>
|
||||
{
|
||||
if (trans.Claim(User))
|
||||
{
|
||||
SelectedNode = "Transactions|" + trans.FullPath;
|
||||
|
||||
User.SendMessage(85, "You claimed the transaction!");
|
||||
}
|
||||
|
||||
Refresh(true);
|
||||
}
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected virtual void OnClaimedTransaction(DonationTransaction trans)
|
||||
{
|
||||
var info = new StringBuilder();
|
||||
|
||||
GetTransactionOverview(trans, info, false, false, true);
|
||||
|
||||
new NoticeDialogGump(User, Refresh())
|
||||
{
|
||||
Title = "Reward Delivered",
|
||||
Html = info.ToString()
|
||||
}.Send();
|
||||
}
|
||||
|
||||
protected override void OnDisposed()
|
||||
{
|
||||
_Indicies = null;
|
||||
|
||||
ObjectPool.Free(ref _Transactions);
|
||||
|
||||
base.OnDisposed();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Misc;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoDonate
|
||||
{
|
||||
public static class DonationEvents
|
||||
{
|
||||
#region TransPending
|
||||
public delegate void TransPending(TransPendingEventArgs e);
|
||||
|
||||
public static event TransPending OnTransPending;
|
||||
|
||||
public static void InvokeTransPending(DonationTransaction trans)
|
||||
{
|
||||
if (OnTransPending != null)
|
||||
{
|
||||
OnTransPending(new TransPendingEventArgs(trans));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TransPendingEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
|
||||
public TransPendingEventArgs(DonationTransaction trans)
|
||||
{
|
||||
Transaction = trans;
|
||||
}
|
||||
}
|
||||
#endregion TransPending
|
||||
|
||||
#region TransVoided
|
||||
public delegate void TransVoided(TransVoidedEventArgs e);
|
||||
|
||||
public static event TransVoided OnTransVoided;
|
||||
|
||||
public static void InvokeTransVoided(DonationTransaction trans)
|
||||
{
|
||||
if (OnTransVoided != null)
|
||||
{
|
||||
OnTransVoided(new TransVoidedEventArgs(trans));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TransVoidedEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
|
||||
public TransVoidedEventArgs(DonationTransaction trans)
|
||||
{
|
||||
Transaction = trans;
|
||||
}
|
||||
}
|
||||
#endregion TransVoided
|
||||
|
||||
#region TransClaimed
|
||||
public delegate void TransClaimed(TransClaimedEventArgs e);
|
||||
|
||||
public static event TransClaimed OnTransClaimed;
|
||||
|
||||
public static void InvokeTransClaimed(DonationTransaction trans, Mobile deliverTo)
|
||||
{
|
||||
if (OnTransClaimed != null)
|
||||
{
|
||||
OnTransClaimed(new TransClaimedEventArgs(trans, deliverTo));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TransClaimedEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
|
||||
public Mobile DeliverTo { get; set; }
|
||||
|
||||
public TransClaimedEventArgs(DonationTransaction trans, Mobile deliverTo)
|
||||
{
|
||||
Transaction = trans;
|
||||
DeliverTo = deliverTo;
|
||||
}
|
||||
}
|
||||
#endregion TransClaimed
|
||||
|
||||
#region TransProcessed
|
||||
public delegate void TransProcessed(TransProcessedEventArgs e);
|
||||
|
||||
public static event TransProcessed OnTransProcessed;
|
||||
|
||||
public static void InvokeTransProcessed(DonationTransaction trans)
|
||||
{
|
||||
if (OnTransProcessed != null)
|
||||
{
|
||||
OnTransProcessed(new TransProcessedEventArgs(trans));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TransProcessedEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
|
||||
public TransProcessedEventArgs(DonationTransaction trans)
|
||||
{
|
||||
Transaction = trans;
|
||||
}
|
||||
}
|
||||
#endregion TransProcessed
|
||||
|
||||
#region StateChanged
|
||||
public delegate void StateChanged(StateChangedEventArgs e);
|
||||
|
||||
public static event StateChanged OnStateChanged;
|
||||
|
||||
public static void InvokeStateChanged(DonationTransaction trans, TransactionState oldState)
|
||||
{
|
||||
if (OnStateChanged != null)
|
||||
{
|
||||
OnStateChanged(new StateChangedEventArgs(trans, oldState));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StateChangedEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
public TransactionState OldState { get; private set; }
|
||||
|
||||
public StateChangedEventArgs(DonationTransaction trans, TransactionState oldState)
|
||||
{
|
||||
Transaction = trans;
|
||||
OldState = oldState;
|
||||
}
|
||||
}
|
||||
#endregion StateChanged
|
||||
|
||||
#region TransactionExchange
|
||||
public delegate void TransExchanger(TransExchangeEventArgs e);
|
||||
|
||||
public static event TransExchanger OnTransExchange;
|
||||
|
||||
public static long InvokeTransExchange(DonationTransaction trans, DonationProfile dp)
|
||||
{
|
||||
var e = new TransExchangeEventArgs(trans, dp);
|
||||
|
||||
if (OnTransExchange != null)
|
||||
{
|
||||
OnTransExchange(e);
|
||||
}
|
||||
|
||||
return e.Exchanged;
|
||||
}
|
||||
|
||||
public sealed class TransExchangeEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
public DonationProfile Profile { get; private set; }
|
||||
|
||||
public long Exchanged { get; set; }
|
||||
|
||||
public ulong Flags { get; set; }
|
||||
|
||||
public TransExchangeEventArgs(DonationTransaction trans, DonationProfile dp)
|
||||
{
|
||||
Transaction = trans;
|
||||
Profile = dp;
|
||||
|
||||
Exchanged = Transaction.Credit;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TransactionPack
|
||||
public delegate void TransPacker(TransPackEventArgs e);
|
||||
|
||||
public static event TransPacker OnTransPack;
|
||||
|
||||
public static Container InvokeTransPack(DonationTransaction trans, DonationProfile dp)
|
||||
{
|
||||
var cont = new Bag
|
||||
{
|
||||
Name = ServerList.ServerName + " Donation Rewards",
|
||||
Hue = 1152
|
||||
};
|
||||
|
||||
var e = new TransPackEventArgs(trans, dp, cont);
|
||||
|
||||
OnTransPack?.Invoke(e);
|
||||
|
||||
return e.Container;
|
||||
}
|
||||
|
||||
public sealed class TransPackEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
public DonationProfile Profile { get; private set; }
|
||||
|
||||
public Container Container { get; set; }
|
||||
|
||||
public ulong Flags { get; set; }
|
||||
|
||||
public TransPackEventArgs(DonationTransaction trans, DonationProfile dp, Container cont)
|
||||
{
|
||||
Transaction = trans;
|
||||
Profile = dp;
|
||||
|
||||
Container = cont;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TransactionDeleted
|
||||
public delegate void TransactionDeleted(TransactionDeletedEventArgs e);
|
||||
|
||||
public static event TransactionDeleted OnTransactionDeleted;
|
||||
|
||||
public static void InvokeTransactionDeleted(DonationTransaction trans)
|
||||
{
|
||||
if (OnTransactionDeleted != null)
|
||||
{
|
||||
OnTransactionDeleted(new TransactionDeletedEventArgs(trans));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TransactionDeletedEventArgs : EventArgs
|
||||
{
|
||||
public DonationTransaction Transaction { get; private set; }
|
||||
|
||||
public TransactionDeletedEventArgs(DonationTransaction trans)
|
||||
{
|
||||
Transaction = trans;
|
||||
}
|
||||
}
|
||||
#endregion StateChanged
|
||||
}
|
||||
}
|
||||
678
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/AutoPvP.cs
Normal file
678
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/AutoPvP.cs
Normal file
@@ -0,0 +1,678 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.IO;
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public delegate void SeasonChangedHandler(PvPSeason newSeason, PvPSeason oldSeason);
|
||||
|
||||
public static partial class AutoPvP
|
||||
{
|
||||
public const AccessLevel Access = AccessLevel.Seer;
|
||||
|
||||
public static ScheduleInfo DefaultSeasonSchedule = new ScheduleInfo(ScheduleMonths.All, ScheduleDays.Monday, ScheduleTimes.Midnight);
|
||||
|
||||
public static AutoPvPOptions CMOptions { get; private set; }
|
||||
|
||||
public static BinaryDataStore<int, PvPSeason> Seasons { get; private set; }
|
||||
public static BinaryDataStore<PlayerMobile, PvPProfile> Profiles { get; private set; }
|
||||
public static BinaryDirectoryDataStore<PvPSerial, PvPBattle> Battles { get; private set; }
|
||||
|
||||
public static Dictionary<IAccount, Timer> Deserters { get; private set; }
|
||||
|
||||
public static Type[] BattleTypes { get; set; }
|
||||
public static PvPScenario[] Scenarios { get; set; }
|
||||
|
||||
public static Schedule SeasonSchedule { get; set; }
|
||||
|
||||
public static PvPSeason CurrentSeason => EnsureSeason(CMOptions.Advanced.Seasons.CurrentSeason);
|
||||
public static DateTime NextSeasonTime => SeasonSchedule.NextGlobalTick ?? DateTime.UtcNow;
|
||||
|
||||
private static DateTime LastSort { get; set; }
|
||||
private static TimeSpan CacheDelay { get; set; }
|
||||
private static List<PvPProfile> CachedSort { get; set; }
|
||||
|
||||
public static event Action<PvPSeason> OnSeasonChanged;
|
||||
|
||||
public static event Action<PvPBattle, PvPTeam, PlayerMobile> OnQueueJoin;
|
||||
public static event Action<PvPBattle, PvPTeam, PlayerMobile> OnQueueLeave;
|
||||
public static event Action<PvPBattle, PvPTeam, PlayerMobile> OnQueueUpdate;
|
||||
|
||||
public static event Action<PvPBattle, PvPRegion, Mobile> OnEnterBattle;
|
||||
public static event Action<PvPBattle, PvPRegion, Mobile> OnExitBattle;
|
||||
|
||||
public static event Action<PvPBattle, string> OnBattleLocalBroadcast;
|
||||
public static event Action<PvPBattle, string> OnBattleWorldBroadcast;
|
||||
|
||||
public static event Action<PvPBattle> OnBattleStateChanged;
|
||||
|
||||
public static void ChangeSeason(Schedule schedule)
|
||||
{
|
||||
if (!CMOptions.ModuleEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CMOptions.Advanced.Seasons.SkippedTicks < CMOptions.Advanced.Seasons.SkipTicks)
|
||||
{
|
||||
++CMOptions.Advanced.Seasons.SkippedTicks;
|
||||
return;
|
||||
}
|
||||
|
||||
CMOptions.Advanced.Seasons.SkippedTicks = 0;
|
||||
|
||||
var old = CurrentSeason;
|
||||
|
||||
EnsureSeason(++CMOptions.Advanced.Seasons.CurrentSeason).Start();
|
||||
|
||||
old.End();
|
||||
|
||||
SeasonChanged(old);
|
||||
}
|
||||
|
||||
public static void SeasonChanged(PvPSeason old)
|
||||
{
|
||||
var idx = 0;
|
||||
|
||||
foreach (var profile in GetSortedProfiles(old).Where(o => o.Owner.AccessLevel <= AccessLevel.Player))
|
||||
{
|
||||
if (idx < CMOptions.Advanced.Seasons.TopListCount)
|
||||
{
|
||||
IssueWinnerRewards(old, ++idx, profile);
|
||||
}
|
||||
else if (idx < CMOptions.Advanced.Seasons.TopListCount + CMOptions.Advanced.Seasons.RunnersUpCount)
|
||||
{
|
||||
IssueLoserRewards(old, ++idx, profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (OnSeasonChanged != null)
|
||||
{
|
||||
OnSeasonChanged(old);
|
||||
}
|
||||
}
|
||||
|
||||
public static void IssueWinnerRewards(this PvPSeason season, int rank, PvPProfile profile)
|
||||
{
|
||||
var rewards = CMOptions.Advanced.Seasons.Rewards.Winner.GiveReward(profile.Owner);
|
||||
|
||||
if (rewards == null)
|
||||
{
|
||||
rewards = new List<Item>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var fmt = "{0} (Season {1} - Rank {2})";
|
||||
|
||||
rewards.ForEach(r => r.Name = String.Format(fmt, r.ResolveName(profile.Owner), season.Number, rank));
|
||||
}
|
||||
|
||||
if (!season.Winners.TryGetValue(profile.Owner, out var list) || list == null)
|
||||
{
|
||||
season.Winners[profile.Owner] = rewards;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.AddRange(rewards);
|
||||
|
||||
rewards.Free(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void IssueLoserRewards(this PvPSeason season, int rank, PvPProfile profile)
|
||||
{
|
||||
var rewards = CMOptions.Advanced.Seasons.Rewards.Loser.GiveReward(profile.Owner);
|
||||
|
||||
if (rewards == null)
|
||||
{
|
||||
rewards = new List<Item>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var fmt = "{0} (Season {1} - Rank {2})";
|
||||
|
||||
rewards.ForEach(r => r.Name = String.Format(fmt, r.ResolveName(profile.Owner), season.Number, rank));
|
||||
}
|
||||
|
||||
if (!season.Losers.TryGetValue(profile.Owner, out var list) || list == null)
|
||||
{
|
||||
season.Losers[profile.Owner] = rewards;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.AddRange(rewards);
|
||||
|
||||
rewards.Free(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static PvPSeason EnsureSeason(int num, bool replace = false)
|
||||
{
|
||||
if (!Seasons.TryGetValue(num, out var season) || season == null || replace)
|
||||
{
|
||||
Seasons[num] = season = new PvPSeason(num);
|
||||
}
|
||||
|
||||
return season;
|
||||
}
|
||||
|
||||
public static PvPProfile EnsureProfile(PlayerMobile pm, bool replace = false)
|
||||
{
|
||||
if (!Profiles.ContainsKey(pm))
|
||||
{
|
||||
Profiles.Add(pm, new PvPProfile(pm));
|
||||
}
|
||||
else if (replace || Profiles[pm] == null || Profiles[pm].Deleted)
|
||||
{
|
||||
Profiles[pm] = new PvPProfile(pm);
|
||||
}
|
||||
|
||||
return Profiles[pm];
|
||||
}
|
||||
|
||||
public static IEnumerable<PvPProfile> GetSortedProfiles(PvPSeason season = null)
|
||||
{
|
||||
return GetSortedProfiles(CMOptions.Advanced.Profiles.RankingOrder, season);
|
||||
}
|
||||
|
||||
public static IEnumerable<PvPProfile> GetSortedProfiles(IEnumerable<PvPProfile> profiles, PvPSeason season = null)
|
||||
{
|
||||
return GetSortedProfiles(CMOptions.Advanced.Profiles.RankingOrder, profiles, season);
|
||||
}
|
||||
|
||||
public static IEnumerable<PvPProfile> GetSortedProfiles(PvPProfileRankOrder order, PvPSeason season = null)
|
||||
{
|
||||
return GetSortedProfiles(order, null, season);
|
||||
}
|
||||
|
||||
public static IEnumerable<PvPProfile> GetSortedProfiles(PvPProfileRankOrder order, IEnumerable<PvPProfile> profiles, PvPSeason season = null)
|
||||
{
|
||||
if (profiles == null)
|
||||
{
|
||||
profiles = Profiles.Values;
|
||||
|
||||
if (Profiles.Count > 1024)
|
||||
{
|
||||
profiles = profiles.AsParallel();
|
||||
}
|
||||
}
|
||||
|
||||
return profiles.OrderByDescending(p => GetSortedValue(order, p, season));
|
||||
}
|
||||
|
||||
public static long GetSortedValue(PvPProfileRankOrder order, PvPProfile profile, PvPSeason season = null)
|
||||
{
|
||||
switch (order)
|
||||
{
|
||||
case PvPProfileRankOrder.Points:
|
||||
{
|
||||
if (season == null)
|
||||
{
|
||||
return profile.TotalPoints;
|
||||
}
|
||||
|
||||
return profile.History.EnsureEntry(season).Points;
|
||||
}
|
||||
case PvPProfileRankOrder.Wins:
|
||||
{
|
||||
if (season == null)
|
||||
{
|
||||
return profile.TotalWins;
|
||||
}
|
||||
|
||||
return profile.History.EnsureEntry(season).Wins;
|
||||
}
|
||||
case PvPProfileRankOrder.Kills:
|
||||
{
|
||||
if (season == null)
|
||||
{
|
||||
return profile.TotalKills;
|
||||
}
|
||||
|
||||
return profile.History.EnsureEntry(season).Kills;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void InvokeQueueJoin(PvPBattle battle, PvPTeam team, PlayerMobile m)
|
||||
{
|
||||
if (OnQueueJoin != null)
|
||||
{
|
||||
OnQueueJoin(battle, team, m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeQueueLeave(PvPBattle battle, PvPTeam team, PlayerMobile m)
|
||||
{
|
||||
if (OnQueueLeave != null)
|
||||
{
|
||||
OnQueueLeave(battle, team, m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeQueueUpdate(PvPBattle battle, PvPTeam team, PlayerMobile m)
|
||||
{
|
||||
if (OnQueueUpdate != null)
|
||||
{
|
||||
OnQueueUpdate(battle, team, m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeEnterBattle(PvPBattle battle, PvPRegion region, Mobile m)
|
||||
{
|
||||
if (OnEnterBattle != null)
|
||||
{
|
||||
OnEnterBattle(battle, region, m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeExitBattle(PvPBattle battle, PvPRegion region, Mobile m)
|
||||
{
|
||||
if (OnExitBattle != null)
|
||||
{
|
||||
OnExitBattle(battle, region, m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeBattleLocalBroadcast(PvPBattle battle, string message)
|
||||
{
|
||||
if (OnBattleLocalBroadcast != null)
|
||||
{
|
||||
OnBattleLocalBroadcast(battle, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeBattleWorldBroadcast(PvPBattle battle, string message)
|
||||
{
|
||||
if (OnBattleWorldBroadcast != null)
|
||||
{
|
||||
OnBattleWorldBroadcast(battle, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void InvokeBattleStateChanged(PvPBattle battle)
|
||||
{
|
||||
if (OnBattleStateChanged != null)
|
||||
{
|
||||
OnBattleStateChanged(battle);
|
||||
}
|
||||
}
|
||||
|
||||
public static PvPBattle FindBattleByID(int uid)
|
||||
{
|
||||
return Battles.Where(o => o.Key.ValueHash.Equals(uid)).Select(kvp => kvp.Value).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static PvPBattle FindBattleByID(PvPSerial serial)
|
||||
{
|
||||
return Battles.Where(o => o.Key.Equals(serial)).Select(kvp => kvp.Value).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static PvPBattle FindBattle(PlayerMobile pm)
|
||||
{
|
||||
return FindBattle<PvPBattle>(pm);
|
||||
}
|
||||
|
||||
public static T FindBattle<T>(PlayerMobile pm)
|
||||
where T : PvPBattle
|
||||
{
|
||||
if (IsParticipant(pm, out T battle) || IsSpectator(pm, out battle))
|
||||
{
|
||||
return battle;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static bool IsParticipant(PlayerMobile pm)
|
||||
{
|
||||
return pm != null && IsParticipant<PvPBattle>(pm);
|
||||
}
|
||||
|
||||
public static bool IsParticipant<T>(PlayerMobile pm)
|
||||
where T : PvPBattle
|
||||
{
|
||||
return pm != null && Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().Any(b => b.IsParticipant(pm));
|
||||
}
|
||||
|
||||
public static bool IsParticipant<T>(PlayerMobile pm, out T battle)
|
||||
where T : PvPBattle
|
||||
{
|
||||
battle = default(T);
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
battle = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().FirstOrDefault(b => b.IsParticipant(pm));
|
||||
|
||||
return battle != null;
|
||||
}
|
||||
|
||||
public static ILookup<PvPBattle, IEnumerable<PlayerMobile>> GetParticipants()
|
||||
{
|
||||
return GetParticipants<PvPBattle>();
|
||||
}
|
||||
|
||||
public static ILookup<T, IEnumerable<PlayerMobile>> GetParticipants<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.ToLookup(b => b, b => b.GetParticipants());
|
||||
}
|
||||
|
||||
public static ILookup<PvPBattle, int> CountParticipants()
|
||||
{
|
||||
return CountParticipants<PvPBattle>();
|
||||
}
|
||||
|
||||
public static ILookup<T, int> CountParticipants<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.ToLookup(b => b, b => b.GetParticipants().Count());
|
||||
}
|
||||
|
||||
public static int TotalParticipants()
|
||||
{
|
||||
return TotalParticipants<PvPBattle>();
|
||||
}
|
||||
|
||||
public static int TotalParticipants<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.Aggregate(0, (c, b) => c + b.GetParticipants().Count());
|
||||
}
|
||||
|
||||
public static bool IsSpectator(PlayerMobile pm)
|
||||
{
|
||||
return pm != null && IsSpectator<PvPBattle>(pm);
|
||||
}
|
||||
|
||||
public static bool IsSpectator<T>(PlayerMobile pm)
|
||||
where T : PvPBattle
|
||||
{
|
||||
return pm != null && Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().Any(b => b.IsSpectator(pm));
|
||||
}
|
||||
|
||||
public static bool IsSpectator<T>(PlayerMobile pm, out T battle)
|
||||
where T : PvPBattle
|
||||
{
|
||||
battle = default(T);
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
battle = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().FirstOrDefault(b => b.IsSpectator(pm));
|
||||
|
||||
return battle != null;
|
||||
}
|
||||
|
||||
public static ILookup<PvPBattle, IEnumerable<PlayerMobile>> GetSpectators()
|
||||
{
|
||||
return GetSpectators<PvPBattle>();
|
||||
}
|
||||
|
||||
public static ILookup<T, IEnumerable<PlayerMobile>> GetSpectators<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.ToLookup(b => b, b => b.GetSpectators());
|
||||
}
|
||||
|
||||
public static ILookup<PvPBattle, int> CountSpectators()
|
||||
{
|
||||
return CountSpectators<PvPBattle>();
|
||||
}
|
||||
|
||||
public static ILookup<T, int> CountSpectators<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.ToLookup(b => b, b => b.Spectators.Count);
|
||||
}
|
||||
|
||||
public static int TotalSpectators()
|
||||
{
|
||||
return TotalSpectators<PvPBattle>();
|
||||
}
|
||||
|
||||
public static int TotalSpectators<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.Aggregate(0, (c, b) => c + b.Spectators.Count);
|
||||
}
|
||||
|
||||
public static void AddDeserter(PlayerMobile pm)
|
||||
{
|
||||
AddDeserter(pm, true);
|
||||
}
|
||||
|
||||
public static void AddDeserter(PlayerMobile pm, bool message)
|
||||
{
|
||||
SetDeserter(pm, true, message);
|
||||
}
|
||||
|
||||
public static void RemoveDeserter(PlayerMobile pm)
|
||||
{
|
||||
RemoveDeserter(pm, true);
|
||||
}
|
||||
|
||||
public static void RemoveDeserter(PlayerMobile pm, bool message)
|
||||
{
|
||||
SetDeserter(pm, false, message);
|
||||
}
|
||||
|
||||
public static void SetDeserter(PlayerMobile pm, bool state, bool message)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var t = Deserters.GetValue(pm.Account);
|
||||
|
||||
if (t != null && t.Running)
|
||||
{
|
||||
t.Stop();
|
||||
}
|
||||
|
||||
if (state && CMOptions.Advanced.Misc.DeserterLockout > TimeSpan.Zero)
|
||||
{
|
||||
Deserters[pm.Account] = t = Timer.DelayCall(CMOptions.Advanced.Misc.DeserterLockout, RemoveDeserter, pm);
|
||||
|
||||
if (message)
|
||||
{
|
||||
pm.SendMessage(0x22, "You have deserted your team and must wait until you can join another battle.");
|
||||
}
|
||||
|
||||
if (!CMOptions.Advanced.Misc.DeserterAssoc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var a in pm.Account.FindSharedAccounts().Where(a => a != pm.Account && !Deserters.ContainsKey(a)))
|
||||
{
|
||||
Deserters[a] = t;
|
||||
|
||||
var p = a.GetOnlineMobile();
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.SendMessage(0x22, "{0} has deserted a battle!", pm.RawName);
|
||||
p.SendMessage(0x22, "You must wait until you can join a battle because you have associated accounts.");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Deserters.Remove(pm.Account) && message)
|
||||
{
|
||||
pm.SendMessage(0x55, "You are no longer known as a deserter and may now join battles.");
|
||||
}
|
||||
|
||||
if (t == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Deserters.RemoveRange(
|
||||
o =>
|
||||
{
|
||||
if (o.Value == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o.Value == t)
|
||||
{
|
||||
var p = o.Key.GetOnlineMobile();
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
p.SendMessage(0x55, "You are no longer associated with a deserter and may now join battles.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDeserter(PlayerMobile pm)
|
||||
{
|
||||
return pm != null && pm.Account != null && Deserters.GetValue(pm.Account) != null;
|
||||
}
|
||||
|
||||
public static IEnumerable<PvPBattle> GetBattles(params PvPBattleState[] states)
|
||||
{
|
||||
return GetBattles<PvPBattle>(states);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetBattles<T>(params PvPBattleState[] states)
|
||||
where T : PvPBattle
|
||||
{
|
||||
if (states == null || states.Length == 0)
|
||||
{
|
||||
return Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
}
|
||||
|
||||
return Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().Where(b => states.Contains(b.State));
|
||||
}
|
||||
|
||||
public static int CountBattles(params PvPBattleState[] states)
|
||||
{
|
||||
return CountBattles<PvPBattle>(states);
|
||||
}
|
||||
|
||||
public static int CountBattles<T>(params PvPBattleState[] states)
|
||||
where T : PvPBattle
|
||||
{
|
||||
if (states == null || states.Length == 0)
|
||||
{
|
||||
return Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().Count();
|
||||
}
|
||||
|
||||
return Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>().Count(b => states.Contains(b.State));
|
||||
}
|
||||
|
||||
public static void DeleteAllBattles()
|
||||
{
|
||||
Battles.Values.Where(b => b != null && !b.Deleted).ForEach(b => b.Delete());
|
||||
}
|
||||
|
||||
public static void InternalizeAllBattles()
|
||||
{
|
||||
Battles.Values.Where(b => b != null && !b.Deleted).ForEach(b => b.State = PvPBattleState.Internal);
|
||||
}
|
||||
|
||||
public static PvPBattle CreateBattle(PvPScenario scenario)
|
||||
{
|
||||
if (scenario == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var battle = scenario.CreateBattle();
|
||||
|
||||
Battles[battle.Serial] = battle;
|
||||
|
||||
battle.Init();
|
||||
|
||||
return battle;
|
||||
}
|
||||
|
||||
public static bool RemoveBattle(PvPBattle battle)
|
||||
{
|
||||
return battle != null && Battles.Remove(battle.Serial);
|
||||
}
|
||||
|
||||
public static bool RemoveProfile(PvPProfile profile)
|
||||
{
|
||||
if (profile != null && Profiles.GetValue(profile.Owner) == profile && Profiles.Remove(profile.Owner))
|
||||
{
|
||||
profile.Remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DeleteAllProfiles()
|
||||
{
|
||||
Profiles.Values.Where(p => p != null && !p.Deleted).ForEach(p => p.Delete());
|
||||
}
|
||||
|
||||
public static int TotalQueued()
|
||||
{
|
||||
return TotalQueued<PvPBattle>();
|
||||
}
|
||||
|
||||
public static int TotalQueued<T>()
|
||||
where T : PvPBattle
|
||||
{
|
||||
var battles = Battles.Values.Where(b => b != null && !b.Deleted).OfType<T>();
|
||||
|
||||
return battles.Aggregate(0, (c, b) => c + b.Queue.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
495
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/AutoPvP_Init.cs
Normal file
495
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/AutoPvP_Init.cs
Normal file
@@ -0,0 +1,495 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Accounting;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.IO;
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[CoreModule("Auto PvP", "3.1.0.0", false, TaskPriority.High)]
|
||||
public static partial class AutoPvP
|
||||
{
|
||||
static AutoPvP()
|
||||
{
|
||||
CMOptions = new AutoPvPOptions();
|
||||
|
||||
Scenarios = new PvPScenario[0];
|
||||
BattleTypes = typeof(PvPBattle).GetConstructableChildren();
|
||||
|
||||
SeasonSchedule = new Schedule(CMOptions.ModuleName + " Seasons", false, DefaultSeasonSchedule);
|
||||
|
||||
Seasons = new BinaryDataStore<int, PvPSeason>(VitaNexCore.SavesDirectory + "/AutoPvP", "Seasons")
|
||||
{
|
||||
OnSerialize = SerializeSeasons,
|
||||
OnDeserialize = DeserializeSeasons
|
||||
};
|
||||
|
||||
Battles = new BinaryDirectoryDataStore<PvPSerial, PvPBattle>(VitaNexCore.SavesDirectory + "/AutoPvP", "Battles", "pvp")
|
||||
{
|
||||
OnSerialize = SerializeBattle,
|
||||
OnDeserialize = DeserializeBattle
|
||||
};
|
||||
|
||||
Profiles = new BinaryDataStore<PlayerMobile, PvPProfile>(VitaNexCore.SavesDirectory + "/AutoPvP", "Profiles")
|
||||
{
|
||||
Async = true,
|
||||
OnSerialize = SerializeProfiles,
|
||||
OnDeserialize = DeserializeProfiles
|
||||
};
|
||||
|
||||
Deserters = new Dictionary<IAccount, Timer>();
|
||||
}
|
||||
|
||||
private static void CMConfig()
|
||||
{
|
||||
SeasonSchedule.OnGlobalTick += ChangeSeason;
|
||||
}
|
||||
|
||||
private static void CMEnabled()
|
||||
{
|
||||
PvPBattle.Bind();
|
||||
BattleNotoriety.Enable();
|
||||
}
|
||||
|
||||
private static void CMDisabled()
|
||||
{
|
||||
InternalizeAllBattles();
|
||||
|
||||
PvPBattle.Unbind();
|
||||
BattleNotoriety.Disable();
|
||||
}
|
||||
|
||||
private static void CMInvoke()
|
||||
{
|
||||
PvPBattle.Bind();
|
||||
BattleNotoriety.Enable();
|
||||
|
||||
var scenarios = new List<PvPScenario>();
|
||||
|
||||
foreach (var type in BattleTypes.Where(t => t != null))
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
() =>
|
||||
{
|
||||
var battle = type.CreateInstanceSafe<PvPBattle>();
|
||||
|
||||
if (battle == null)
|
||||
{
|
||||
throw new Exception("PvPBattle Type could not be constructed, requires a constructor with 0 arguments.");
|
||||
}
|
||||
|
||||
PvPScenario scenario = battle;
|
||||
scenarios.Add(scenario);
|
||||
battle.Delete();
|
||||
|
||||
CMOptions.ToConsole("Created scenario ({0}) '{1}'", scenario.TypeOf.Name, scenario.Name);
|
||||
},
|
||||
CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
Scenarios = scenarios.ToArray();
|
||||
scenarios.Clear();
|
||||
|
||||
foreach (var battle in Battles.Values.Where(b => b != null && !b.Deleted).ToArray())
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
battle.Init,
|
||||
ex =>
|
||||
{
|
||||
VitaNexCore.TryCatch(battle.Delete);
|
||||
|
||||
CMOptions.ToConsole("Failed to initialize battle #{0} '{1}'", battle.Serial, battle.Name);
|
||||
CMOptions.ToConsole(ex);
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var profile in Profiles.Values.Where(p => p != null && !p.Deleted).ToArray())
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
profile.Init,
|
||||
ex =>
|
||||
{
|
||||
VitaNexCore.TryCatch(profile.Delete);
|
||||
|
||||
CMOptions.ToConsole("Failed to initialize profile #{0} '{1}'", profile.Owner.Serial.Value, profile.Owner.RawName);
|
||||
CMOptions.ToConsole(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void CMSave()
|
||||
{
|
||||
Save();
|
||||
Sync();
|
||||
}
|
||||
|
||||
private static void CMLoad()
|
||||
{
|
||||
Load();
|
||||
Sync();
|
||||
}
|
||||
|
||||
public static void Save()
|
||||
{
|
||||
VitaNexCore.TryCatch(SaveSeasons, CMOptions.ToConsole);
|
||||
VitaNexCore.TryCatch(SaveBattles, CMOptions.ToConsole);
|
||||
VitaNexCore.TryCatch(SaveProfiles, CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
public static void SaveSeasons()
|
||||
{
|
||||
var result = Seasons.Export();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Seasons.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Seasons database has errors...");
|
||||
|
||||
Seasons.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Season count: {0:#,0}", Seasons.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveProfiles()
|
||||
{
|
||||
var result = Profiles.Export();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Profiles.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Profiles database has errors...");
|
||||
|
||||
Profiles.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Profile count: {0:#,0}", Profiles.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveBattles()
|
||||
{
|
||||
var result = Battles.Export();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Battles.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Battles database has errors...");
|
||||
|
||||
Battles.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Battle count: {0:#,0}", Battles.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
VitaNexCore.TryCatch(LoadSeasons, CMOptions.ToConsole);
|
||||
VitaNexCore.TryCatch(LoadBattles, CMOptions.ToConsole);
|
||||
VitaNexCore.TryCatch(LoadProfiles, CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
public static void LoadSeasons()
|
||||
{
|
||||
var result = Seasons.Import();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Seasons.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Seasons database has errors...");
|
||||
|
||||
Seasons.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Season count: {0:#,0}", Seasons.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadProfiles()
|
||||
{
|
||||
var result = Profiles.Import();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Profiles.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Profiles database has errors...");
|
||||
|
||||
Profiles.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Profile count: {0:#,0}", Profiles.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadBattles()
|
||||
{
|
||||
var result = Battles.Import();
|
||||
|
||||
CMOptions.ToConsole("Result: {0}", result.ToString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case DataStoreResult.Null:
|
||||
case DataStoreResult.Busy:
|
||||
case DataStoreResult.Error:
|
||||
{
|
||||
if (Battles.HasErrors)
|
||||
{
|
||||
CMOptions.ToConsole("Battles database has errors...");
|
||||
|
||||
Battles.Errors.ForEach(CMOptions.ToConsole);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DataStoreResult.OK:
|
||||
CMOptions.ToConsole("Battle count: {0:#,0}", Battles.Count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sync()
|
||||
{
|
||||
VitaNexCore.TryCatch(SyncSeasons, CMOptions.ToConsole);
|
||||
VitaNexCore.TryCatch(SyncBattles, CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
public static void SyncSeasons()
|
||||
{
|
||||
foreach (var season in Seasons.Values)
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
season.Sync,
|
||||
ex =>
|
||||
{
|
||||
CMOptions.ToConsole("Failed to sync season #{0}", season.Number);
|
||||
CMOptions.ToConsole(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void SyncBattles()
|
||||
{
|
||||
foreach (var battle in Battles.Values)
|
||||
{
|
||||
VitaNexCore.TryCatch(
|
||||
battle.Sync,
|
||||
ex =>
|
||||
{
|
||||
CMOptions.ToConsole("Failed to sync battle #{0} '{1}'", battle.Serial, battle.Name);
|
||||
CMOptions.ToConsole(ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SerializeSeasons(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockDictionary(
|
||||
Seasons,
|
||||
(w, key, val) =>
|
||||
{
|
||||
w.Write(key);
|
||||
w.WriteType(val, t => val.Serialize(w));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DeserializeSeasons(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var key = r.ReadInt();
|
||||
var val = r.ReadTypeCreate<PvPSeason>(r) ?? new PvPSeason(key);
|
||||
|
||||
return new KeyValuePair<int, PvPSeason>(key, val);
|
||||
},
|
||||
Seasons);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SerializeProfiles(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockDictionary(
|
||||
Profiles,
|
||||
(w, key, val) =>
|
||||
{
|
||||
w.Write(key);
|
||||
w.WriteType(val, t => val.Serialize(w));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool DeserializeProfiles(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var key = r.ReadMobile<PlayerMobile>();
|
||||
var val = r.ReadTypeCreate<PvPProfile>(r);
|
||||
|
||||
return new KeyValuePair<PlayerMobile, PvPProfile>(key, val);
|
||||
},
|
||||
Profiles);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SerializeBattle(GenericWriter writer, PvPSerial key, PvPBattle val)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlock(w => w.WriteType(key, t => key.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(val, t => val.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Tuple<PvPSerial, PvPBattle> DeserializeBattle(GenericReader reader)
|
||||
{
|
||||
PvPSerial key = null;
|
||||
PvPBattle val = null;
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
key = reader.ReadBlock(r => r.ReadTypeCreate<PvPSerial>(r));
|
||||
val = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattle>(r));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
if (val != null && val.Serial != null)
|
||||
{
|
||||
key = val.Serial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new Tuple<PvPSerial, PvPBattle>(key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class CTFBattle : PvPBattle
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual double FlagDamageInc { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual double FlagDamageIncMax { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int FlagCapturePoints { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int FlagReturnPoints { get; set; }
|
||||
|
||||
public CTFBattle()
|
||||
{
|
||||
Name = "Capture The Flag";
|
||||
Category = "Capture The Flag";
|
||||
Description = "Capture the enemy flag and return it to your podium to score points!" +
|
||||
"\nDefend your flag from the enemy, you can only capture their flag when your flag is on your podium.";
|
||||
|
||||
AddTeam(NameList.RandomName("daemon"), 1, 5, 0x22);
|
||||
AddTeam(NameList.RandomName("daemon"), 1, 5, 0x55);
|
||||
|
||||
Options.Missions.Enabled = true;
|
||||
Options.Missions.Team = new CTFBattleObjectives
|
||||
{
|
||||
FlagsCaptured = 5
|
||||
};
|
||||
|
||||
Schedule.Info.Months = ScheduleMonths.All;
|
||||
Schedule.Info.Days = ScheduleDays.All;
|
||||
Schedule.Info.Times = ScheduleTimes.EveryQuarterHour;
|
||||
|
||||
Options.Timing.QueuePeriod = TimeSpan.FromMinutes(5.0);
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(5.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(15.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(5.0);
|
||||
|
||||
Options.Rules.AllowBeneficial = true;
|
||||
Options.Rules.AllowHarmful = true;
|
||||
Options.Rules.AllowHousing = false;
|
||||
Options.Rules.AllowPets = false;
|
||||
Options.Rules.AllowSpawn = false;
|
||||
Options.Rules.AllowSpeech = true;
|
||||
Options.Rules.CanBeDamaged = true;
|
||||
Options.Rules.CanDamageEnemyTeam = true;
|
||||
Options.Rules.CanDamageOwnTeam = false;
|
||||
Options.Rules.CanDie = false;
|
||||
Options.Rules.CanHeal = true;
|
||||
Options.Rules.CanHealEnemyTeam = false;
|
||||
Options.Rules.CanHealOwnTeam = true;
|
||||
Options.Rules.CanMount = false;
|
||||
Options.Rules.CanFly = false;
|
||||
Options.Rules.CanResurrect = true;
|
||||
Options.Rules.CanUseStuckMenu = false;
|
||||
Options.Rules.CanEquip = true;
|
||||
}
|
||||
|
||||
public CTFBattle(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override bool Validate(Mobile viewer, List<string> errors, bool pop = true)
|
||||
{
|
||||
if (!base.Validate(viewer, errors, pop) && pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Teams.All(t => t is CTFTeam))
|
||||
{
|
||||
errors.Add("One or more teams are not of the CTFTeam type.");
|
||||
errors.Add("[Options] -> [View Teams]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool AddTeam(string name, int minCapacity, int capacity, int color)
|
||||
{
|
||||
return AddTeam(new CTFTeam(this, name, minCapacity, capacity, color));
|
||||
}
|
||||
|
||||
public override bool AddTeam(PvPTeam team)
|
||||
{
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (team is CTFTeam)
|
||||
{
|
||||
return base.AddTeam(team);
|
||||
}
|
||||
|
||||
var added = AddTeam(team.Name, team.MinCapacity, team.MinCapacity, team.Color);
|
||||
|
||||
team.Delete();
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
public virtual void OnFlagDropped(CTFFlag flag, PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
UpdateStatistics(enemyTeam, attacker, s => ++s["Flags Dropped"]);
|
||||
|
||||
PlaySound(746);
|
||||
|
||||
LocalBroadcast("[{0}]: {1} has dropped the flag of {2}!", enemyTeam.Name, attacker.Name, flag.Team.Name);
|
||||
}
|
||||
|
||||
public virtual void OnFlagCaptured(CTFFlag flag, PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
UpdateStatistics(enemyTeam, attacker, s => ++s["Flags Captured"]);
|
||||
|
||||
if (FlagCapturePoints > 0)
|
||||
{
|
||||
AwardPoints(attacker, FlagCapturePoints);
|
||||
}
|
||||
|
||||
PlaySound(747);
|
||||
|
||||
LocalBroadcast("[{0}]: {1} has captured the flag of {2}!", enemyTeam.Name, attacker.Name, flag.Team.Name);
|
||||
}
|
||||
|
||||
public virtual void OnFlagStolen(CTFFlag flag, PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
UpdateStatistics(enemyTeam, attacker, s => ++s["Flags Stolen"]);
|
||||
|
||||
PlaySound(748);
|
||||
|
||||
LocalBroadcast("[{0}]: {1} has stolen the flag of {2}!", enemyTeam.Name, attacker.Name, flag.Team.Name);
|
||||
}
|
||||
|
||||
public virtual void OnFlagReturned(CTFFlag flag, PlayerMobile defender)
|
||||
{
|
||||
UpdateStatistics(flag.Team, defender, s => ++s["Flags Returned"]);
|
||||
|
||||
if (FlagReturnPoints > 0)
|
||||
{
|
||||
AwardPoints(defender, FlagReturnPoints);
|
||||
}
|
||||
|
||||
PlaySound(749);
|
||||
|
||||
LocalBroadcast("[{0}]: {1} has returned the flag of {0}!", flag.Team.Name, defender.Name);
|
||||
}
|
||||
|
||||
public virtual void OnFlagTimeout(CTFFlag flag)
|
||||
{
|
||||
PlaySound(749);
|
||||
|
||||
LocalBroadcast("[{0}]: Flag has been returned to the base!", flag.Team.Name);
|
||||
}
|
||||
|
||||
public override bool CheckDamage(Mobile damaged, ref int damage)
|
||||
{
|
||||
if (!base.CheckDamage(damaged, ref damage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (damaged != null && damaged.Player && damaged.Backpack != null && damage > 0)
|
||||
{
|
||||
var flag = damaged.Backpack.FindItemByType<CTFFlag>();
|
||||
|
||||
if (flag != null)
|
||||
{
|
||||
damage += (int)(damage * flag.DamageInc);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(3);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
case 2:
|
||||
{
|
||||
writer.Write(FlagDamageInc);
|
||||
writer.Write(FlagDamageIncMax);
|
||||
}
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
writer.Write(FlagCapturePoints);
|
||||
writer.Write(FlagReturnPoints);
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
writer.Write(-1); // CapsToWin
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
if (!(Options.Missions.Team is CTFBattleObjectives))
|
||||
{
|
||||
Options.Missions.Enabled = true;
|
||||
|
||||
Options.Missions.Team = new CTFBattleObjectives
|
||||
{
|
||||
FlagsCaptured = 5
|
||||
};
|
||||
}
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
case 2:
|
||||
{
|
||||
FlagDamageInc = reader.ReadDouble();
|
||||
FlagDamageIncMax = reader.ReadDouble();
|
||||
}
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
FlagCapturePoints = reader.ReadInt();
|
||||
FlagReturnPoints = reader.ReadInt();
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
var capsToWin = reader.ReadInt();
|
||||
|
||||
if (capsToWin >= 0)
|
||||
{
|
||||
((CTFBattleObjectives)Options.Missions.Team).FlagsCaptured = capsToWin;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (version < 3)
|
||||
{
|
||||
RewardTeam = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class CTFBattleObjectives : PvPBattleObjectives
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long FlagsCaptured { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long FlagsDropped { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long FlagsStolen { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long FlagsReturned { get; set; }
|
||||
|
||||
public override bool IsEmpty => base.IsEmpty && FlagsCaptured + FlagsDropped + FlagsStolen + FlagsReturned <= 0;
|
||||
|
||||
public CTFBattleObjectives()
|
||||
{ }
|
||||
|
||||
public CTFBattleObjectives(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void SetDefaults()
|
||||
{
|
||||
base.SetDefaults();
|
||||
|
||||
FlagsCaptured = 0;
|
||||
FlagsDropped = 0;
|
||||
FlagsStolen = 0;
|
||||
FlagsReturned = 0;
|
||||
}
|
||||
|
||||
public override double ComputeScore(PvPTeam t, ref double min, ref double max, ref double total)
|
||||
{
|
||||
var score = base.ComputeScore(t, ref min, ref max, ref total);
|
||||
|
||||
if (t == null || t.Deleted)
|
||||
{
|
||||
return score;
|
||||
}
|
||||
|
||||
var ct = t as CTFTeam;
|
||||
|
||||
if (ct == null)
|
||||
{
|
||||
return score;
|
||||
}
|
||||
|
||||
double val;
|
||||
|
||||
if (FlagsCaptured > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, ct.GetTotalFlagsCaptured() / (double)FlagsCaptured);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsDropped > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, ct.GetTotalFlagsDropped() / (double)FlagsDropped);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsStolen > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, ct.GetTotalFlagsStolen() / (double)FlagsStolen);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsReturned > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, ct.GetTotalFlagsReturned() / (double)FlagsReturned);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public override double ComputeScore(PvPBattle b, PlayerMobile p, ref double min, ref double max, ref double total)
|
||||
{
|
||||
var score = base.ComputeScore(b, p, ref min, ref max, ref total);
|
||||
|
||||
if (b == null || b.Deleted)
|
||||
{
|
||||
return score;
|
||||
}
|
||||
|
||||
if (p == null || p.Deleted)
|
||||
{
|
||||
return score;
|
||||
}
|
||||
|
||||
double val;
|
||||
|
||||
if (FlagsCaptured > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o["Flags Captured"]) / (double)FlagsCaptured);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsDropped > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o["Flags Dropped"]) / (double)FlagsDropped);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsStolen > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o["Flags Stolen"]) / (double)FlagsStolen);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (FlagsReturned > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o["Flags Returned"]) / (double)FlagsReturned);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public override string GetStatus(PvPTeam t)
|
||||
{
|
||||
var status = base.GetStatus(t);
|
||||
|
||||
if (t == null || t.Deleted)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
var ct = t as CTFTeam;
|
||||
|
||||
if (ct == null)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
var lines = new StringBuilder(status);
|
||||
|
||||
if (FlagsCaptured > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Captured: {0:#,0} / {1:#,0}", ct.GetTotalFlagsCaptured(), FlagsCaptured);
|
||||
}
|
||||
|
||||
if (FlagsDropped > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Dropped: {0:#,0} / {1:#,0}", ct.GetTotalFlagsDropped(), FlagsDropped);
|
||||
}
|
||||
|
||||
if (FlagsStolen > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Stolen: {0:#,0} / {1:#,0}", ct.GetTotalFlagsStolen(), FlagsStolen);
|
||||
}
|
||||
|
||||
if (FlagsReturned > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Returned: {0:#,0} / {1:#,0}", ct.GetTotalFlagsReturned(), FlagsReturned);
|
||||
}
|
||||
|
||||
return lines.ToString();
|
||||
}
|
||||
|
||||
public override string GetStatus(PvPBattle b, PlayerMobile p)
|
||||
{
|
||||
var status = base.GetStatus(b, p);
|
||||
|
||||
if (b == null || b.Deleted)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
if (p == null || p.Deleted)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
var lines = new StringBuilder(status);
|
||||
|
||||
if (FlagsCaptured > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Captured: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o["Flags Captured"]), FlagsCaptured);
|
||||
}
|
||||
|
||||
if (FlagsDropped > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Dropped: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o["Flags Dropped"]), FlagsDropped);
|
||||
}
|
||||
|
||||
if (FlagsStolen > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Stolen: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o["Flags Stolen"]), FlagsStolen);
|
||||
}
|
||||
|
||||
if (FlagsReturned > 0)
|
||||
{
|
||||
lines.AppendLine("Flags Returned: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o["Flags Returned"]), FlagsReturned);
|
||||
}
|
||||
|
||||
return lines.ToString();
|
||||
}
|
||||
|
||||
public override void GetHtmlString(StringBuilder html)
|
||||
{
|
||||
base.GetHtmlString(html);
|
||||
|
||||
var len = html.Length;
|
||||
|
||||
if (FlagsCaptured > 0)
|
||||
{
|
||||
html.AppendLine("Flags Captured: {0:#,0}", FlagsCaptured);
|
||||
}
|
||||
|
||||
if (FlagsDropped > 0)
|
||||
{
|
||||
html.AppendLine("Flags Dropped: {0:#,0}", FlagsDropped);
|
||||
}
|
||||
|
||||
if (FlagsStolen > 0)
|
||||
{
|
||||
html.AppendLine("Flags Stolen: {0:#,0}", FlagsStolen);
|
||||
}
|
||||
|
||||
if (FlagsReturned > 0)
|
||||
{
|
||||
html.AppendLine("Flags Returned: {0:#,0}", FlagsReturned);
|
||||
}
|
||||
|
||||
if (len < html.Length)
|
||||
{
|
||||
html.Insert(len, String.Empty.WrapUOHtmlColor(Color.PaleGoldenrod, false));
|
||||
html.AppendLine(String.Empty.WrapUOHtmlColor(Color.White, false));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(FlagsCaptured);
|
||||
writer.Write(FlagsDropped);
|
||||
writer.Write(FlagsStolen);
|
||||
writer.Write(FlagsReturned);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
FlagsCaptured = reader.ReadLong();
|
||||
FlagsDropped = reader.ReadLong();
|
||||
FlagsStolen = reader.ReadLong();
|
||||
FlagsReturned = reader.ReadLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.FX;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class CTFFlag : Item
|
||||
{
|
||||
private static readonly TimeSpan SplitSecond = TimeSpan.FromMilliseconds(250.0);
|
||||
|
||||
private static readonly TimeSpan HalfSecond = TimeSpan.FromSeconds(0.5);
|
||||
private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1.0);
|
||||
private static readonly TimeSpan FiveSeconds = TimeSpan.FromSeconds(5.0);
|
||||
private static readonly TimeSpan TenSeconds = TimeSpan.FromSeconds(10.0);
|
||||
private static readonly TimeSpan TwentySeconds = TimeSpan.FromSeconds(20.0);
|
||||
|
||||
private DateTime _NextMultiUpdate = DateTime.UtcNow;
|
||||
private DateTime _NextFlagReturn = DateTime.UtcNow;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual CTFTeam Team { get; set; }
|
||||
|
||||
private PlayerMobile _Carrier;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PlayerMobile Carrier
|
||||
{
|
||||
get => _Carrier;
|
||||
set
|
||||
{
|
||||
if (_Carrier == null || _Carrier == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
Drop(_Carrier);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drop(_Carrier);
|
||||
Steal(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual double DamageInc { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual DateTime NextAssault { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual DateTime NextEffect { get; set; }
|
||||
|
||||
public override bool HandlesOnMovement => RootParent == null;
|
||||
public override bool Nontransferable => RootParent != null;
|
||||
public override bool IsVirtualItem => RootParent != null;
|
||||
|
||||
public CTFFlag(CTFTeam team)
|
||||
: base(8351)
|
||||
{
|
||||
Team = team;
|
||||
|
||||
Name = Team.Name;
|
||||
Hue = Team.Color;
|
||||
Weight = 10.1;
|
||||
Movable = false;
|
||||
NextAssault = NextEffect = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public CTFFlag(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
public CTFTeam GetTeam(PlayerMobile pm)
|
||||
{
|
||||
return Team.Battle.FindTeam<CTFTeam>(pm);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team.FlagPodium != null && !Team.FlagPodium.Deleted)
|
||||
{
|
||||
MoveToWorld(Team.FlagPodium.Location.Clone3D(0, 0, Team.FlagPodium.ItemData.Height + 5), Team.FlagPodium.Map);
|
||||
DamageInc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveToWorld(Team.HomeBase, Team.Battle.Options.Locations.Map);
|
||||
DamageInc = 0;
|
||||
}
|
||||
|
||||
NextAssault = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void Drop(PlayerMobile attacker)
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker == null || attacker.Deleted || attacker != _Carrier)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var enemy = GetTeam(attacker);
|
||||
|
||||
if (enemy != null)
|
||||
{
|
||||
attacker.SolidHueOverride = enemy.Color;
|
||||
}
|
||||
|
||||
NextAssault = DateTime.UtcNow + FiveSeconds;
|
||||
|
||||
_Carrier = null;
|
||||
|
||||
_NextFlagReturn = DateTime.UtcNow + TwentySeconds;
|
||||
|
||||
MoveToWorld(attacker.Location, attacker.Map);
|
||||
|
||||
Team.OnFlagDropped(attacker, enemy);
|
||||
|
||||
InvalidateCarrier();
|
||||
}
|
||||
|
||||
public void Steal(PlayerMobile attacker)
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker == null || attacker.Deleted || attacker == _Carrier || attacker.Backpack == null || attacker.Backpack.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NextAssault > DateTime.UtcNow)
|
||||
{
|
||||
attacker.SendMessage(54, "This flag cannot be picked up for another {0} seconds.", (NextAssault - DateTime.UtcNow).TotalSeconds);
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker.HasItem<CTFFlag>())
|
||||
{
|
||||
attacker.SendMessage("You may only carry one flag at any given time!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!attacker.Backpack.TryDropItem(attacker, this, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var enemy = GetTeam(attacker);
|
||||
|
||||
attacker.SolidHueOverride = 2498;
|
||||
|
||||
_Carrier = attacker;
|
||||
|
||||
Team.OnFlagStolen(attacker, enemy);
|
||||
|
||||
InvalidateCarrier();
|
||||
}
|
||||
|
||||
public void Capture(PlayerMobile attacker)
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker == null || attacker.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var enemy = GetTeam(attacker);
|
||||
|
||||
if (enemy != null)
|
||||
{
|
||||
attacker.SolidHueOverride = enemy.Color;
|
||||
}
|
||||
|
||||
Team.OnFlagCaptured(attacker, enemy);
|
||||
|
||||
InvalidateCarrier();
|
||||
|
||||
Delete();
|
||||
}
|
||||
|
||||
public void Return(PlayerMobile defender)
|
||||
{
|
||||
if (Team == null || Team.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (defender == null || defender.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team.FlagPodium != null && !Team.FlagPodium.Deleted)
|
||||
{
|
||||
MoveToWorld(Team.FlagPodium.Location.Clone3D(0, 0, Team.FlagPodium.ItemData.Height + 5), Team.FlagPodium.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveToWorld(Team.HomeBase, Team.Battle.Options.Locations.Map);
|
||||
}
|
||||
|
||||
NextAssault = DateTime.UtcNow;
|
||||
|
||||
DamageInc = 0;
|
||||
|
||||
Team.OnFlagReturned(defender);
|
||||
|
||||
InvalidateCarrier();
|
||||
}
|
||||
|
||||
public bool IsAtPodium()
|
||||
{
|
||||
return Carrier == null && (this.InRange2D(Team.FlagPodium, 0) || this.InRange2D(Team.HomeBase, 0));
|
||||
}
|
||||
|
||||
public void UpdateDamageIncrease()
|
||||
{
|
||||
if (DateTime.UtcNow < _NextMultiUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsAtPodium())
|
||||
{
|
||||
DamageInc = 0;
|
||||
}
|
||||
else if (Carrier != null && DamageInc < Team.CTFBattle.FlagDamageIncMax)
|
||||
{
|
||||
DamageInc += Team.CTFBattle.FlagDamageInc;
|
||||
_NextMultiUpdate = DateTime.UtcNow + TenSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckReset()
|
||||
{
|
||||
if (DateTime.UtcNow < _NextFlagReturn || Carrier != null || IsAtPodium())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Team.CTFBattle.OnFlagTimeout(this);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
#if NEWPARENT
|
||||
public override void OnParentDeleted(IEntity parent)
|
||||
#else
|
||||
public override void OnParentDeleted(object parent)
|
||||
#endif
|
||||
{
|
||||
Drop(parent as PlayerMobile);
|
||||
|
||||
base.OnParentDeleted(parent);
|
||||
}
|
||||
|
||||
public override DeathMoveResult OnParentDeath(Mobile parent)
|
||||
{
|
||||
Drop(parent as PlayerMobile);
|
||||
|
||||
return base.OnParentDeath(parent);
|
||||
}
|
||||
|
||||
public override void OnDoubleClick(Mobile m)
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted ||
|
||||
!this.CheckDoubleClick(m, true, false, 1) || !(m is PlayerMobile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = (PlayerMobile)m;
|
||||
var battle = Team.CTFBattle;
|
||||
|
||||
if (battle == null || !battle.IsParticipant(pm))
|
||||
{
|
||||
pm.SendMessage("You must be a participant to perform that action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team.IsMember(pm))
|
||||
{
|
||||
if (Team.FlagPodium != null && !Team.FlagPodium.Deleted)
|
||||
{
|
||||
if (!this.InRange3D(Team.FlagPodium, 1, -10, 10))
|
||||
{
|
||||
Return(pm);
|
||||
}
|
||||
}
|
||||
else if (!this.InRange3D(Team.HomeBase, 1, -10, 10))
|
||||
{
|
||||
Return(pm);
|
||||
}
|
||||
}
|
||||
else if (Carrier != pm)
|
||||
{
|
||||
Steal(pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Drop(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMovement(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
base.OnMovement(m, oldLocation);
|
||||
|
||||
if (Deleted || Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted || !(m is PlayerMobile) ||
|
||||
!this.CheckDoubleClick(m, false, false, 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = (PlayerMobile)m;
|
||||
var battle = Team.CTFBattle;
|
||||
|
||||
if (battle == null || !battle.IsParticipant(pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team.IsMember(pm))
|
||||
{
|
||||
if (Team.FlagPodium != null && !Team.FlagPodium.Deleted)
|
||||
{
|
||||
if (!this.InRange3D(Team.FlagPodium, 1, -10, 10))
|
||||
{
|
||||
Return(pm);
|
||||
}
|
||||
}
|
||||
else if (!this.InRange3D(Team.HomeBase, 1, -10, 10))
|
||||
{
|
||||
Return(pm);
|
||||
}
|
||||
}
|
||||
else if (Carrier != pm)
|
||||
{
|
||||
Steal(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateCarrier()
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
_Carrier = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team == null || Team.Deleted)
|
||||
{
|
||||
Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateCarryEffect();
|
||||
}
|
||||
|
||||
public void InvalidateCarryEffect()
|
||||
{
|
||||
if (Deleted || Carrier == null || Carrier.Deleted || !IsChildOf(Carrier.Backpack))
|
||||
{
|
||||
_Carrier = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow < NextEffect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
new EffectInfo(Carrier.Clone3D(0, 0, 22), Carrier.Map, ItemID, Team.Color).Send();
|
||||
|
||||
NextEffect = DateTime.UtcNow + SplitSecond;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
writer.Write(_Carrier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
_Carrier = reader.ReadMobile<PlayerMobile>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.FX;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class CTFPodium : Item
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual CTFTeam Team { get; set; }
|
||||
|
||||
public override bool HandlesOnMovement => true;
|
||||
|
||||
public CTFPodium(CTFTeam team)
|
||||
: base(16144)
|
||||
{
|
||||
Team = team;
|
||||
|
||||
Name = Team.Name;
|
||||
Hue = Team.Color;
|
||||
Movable = false;
|
||||
}
|
||||
|
||||
public CTFPodium(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
public override void OnDoubleClick(Mobile from)
|
||||
{
|
||||
if (this.CheckDoubleClick(from, true, false, 2))
|
||||
{
|
||||
CheckCapture(from as PlayerMobile);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMovement(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
base.OnMovement(m, oldLocation);
|
||||
|
||||
CheckCapture(m as PlayerMobile);
|
||||
}
|
||||
|
||||
public virtual void CheckCapture(PlayerMobile attacker)
|
||||
{
|
||||
if (Team == null || Team.Deleted || Team.Flag == null || Team.Flag.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (attacker == null || attacker.Deleted || !attacker.InRange3D(this, 2, -10, 10) || !Team.IsMember(attacker))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Team.Flag.Carrier != null && !Team.Flag.Carrier.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Team.Battle.ForEachTeam<CTFTeam>(
|
||||
t =>
|
||||
{
|
||||
if (t == Team || t.Flag == null || t.Flag.Deleted || t.Flag.Carrier != attacker)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
t.Flag.Capture(attacker);
|
||||
|
||||
ExplodeFX.Random.CreateInstance(this, Map, 3, 0, null, e => e.Hue = t.Color).Send();
|
||||
});
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,554 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class CTFTeam : PvPTeam
|
||||
{
|
||||
private static readonly TimeSpan _OneSecond = TimeSpan.FromSeconds(1.0);
|
||||
|
||||
private CTFPodium _FlagPodium;
|
||||
private bool _SolidHueOverride = true;
|
||||
|
||||
public Dictionary<PlayerMobile, int> Attackers { get; private set; }
|
||||
public Dictionary<PlayerMobile, int> Defenders { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public CTFBattle CTFBattle => Battle as CTFBattle;
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public CTFFlag Flag { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool SolidHueOverride
|
||||
{
|
||||
get => _SolidHueOverride;
|
||||
set
|
||||
{
|
||||
_SolidHueOverride = value;
|
||||
InvalidateSolidHueOverride();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual TimeSpan FlagRespawnDelay { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual CTFPodium FlagPodium
|
||||
{
|
||||
get => _FlagPodium;
|
||||
set
|
||||
{
|
||||
_FlagPodium = value;
|
||||
InvalidateFlagPodium();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public override Point3D HomeBase
|
||||
{
|
||||
get => base.HomeBase;
|
||||
set
|
||||
{
|
||||
base.HomeBase = value;
|
||||
InvalidateFlagPodium();
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public override Point3D SpawnPoint
|
||||
{
|
||||
get => base.SpawnPoint;
|
||||
set
|
||||
{
|
||||
base.SpawnPoint = value;
|
||||
InvalidateFlagPodium();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void EnsureConstructDefaults()
|
||||
{
|
||||
base.EnsureConstructDefaults();
|
||||
|
||||
Attackers = new Dictionary<PlayerMobile, int>();
|
||||
Defenders = new Dictionary<PlayerMobile, int>();
|
||||
}
|
||||
|
||||
public CTFTeam(PvPBattle battle, string name = "Team", int minCapacity = 0, int maxCapacity = 1, int color = 12)
|
||||
: base(battle, name, minCapacity, maxCapacity, color)
|
||||
{
|
||||
FlagRespawnDelay = TimeSpan.FromSeconds(10.0);
|
||||
|
||||
RespawnOnDeath = true;
|
||||
KickOnDeath = false;
|
||||
}
|
||||
|
||||
public CTFTeam(PvPBattle battle, GenericReader reader)
|
||||
: base(battle, reader)
|
||||
{ }
|
||||
|
||||
protected override void OnMicroSync()
|
||||
{
|
||||
base.OnMicroSync();
|
||||
|
||||
if (Flag == null || Flag.Deleted)
|
||||
{
|
||||
Flag = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Battle == null || Battle.Deleted || Battle.Hidden || Battle.IsInternal)
|
||||
{
|
||||
Flag.Delete();
|
||||
Flag = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Flag.InvalidateCarrier();
|
||||
|
||||
if (Flag.IsAtPodium())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Flag.UpdateDamageIncrease();
|
||||
Flag.CheckReset();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
if (Flag != null)
|
||||
{
|
||||
Flag.Delete();
|
||||
Flag = null;
|
||||
}
|
||||
|
||||
if (Attackers != null)
|
||||
{
|
||||
Attackers.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Attackers = new Dictionary<PlayerMobile, int>();
|
||||
}
|
||||
|
||||
if (Defenders != null)
|
||||
{
|
||||
Defenders.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Defenders = new Dictionary<PlayerMobile, int>();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateFlagPodium()
|
||||
{
|
||||
if (Deserializing || SpawnPoint == Point3D.Zero || Battle.Options.Locations.Map == null ||
|
||||
Battle.Options.Locations.Map == Map.Internal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (FlagPodium == null || FlagPodium.Deleted)
|
||||
{
|
||||
FlagPodium = new CTFPodium(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
FlagPodium.Hue = Color;
|
||||
FlagPodium.Name = Name;
|
||||
}
|
||||
|
||||
FlagPodium.MoveToWorld(SpawnPoint, Battle.Options.Locations.Map);
|
||||
}
|
||||
|
||||
public virtual void InvalidateSolidHueOverride()
|
||||
{
|
||||
if (!Deserializing)
|
||||
{
|
||||
ForEachMember(InvalidateSolidHueOverride);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateSolidHueOverride(PlayerMobile pm)
|
||||
{
|
||||
if (!Deserializing && pm != null && !pm.Deleted && IsMember(pm) && pm.InRegion(Battle.BattleRegion))
|
||||
{
|
||||
if ((Battle.IsPreparing || Battle.IsRunning) && _SolidHueOverride)
|
||||
{
|
||||
pm.SolidHueOverride = Color;
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SolidHueOverride = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnMemberAdded(PlayerMobile pm)
|
||||
{
|
||||
base.OnMemberAdded(pm);
|
||||
|
||||
InvalidateSolidHueOverride(pm);
|
||||
}
|
||||
|
||||
public override void OnMemberRemoved(PlayerMobile pm)
|
||||
{
|
||||
base.OnMemberRemoved(pm);
|
||||
|
||||
if (Battle != null && !Battle.Deleted)
|
||||
{
|
||||
Battle.ForEachTeam<CTFTeam>(
|
||||
t =>
|
||||
{
|
||||
if (t.Flag != null && !t.Flag.Deleted && t.Flag.Carrier == pm)
|
||||
{
|
||||
t.Flag.Carrier = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
InvalidateSolidHueOverride(pm);
|
||||
}
|
||||
|
||||
public override void OnBattleOpened()
|
||||
{
|
||||
base.OnBattleOpened();
|
||||
|
||||
InvalidateFlagPodium();
|
||||
InvalidateSolidHueOverride();
|
||||
}
|
||||
|
||||
public override void OnBattlePreparing()
|
||||
{
|
||||
base.OnBattlePreparing();
|
||||
|
||||
InvalidateFlagPodium();
|
||||
InvalidateSolidHueOverride();
|
||||
}
|
||||
|
||||
public override void OnBattleStarted()
|
||||
{
|
||||
base.OnBattleStarted();
|
||||
|
||||
InvalidateFlagPodium();
|
||||
InvalidateSolidHueOverride();
|
||||
|
||||
if (Flag == null || Flag.Deleted)
|
||||
{
|
||||
RespawnFlag();
|
||||
}
|
||||
else
|
||||
{
|
||||
Flag.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnBattleEnded()
|
||||
{
|
||||
base.OnBattleEnded();
|
||||
|
||||
InvalidateFlagPodium();
|
||||
InvalidateSolidHueOverride();
|
||||
}
|
||||
|
||||
public override void OnMemberDeath(PlayerMobile pm)
|
||||
{
|
||||
if (Battle != null && !Battle.Deleted)
|
||||
{
|
||||
Battle.ForEachTeam<CTFTeam>(
|
||||
t =>
|
||||
{
|
||||
if (t.Flag != null && !t.Flag.Deleted && t.Flag.Carrier == pm)
|
||||
{
|
||||
t.Flag.Carrier = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
InvalidateSolidHueOverride(pm);
|
||||
|
||||
base.OnMemberDeath(pm);
|
||||
}
|
||||
|
||||
public virtual void SpawnFlag()
|
||||
{
|
||||
if (!Battle.IsRunning)
|
||||
{
|
||||
if (Flag != null && !Flag.Deleted)
|
||||
{
|
||||
Flag.Delete();
|
||||
}
|
||||
|
||||
Flag = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Flag == null || Flag.Deleted)
|
||||
{
|
||||
Flag = new CTFFlag(this);
|
||||
}
|
||||
|
||||
Flag.Carrier = null;
|
||||
Flag.Reset();
|
||||
}
|
||||
|
||||
public virtual void RespawnFlag()
|
||||
{
|
||||
Timer.DelayCall(FlagRespawnDelay, SpawnFlag);
|
||||
}
|
||||
|
||||
public virtual void OnFlagDropped(PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
if (attacker == null || enemyTeam == null || Flag == null || Flag.Deleted || CTFBattle == null || CTFBattle.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Broadcast("[{0}]: {1} has dropped your flag!", enemyTeam.Name, attacker.RawName);
|
||||
|
||||
CTFBattle.OnFlagDropped(Flag, attacker, enemyTeam);
|
||||
}
|
||||
|
||||
public virtual void OnFlagCaptured(PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
if (attacker == null || enemyTeam == null || Flag == null || Flag.Deleted || CTFBattle == null || CTFBattle.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (enemyTeam.Attackers.ContainsKey(attacker))
|
||||
{
|
||||
enemyTeam.Attackers[attacker]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
enemyTeam.Attackers.Add(attacker, 1);
|
||||
}
|
||||
|
||||
Broadcast("[{0}]: {1} has captured your flag!", enemyTeam.Name, attacker.RawName);
|
||||
|
||||
CTFBattle.OnFlagCaptured(Flag, attacker, enemyTeam);
|
||||
|
||||
RespawnFlag();
|
||||
}
|
||||
|
||||
public virtual void OnFlagStolen(PlayerMobile attacker, CTFTeam enemyTeam)
|
||||
{
|
||||
if (attacker == null || enemyTeam == null || Flag == null || Flag.Deleted || CTFBattle == null || CTFBattle.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Broadcast("[{0}]: {1} has stolen your flag!", enemyTeam.Name, attacker.RawName);
|
||||
|
||||
CTFBattle.OnFlagStolen(Flag, attacker, enemyTeam);
|
||||
}
|
||||
|
||||
public virtual void OnFlagReturned(PlayerMobile defender)
|
||||
{
|
||||
if (defender == null || Flag == null || Flag.Deleted || CTFBattle == null || CTFBattle.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Defenders.ContainsKey(defender))
|
||||
{
|
||||
Defenders[defender]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Defenders.Add(defender, 1);
|
||||
}
|
||||
|
||||
Broadcast("[{0}]: {1} has returned your flag!", Name, defender.RawName);
|
||||
|
||||
CTFBattle.OnFlagReturned(Flag, defender);
|
||||
}
|
||||
|
||||
public long GetTotalFlagsCaptured()
|
||||
{
|
||||
return Statistics.Values.Aggregate(0L, (v, o) => v + o["Flags Captured"]);
|
||||
}
|
||||
|
||||
public long GetTotalFlagsDropped()
|
||||
{
|
||||
return Statistics.Values.Aggregate(0L, (v, o) => v + o["Flags Dropped"]);
|
||||
}
|
||||
|
||||
public long GetTotalFlagsStolen()
|
||||
{
|
||||
return Statistics.Values.Aggregate(0L, (v, o) => v + o["Flags Stolen"]);
|
||||
}
|
||||
|
||||
public long GetTotalFlagsReturned()
|
||||
{
|
||||
return Statistics.Values.Aggregate(0L, (v, o) => v + o["Flags Returned"]);
|
||||
}
|
||||
|
||||
protected override void OnDeleted()
|
||||
{
|
||||
if (Flag != null)
|
||||
{
|
||||
Flag.Delete();
|
||||
Flag = null;
|
||||
}
|
||||
|
||||
if (FlagPodium != null)
|
||||
{
|
||||
FlagPodium.Delete();
|
||||
FlagPodium = null;
|
||||
}
|
||||
|
||||
base.OnDeleted();
|
||||
}
|
||||
|
||||
public override void GetHtmlStatistics(Mobile viewer, StringBuilder html)
|
||||
{
|
||||
base.GetHtmlStatistics(viewer, html);
|
||||
|
||||
html.AppendLine("----------");
|
||||
html.AppendLine("Flags Captured: {0:#,0}", GetTotalFlagsCaptured());
|
||||
html.AppendLine("Flags Dropped: {0:#,0}", GetTotalFlagsDropped());
|
||||
html.AppendLine("Flags Stolen: {0:#,0}", GetTotalFlagsStolen());
|
||||
html.AppendLine("Flags Returned: {0:#,0}", GetTotalFlagsReturned());
|
||||
|
||||
html.AppendLine();
|
||||
|
||||
html.Append(String.Empty.WrapUOHtmlColor(System.Drawing.Color.LawnGreen, false));
|
||||
html.AppendLine("Defenders:");
|
||||
html.AppendLine();
|
||||
|
||||
string t;
|
||||
|
||||
var i = 0;
|
||||
|
||||
foreach (var o in Defenders.OrderBy(kv => kv.Value))
|
||||
{
|
||||
t = String.Format("{0:#,0}: {1} ({2:#,0} Returns)", ++i, o.Key.RawName, o.Value);
|
||||
t = t.WrapUOHtmlColor(viewer.GetNotorietyColor(o.Key), SuperGump.DefaultHtmlColor);
|
||||
|
||||
html.AppendLine(t);
|
||||
}
|
||||
|
||||
html.AppendLine();
|
||||
|
||||
html.Append(String.Empty.WrapUOHtmlColor(System.Drawing.Color.OrangeRed, false));
|
||||
html.AppendLine("Attackers:");
|
||||
html.AppendLine();
|
||||
|
||||
i = 0;
|
||||
|
||||
foreach (var o in Attackers.OrderBy(kv => kv.Value))
|
||||
{
|
||||
t = String.Format("{0:#,0}: {1} ({2:#,0} Captures)", ++i, o.Key.RawName, o.Value);
|
||||
t = t.WrapUOHtmlColor(viewer.GetNotorietyColor(o.Key), SuperGump.DefaultHtmlColor);
|
||||
|
||||
html.AppendLine(t);
|
||||
}
|
||||
|
||||
html.Append(String.Empty.WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false));
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Flag);
|
||||
writer.Write(FlagPodium);
|
||||
writer.Write(-1); // Caps
|
||||
|
||||
writer.Write(FlagRespawnDelay);
|
||||
writer.Write(SolidHueOverride);
|
||||
|
||||
writer.WriteDictionary(
|
||||
Attackers,
|
||||
(w, m, c) =>
|
||||
{
|
||||
w.Write(m);
|
||||
w.Write(c);
|
||||
});
|
||||
|
||||
writer.WriteDictionary(
|
||||
Defenders,
|
||||
(w, m, c) =>
|
||||
{
|
||||
w.Write(m);
|
||||
w.Write(c);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Flag = reader.ReadItem<CTFFlag>();
|
||||
|
||||
if (Flag != null)
|
||||
{
|
||||
Flag.Team = this;
|
||||
}
|
||||
|
||||
FlagPodium = reader.ReadItem<CTFPodium>();
|
||||
|
||||
if (FlagPodium != null)
|
||||
{
|
||||
FlagPodium.Team = this;
|
||||
}
|
||||
|
||||
reader.ReadInt(); // Caps
|
||||
|
||||
FlagRespawnDelay = reader.ReadTimeSpan();
|
||||
SolidHueOverride = reader.ReadBool();
|
||||
|
||||
reader.ReadDictionary(
|
||||
r => new KeyValuePair<PlayerMobile, int>(r.ReadMobile<PlayerMobile>(), r.ReadInt()),
|
||||
Attackers);
|
||||
|
||||
reader.ReadDictionary(
|
||||
r => new KeyValuePair<PlayerMobile, int>(r.ReadMobile<PlayerMobile>(), r.ReadInt()),
|
||||
Defenders);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class FFABattle : PvPBattle
|
||||
{
|
||||
public FFABattle()
|
||||
{
|
||||
Name = "Free For All";
|
||||
Category = "Free For All";
|
||||
Description = "The last participant alive wins!";
|
||||
|
||||
AddTeam(NameList.RandomName("daemon"), 5, 40, 85);
|
||||
|
||||
Schedule.Info.Months = ScheduleMonths.All;
|
||||
Schedule.Info.Days = ScheduleDays.All;
|
||||
Schedule.Info.Times = ScheduleTimes.EveryHour;
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(5.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(15.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(5.0);
|
||||
|
||||
Options.Rules.AllowBeneficial = true;
|
||||
Options.Rules.AllowHarmful = true;
|
||||
Options.Rules.AllowHousing = false;
|
||||
Options.Rules.AllowPets = false;
|
||||
Options.Rules.AllowSpawn = false;
|
||||
Options.Rules.AllowSpeech = true;
|
||||
Options.Rules.CanBeDamaged = true;
|
||||
Options.Rules.CanDamageEnemyTeam = true;
|
||||
Options.Rules.CanDamageOwnTeam = true;
|
||||
Options.Rules.CanDie = false;
|
||||
Options.Rules.CanHeal = true;
|
||||
Options.Rules.CanHealEnemyTeam = false;
|
||||
Options.Rules.CanHealOwnTeam = false;
|
||||
Options.Rules.CanMount = false;
|
||||
Options.Rules.CanFly = false;
|
||||
Options.Rules.CanResurrect = false;
|
||||
Options.Rules.CanUseStuckMenu = false;
|
||||
}
|
||||
|
||||
public FFABattle(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public sealed class PvPCustomBattle : PvPBattle
|
||||
{
|
||||
public PvPCustomBattle()
|
||||
{
|
||||
Name = "Generic Battle";
|
||||
Description =
|
||||
"This generic battle serves as a template to create standard battles with general rules, using no advanced features.";
|
||||
|
||||
AddTeam("Team Alpha", 5, 10, 11);
|
||||
AddTeam("Team Bravo", 5, 10, 22);
|
||||
AddTeam("Team Gamma", 5, 10, 33);
|
||||
AddTeam("Team Omega", 5, 10, 44);
|
||||
}
|
||||
|
||||
public PvPCustomBattle(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public sealed class TournamentArchive : PropertyObject, IEnumerable<TournamentMatch>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public List<TournamentMatch> Matches { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int Count => Matches.Count;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public string Name { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public DateTime Date { get; private set; }
|
||||
|
||||
public TournamentArchive(string name, DateTime date)
|
||||
{
|
||||
Name = name;
|
||||
Date = date;
|
||||
|
||||
Matches = new List<TournamentMatch>();
|
||||
}
|
||||
|
||||
public TournamentArchive(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public IEnumerator<TournamentMatch> GetEnumerator()
|
||||
{
|
||||
return Matches.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Name);
|
||||
writer.Write(Date);
|
||||
|
||||
writer.WriteList(Matches, (w, o) => o.Serialize(w));
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Name = reader.ReadString();
|
||||
Date = reader.ReadDateTime();
|
||||
|
||||
Matches = reader.ReadList(r => new TournamentMatch(r), Matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TournamentArchivesUI : ListGump<TournamentArchive>
|
||||
{
|
||||
public TournamentArchivesUI(Mobile user, Gump parent = null)
|
||||
: base(user, parent)
|
||||
{ }
|
||||
|
||||
protected override void CompileList(List<TournamentArchive> list)
|
||||
{
|
||||
list.Clear();
|
||||
list.AddRange(TournamentArchives.Registry);
|
||||
|
||||
base.CompileList(list);
|
||||
}
|
||||
|
||||
public override string GetSearchKeyFor(TournamentArchive key)
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
return key.Name + " @ " + key.Date;
|
||||
}
|
||||
|
||||
return base.GetSearchKeyFor(null);
|
||||
}
|
||||
|
||||
protected override string GetLabelText(int index, int pageIndex, TournamentArchive entry)
|
||||
{
|
||||
if (entry != null)
|
||||
{
|
||||
return entry.Name + " @ " + entry.Date.ToShortDateString();
|
||||
}
|
||||
|
||||
return base.GetLabelText(index, pageIndex, null);
|
||||
}
|
||||
|
||||
protected override void SelectEntry(GumpButton button, TournamentArchive entry)
|
||||
{
|
||||
base.SelectEntry(button, entry);
|
||||
|
||||
new TournamentArchiveUI(User, entry, this).Send();
|
||||
}
|
||||
}
|
||||
|
||||
public class TournamentArchiveUI : SuperGumpList<TournamentMatch>
|
||||
{
|
||||
public IEnumerable<TournamentMatch> Archive { get; private set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public int EntryWidth { get; set; }
|
||||
public int EntryHeight { get; set; }
|
||||
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
|
||||
public PlayerMobile Leader { get; private set; }
|
||||
|
||||
public TournamentArchiveUI(Mobile user, TournamentBattle battle, Gump parent = null)
|
||||
: this(user, (IEnumerable<TournamentMatch>)battle, parent)
|
||||
{ }
|
||||
|
||||
public TournamentArchiveUI(Mobile user, TournamentArchive archive, Gump parent = null)
|
||||
: this(user, (IEnumerable<TournamentMatch>)archive, parent)
|
||||
{ }
|
||||
|
||||
private TournamentArchiveUI(Mobile user, IEnumerable<TournamentMatch> archive, Gump parent = null)
|
||||
: base(user, parent)
|
||||
{
|
||||
Archive = archive;
|
||||
|
||||
Title = "Tournament Records";
|
||||
|
||||
if (Archive is TournamentArchive)
|
||||
{
|
||||
var a = (TournamentArchive)Archive;
|
||||
|
||||
Title = String.Format("{0} - {1}", a.Name, a.Date.ToSimpleString("t@h:m:s@ D d M y"));
|
||||
}
|
||||
else if (Archive is TournamentBattle)
|
||||
{
|
||||
var b = (TournamentBattle)Archive;
|
||||
|
||||
Title = b.Name;
|
||||
|
||||
if (b.State == PvPBattleState.Running)
|
||||
{
|
||||
AutoRefresh = true;
|
||||
AutoRefreshRate = TimeSpan.FromSeconds(30.0);
|
||||
}
|
||||
}
|
||||
|
||||
CanClose = true;
|
||||
CanDispose = true;
|
||||
CanMove = true;
|
||||
CanResize = true;
|
||||
|
||||
EntriesPerPage = 5;
|
||||
EntryWidth = 400;
|
||||
EntryHeight = 120;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
protected override void Compile()
|
||||
{
|
||||
EntryWidth = Math.Max(400, EntryWidth);
|
||||
EntryHeight = Math.Max(120, EntryHeight);
|
||||
|
||||
base.Compile();
|
||||
}
|
||||
|
||||
protected virtual void Init()
|
||||
{
|
||||
if (Archive != null)
|
||||
{
|
||||
Leader = List.Where(m => m.Winner != null)
|
||||
.GroupBy(m => m.Winner)
|
||||
.OrderByDescending(o => o.Count())
|
||||
.ThenByDescending(o => o.Aggregate(0.0, (c, m) => c + m.ComputeScore(o.Key)))
|
||||
.Select(o => o.Key)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
var pad = SupportsUltimaStore ? 10 : 15;
|
||||
var count = Math.Max(1, Math.Min(EntryCount, EntriesPerPage));
|
||||
|
||||
Width = 30 + (pad * 3) + EntryWidth;
|
||||
Height = 90 + (pad * 3) + (count * EntryHeight);
|
||||
}
|
||||
|
||||
protected override bool OnBeforeSend()
|
||||
{
|
||||
return base.OnBeforeSend() && Archive != null;
|
||||
}
|
||||
|
||||
protected override void CompileList(List<TournamentMatch> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (Archive != null)
|
||||
{
|
||||
list.AddRange(Archive.Not(o => o.IsEmpty));
|
||||
}
|
||||
|
||||
base.CompileList(list);
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
protected override void CompileLayout(SuperGumpLayout layout)
|
||||
{
|
||||
base.CompileLayout(layout);
|
||||
|
||||
var sup = SupportsUltimaStore;
|
||||
var pad = sup ? 10 : 15;
|
||||
var bgID = sup ? 40000 : 9270;
|
||||
var bgCol = sup ? Color.FromArgb(0xFF, 0x29, 0x31, 0x39) : Color.Black;
|
||||
|
||||
layout.Add(
|
||||
"bg",
|
||||
() =>
|
||||
{
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var w = Width;
|
||||
var h = Height;
|
||||
|
||||
AddBackground(x, y, w, h, bgID);
|
||||
|
||||
x = pad;
|
||||
y = (Height - pad) - 60;
|
||||
w = Width - (pad * 2);
|
||||
h = 60;
|
||||
|
||||
AddRectangle(x, y, w, h, bgCol, true);
|
||||
});
|
||||
|
||||
layout.Add(
|
||||
"title",
|
||||
() =>
|
||||
{
|
||||
var title = Title;
|
||||
|
||||
title = title.WrapUOHtmlBig();
|
||||
title = title.WrapUOHtmlCenter();
|
||||
title = title.WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
AddHtml(pad, pad, Width - (pad * 2), 40, title, false, false);
|
||||
});
|
||||
|
||||
layout.Add("results", CompileResults);
|
||||
|
||||
layout.Add(
|
||||
"scroll",
|
||||
() =>
|
||||
{
|
||||
var x = Width - (pad + 30);
|
||||
var y = pad + 30;
|
||||
var h = Height - ((pad * 3) + 90);
|
||||
|
||||
AddScrollbarV(x, y, h, PageCount, Page, PreviousPage, NextPage);
|
||||
});
|
||||
|
||||
layout.Add("cpanel", CompileControls);
|
||||
}
|
||||
|
||||
private void CompileControls()
|
||||
{
|
||||
var sup = SupportsUltimaStore;
|
||||
var pad = sup ? 10 : 15;
|
||||
//var bgCol = sup ? Color.FromArgb(0xFF, 0x29, 0x31, 0x39) : Color.Black;
|
||||
|
||||
var x = pad;
|
||||
var y = Height - (pad + 60);
|
||||
var w = (Width - (pad * 2)) / 4;
|
||||
|
||||
//////////
|
||||
|
||||
var players = List.SelectMany(o => o.Players.Where(p => p != null)).Distinct().Count();
|
||||
|
||||
var text = String.Format("PLAYERS:\n{0:#,0}", players);
|
||||
|
||||
text = text.WrapUOHtmlBold();
|
||||
text = text.WrapUOHtmlCenter();
|
||||
text = text.WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
|
||||
AddRectangle(x, y, w, 60, Color.White);
|
||||
AddHtml(x + pad, y + pad, w - (pad * 2), 60, text, false, false);
|
||||
|
||||
x += w;
|
||||
|
||||
//////////
|
||||
|
||||
text = String.Format("MATCHES:\n{0:#,0}", EntryCount);
|
||||
|
||||
text = text.WrapUOHtmlBold();
|
||||
text = text.WrapUOHtmlCenter();
|
||||
text = text.WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
|
||||
AddRectangle(x, y, w, 60, Color.White);
|
||||
AddHtml(x + pad, y + pad, w - (pad * 2), 60, text, false, false);
|
||||
|
||||
x += w;
|
||||
|
||||
//////////
|
||||
|
||||
var ticks = List.Select(o => o.IsRunning ? o.Expire : o.TotalTime).Aggregate(0L, (v, o) => v + o.Ticks);
|
||||
|
||||
var duration = TimeSpan.FromTicks(ticks).ToSimpleString(@"!<d\d ><h\h >m\m");
|
||||
|
||||
text = String.Format("DURATION:\n{0}", duration);
|
||||
|
||||
text = text.WrapUOHtmlBold();
|
||||
text = text.WrapUOHtmlCenter();
|
||||
text = text.WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
|
||||
AddRectangle(x, y, w, 60, Color.White);
|
||||
AddHtml(x + pad, y + pad, w - (pad * 2), 60, text, false, false);
|
||||
|
||||
x += w;
|
||||
|
||||
//////////
|
||||
|
||||
if (Archive is TournamentArchive || (Archive is TournamentBattle && !((TournamentBattle)Archive).IsRunning))
|
||||
{
|
||||
text = String.Format("WINNER:\n{0}", Leader != null ? Leader.RawName : "N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
text = String.Format("LEADER:\n{0}", Leader != null ? Leader.RawName : "N/A");
|
||||
}
|
||||
|
||||
text = text.WrapUOHtmlBold();
|
||||
text = text.WrapUOHtmlCenter();
|
||||
text = text.WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
|
||||
AddRectangle(x, y, w, 60, Color.White);
|
||||
AddHtml(x + pad, y + pad, w - (pad * 2), 60, text, false, false);
|
||||
}
|
||||
|
||||
private void CompileResults()
|
||||
{
|
||||
var sup = SupportsUltimaStore;
|
||||
var pad = sup ? 10 : 15;
|
||||
|
||||
var x = pad;
|
||||
var y = pad + 30;
|
||||
|
||||
var range = GetListRange();
|
||||
|
||||
foreach (var o in range.Values)
|
||||
{
|
||||
CompileMatchLayout(x, y, EntryWidth, EntryHeight, o);
|
||||
|
||||
y += EntryHeight;
|
||||
}
|
||||
|
||||
range.Clear();
|
||||
}
|
||||
|
||||
private void CompileMatchLayout(int x, int y, int w, int h, TournamentMatch match)
|
||||
{
|
||||
var sup = SupportsUltimaStore;
|
||||
var pad = sup ? 10 : 15;
|
||||
var bgID = sup ? 40000 : 9270;
|
||||
//var bgCol = sup ? Color.FromArgb(0xFF, 0x29, 0x31, 0x39) : Color.Black;
|
||||
|
||||
AddTileButton(x, y, w, h, b => Select(match));
|
||||
|
||||
AddBackground(x, y, w, h, bgID);
|
||||
|
||||
var capacity = match.Capacity;
|
||||
|
||||
if (capacity <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var xx = x + pad;
|
||||
var yy = y + pad;
|
||||
var ww = (w - (pad * 2)) / capacity;
|
||||
//var hh = h - (pad * 2);
|
||||
|
||||
string t;
|
||||
Color c;
|
||||
Size s;
|
||||
Point o;
|
||||
int ox, oy, tx, ty;
|
||||
|
||||
for (var i = 0; i < capacity; i++)
|
||||
{
|
||||
var p = match.Players[i];
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
t = p.RawName;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = "???";
|
||||
}
|
||||
|
||||
t = t.WrapUOHtmlBig();
|
||||
t = t.WrapUOHtmlCenter();
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
t = t.WrapUOHtmlColor(User.GetNotorietyColor(p), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = t.WrapUOHtmlColor(Color.White, false);
|
||||
}
|
||||
|
||||
AddHtml(xx, yy, ww, 40, t, false, false);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
s = GetImageSize(7034);
|
||||
o = new Point(ComputeCenter(xx, ww - s.Width), yy + 30);
|
||||
|
||||
AddImage(o.X, o.Y, 7034);
|
||||
|
||||
c = match.Winner == null ? Color.Silver : match.Winner == p ? Color.LawnGreen : Color.IndianRed;
|
||||
|
||||
AddRectangle(o.X, o.Y, s.Width, s.Height, c, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = GetImageSize(7034);
|
||||
o = new Point(ComputeCenter(xx, ww - s.Width), yy + 30);
|
||||
|
||||
AddImage(o.X, o.Y, 7034);
|
||||
|
||||
c = Color.White;
|
||||
|
||||
AddRectangle(o.X, o.Y, s.Width, s.Height, c, 2);
|
||||
}
|
||||
|
||||
if (i + 1 < capacity)
|
||||
{
|
||||
ox = o.X + s.Width;
|
||||
tx = xx + (ww - 30);
|
||||
}
|
||||
else
|
||||
{
|
||||
ox = xx + 30;
|
||||
tx = o.X;
|
||||
}
|
||||
|
||||
oy = ComputeCenter(o.Y, s.Height - 4);
|
||||
ty = oy + 4;
|
||||
|
||||
AddRectangle(ox, oy, tx - ox, ty - oy, c, true);
|
||||
|
||||
if (i + 1 < capacity)
|
||||
{
|
||||
AddImageShadow(xx + (ww - 30), yy + 30, 5578, 0, 90);
|
||||
}
|
||||
|
||||
xx += ww;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetHtml(StringBuilder html, TournamentMatch match, bool extended)
|
||||
{
|
||||
var text = String.Format("Match #{0}", match.Index + 1);
|
||||
|
||||
text = text.WrapUOHtmlBig();
|
||||
text = text.WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
html.AppendLine(text);
|
||||
|
||||
var sep = " vs ".WrapUOHtmlColor(Color.IndianRed, Color.White);
|
||||
|
||||
text = String.Join(sep, match.Players.Select(o => o != null ? o.RawName : "???"));
|
||||
text = text.WrapUOHtmlColor(Color.White, false);
|
||||
|
||||
html.AppendLine(text);
|
||||
|
||||
if (extended)
|
||||
{
|
||||
html.AppendLine();
|
||||
}
|
||||
|
||||
if (match.Winner != null)
|
||||
{
|
||||
text = String.Format("Winner: {0}", match.Winner.RawName);
|
||||
text = text.WrapUOHtmlColor(Color.PaleGoldenrod, false);
|
||||
}
|
||||
else if (match.IsComplete)
|
||||
{
|
||||
text = "Draw";
|
||||
text = text.WrapUOHtmlColor(Color.LightGray, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var exp = match.Expire;
|
||||
|
||||
if (exp > TimeSpan.Zero)
|
||||
{
|
||||
text = String.Format("Expire: {0}", exp.ToSimpleString("h:m:s"));
|
||||
text = text.WrapUOHtmlColor(Color.SkyBlue, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = "Expired";
|
||||
text = text.WrapUOHtmlColor(Color.IndianRed, false);
|
||||
}
|
||||
}
|
||||
|
||||
html.AppendLine(text);
|
||||
|
||||
if (!extended)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
html.AppendLine();
|
||||
|
||||
if (match.DateStart > DateTime.MinValue)
|
||||
{
|
||||
text = String.Format("Start: {0}", match.DateStart.ToSimpleString("t@h:m:s@ D d M y"));
|
||||
text = text.WrapUOHtmlColor(Color.SkyBlue, false);
|
||||
|
||||
html.AppendLine(text);
|
||||
}
|
||||
|
||||
if (match.DateEnd < DateTime.MaxValue)
|
||||
{
|
||||
text = String.Format("Finish: {0}", match.DateEnd.ToSimpleString("t@h:m:s@ D d M y"));
|
||||
text = text.WrapUOHtmlColor(Color.SkyBlue, false);
|
||||
|
||||
html.AppendLine(text);
|
||||
}
|
||||
|
||||
html.AppendLine();
|
||||
|
||||
text = "Records";
|
||||
text = text.WrapUOHtmlBig();
|
||||
text = text.WrapUOHtmlColor(Color.Gold, false);
|
||||
|
||||
html.AppendLine(text);
|
||||
|
||||
html.AppendLine(String.Empty.WrapUOHtmlColor(Color.White, false));
|
||||
|
||||
foreach (var o in match.Records)
|
||||
{
|
||||
html.AppendLine("[{0}]: {1}", o.Time.ToSimpleString("t@h:m:s@"), o.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Select(TournamentMatch match)
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetHtml(html, match, true);
|
||||
|
||||
new NoticeDialogGump(User, Refresh(true))
|
||||
{
|
||||
Width = 600,
|
||||
Height = 400,
|
||||
Title = String.Format("Match #{0:#,0}", match.Index + 1),
|
||||
Html = html.ToString()
|
||||
}.Send();
|
||||
}
|
||||
|
||||
public override int SortCompare(TournamentMatch a, TournamentMatch b)
|
||||
{
|
||||
var res = 0;
|
||||
|
||||
if (a.CompareNull(b, ref res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
if (a.Index < b.Index)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.Index > b.Index)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return base.SortCompare(a, b);
|
||||
}
|
||||
|
||||
protected override void OnAutoRefresh()
|
||||
{
|
||||
base.OnAutoRefresh();
|
||||
|
||||
AutoRefresh = Archive is TournamentBattle && ((TournamentBattle)Archive).IsRunning;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.IO;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public static class TournamentArchives
|
||||
{
|
||||
static TournamentArchives()
|
||||
{
|
||||
Registry = new List<TournamentArchive>();
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
CommandUtility.Register("TournamentArchives", AutoPvP.Access, e => new TournamentArchivesUI(e.Mobile).Send());
|
||||
CommandUtility.RegisterAlias("TournamentArchives", "TArchives");
|
||||
}
|
||||
|
||||
public static FileInfo File => IOUtility.EnsureFile(VitaNexCore.SavesDirectory + "/AutoPvP/Tournament/Archives.bin");
|
||||
|
||||
public static List<TournamentArchive> Registry { get; private set; }
|
||||
|
||||
public static int Count => Registry.Count;
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
VitaNexCore.OnModuleLoaded += OnModuleLoaded;
|
||||
VitaNexCore.OnModuleSaved += OnModuleSaved;
|
||||
}
|
||||
|
||||
private static void OnModuleLoaded(CoreModuleInfo cmi)
|
||||
{
|
||||
if (cmi.Enabled && cmi.TypeOf == typeof(AutoPvP))
|
||||
{
|
||||
File.Deserialize(r => r.ReadBlockList(r1 => new TournamentArchive(r1), Registry));
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnModuleSaved(CoreModuleInfo cmi)
|
||||
{
|
||||
if (cmi.Enabled && cmi.TypeOf == typeof(AutoPvP))
|
||||
{
|
||||
File.Serialize(w => w.WriteBlockList(Registry, (w1, o) => o.Serialize(w1)));
|
||||
}
|
||||
}
|
||||
|
||||
public static TournamentArchive CreateArchive(TournamentBattle b)
|
||||
{
|
||||
TournamentArchive a = null;
|
||||
|
||||
foreach (var r in b.Matches.Where(o => !o.IsDisposed && o.IsComplete && o.Records.Count > 0))
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
a = new TournamentArchive(b.Name, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
a.Matches.Add(r);
|
||||
}
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
Registry.Add(a);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TournamentRecord : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public DateTime Time { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public string Value { get; private set; }
|
||||
|
||||
public TournamentRecord(string value)
|
||||
{
|
||||
Time = DateTime.UtcNow;
|
||||
Value = value ?? String.Empty;
|
||||
}
|
||||
|
||||
public TournamentRecord(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Time);
|
||||
writer.Write(Value);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Time = reader.ReadDateTime();
|
||||
Value = reader.ReadString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public sealed class TournamentRecords : PropertyObject, IEnumerable<TournamentRecord>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public List<TournamentRecord> Entries { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int Count => Entries.Count;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public DateTime DateBegin => Count > 0 ? Entries.Lowest(o => o.Time.Ticks).Time : DateTime.MinValue;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public DateTime DateEnd => Count > 0 ? Entries.Highest(o => o.Time.Ticks).Time : DateTime.MaxValue;
|
||||
|
||||
public TournamentRecords()
|
||||
{
|
||||
Entries = new List<TournamentRecord>();
|
||||
}
|
||||
|
||||
public TournamentRecords(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public void Record(string format, params object[] args)
|
||||
{
|
||||
Record(String.Format(format, args));
|
||||
}
|
||||
|
||||
public void Record(string value)
|
||||
{
|
||||
Entries.Add(new TournamentRecord(value));
|
||||
}
|
||||
|
||||
public IEnumerator<TournamentRecord> GetEnumerator()
|
||||
{
|
||||
return Entries.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.WriteList(Entries, (w, o) => o.Serialize(w));
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Entries = reader.ReadList(r => new TournamentRecord(r), Entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,544 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public sealed class TournamentMatch : PropertyObject, IDisposable
|
||||
{
|
||||
private long _MessageBuffer;
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public TournamentRecords Records { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public int Index { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public int Team { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PlayerMobile[] Players { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool[] Dead { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int[][] Statistics { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public PlayerMobile Winner { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public DateTime DateStart { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public DateTime DateEnd { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public TimeSpan Delay { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public TimeSpan Duration { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public TimeSpan TotalTime => Delay + Duration;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public TimeSpan Expire
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DateStart > DateTime.MinValue)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var exp = DateStart + TotalTime;
|
||||
|
||||
if (exp > now)
|
||||
{
|
||||
return exp - now;
|
||||
}
|
||||
}
|
||||
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsRunning => Expire > TimeSpan.Zero;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsDelayed => DateStart + Delay > DateTime.UtcNow;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsComplete => DateEnd < DateTime.MaxValue;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int Capacity => Players.Length;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int Count => Players.Count(o => o != null);
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int CountAlive => Players.Count(o => o != null && IsAlive(o));
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int CountDead => Players.Count(o => o != null && IsDead(o));
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsEmpty => Count == 0;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsFull => Count == Capacity;
|
||||
|
||||
public TournamentMatch(int index, int team, TimeSpan delay, TimeSpan duration)
|
||||
{
|
||||
Index = index;
|
||||
Team = team;
|
||||
Delay = delay;
|
||||
Duration = duration;
|
||||
|
||||
Records = new TournamentRecords();
|
||||
|
||||
Players = new PlayerMobile[2];
|
||||
Dead = new bool[Players.Length];
|
||||
|
||||
Statistics = new int[Players.Length][];
|
||||
Statistics.SetAll(i => new int[2]); // Damage, Healing
|
||||
|
||||
DateStart = DateTime.MinValue;
|
||||
DateEnd = DateTime.MaxValue;
|
||||
}
|
||||
|
||||
public TournamentMatch(int index, TournamentBattle battle, TournamentTeam team)
|
||||
: this(index, team.Serial.ValueHash, battle.MatchDelay, battle.MatchDuration)
|
||||
{ }
|
||||
|
||||
public TournamentMatch(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public void RecordDamage(PlayerMobile pm, int amount)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
Statistics[i][0] += amount;
|
||||
|
||||
Record("{0} deals {1:#,0} damage.", pm.RawName, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public void RecordHeal(PlayerMobile pm, int amount)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
Statistics[i][1] += amount;
|
||||
|
||||
Record("{0} heals {1:#,0} health.", pm.RawName, amount);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetDamageDone(PlayerMobile pm)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
return Statistics[i][0];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int GetHealingDone(PlayerMobile pm)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
return Statistics[i][1];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public double ComputeScore(PlayerMobile pm)
|
||||
{
|
||||
return GetDamageDone(pm) - GetHealingDone(pm);
|
||||
}
|
||||
|
||||
public void Record(string format, params object[] args)
|
||||
{
|
||||
Records.Record(format, args);
|
||||
}
|
||||
|
||||
public void Record(string value)
|
||||
{
|
||||
Records.Record(value);
|
||||
}
|
||||
|
||||
public void ForEachPlayer(Action<PlayerMobile> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pm in Players.Where(o => o != null))
|
||||
{
|
||||
action(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public void Broadcast(int hue, string format, params object[] args)
|
||||
{
|
||||
Record(format, args);
|
||||
|
||||
ForEachPlayer(pm => pm.SendMessage(hue, format, args));
|
||||
}
|
||||
|
||||
public void Broadcast(int hue, string message)
|
||||
{
|
||||
Record(message);
|
||||
|
||||
ForEachPlayer(pm => pm.SendMessage(hue, message));
|
||||
}
|
||||
|
||||
public void Broadcast(string format, params object[] args)
|
||||
{
|
||||
Record(format, args);
|
||||
|
||||
ForEachPlayer(pm => pm.SendMessage(format, args));
|
||||
}
|
||||
|
||||
public void Broadcast(string message)
|
||||
{
|
||||
Record(message);
|
||||
|
||||
ForEachPlayer(pm => pm.SendMessage(message));
|
||||
}
|
||||
|
||||
public void HandleDeath(PlayerMobile pm)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (Dead.InBounds(i))
|
||||
{
|
||||
Dead[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDead(PlayerMobile pm)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (Dead.InBounds(i))
|
||||
{
|
||||
return Dead[i];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsAlive(PlayerMobile pm)
|
||||
{
|
||||
var i = Players.IndexOf(pm);
|
||||
|
||||
if (Dead.InBounds(i))
|
||||
{
|
||||
return !Dead[i];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Contains(PlayerMobile pm)
|
||||
{
|
||||
return Players.Contains(pm);
|
||||
}
|
||||
|
||||
public void Sync(TournamentBattle b, TournamentTeam t)
|
||||
{
|
||||
if (IsDisposed || IsComplete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (b == null || b.Deleted || b.IsInternal || b.IsQueueing || b.IsPreparing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (t == null || t.Deleted || t.Serial.ValueHash != Team)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (b.IsRunning && DateStart == DateTime.MinValue) // Populate & Start
|
||||
{
|
||||
for (var i = 0; i < Capacity; i++)
|
||||
{
|
||||
var pm = Players[i];
|
||||
|
||||
if (pm != null && !b.IsAliveParticipant(pm))
|
||||
{
|
||||
Players[i] = null;
|
||||
Dead[i] = false;
|
||||
Statistics[i].SetAll(0);
|
||||
|
||||
Broadcast(0x22, "{0} has left the match! [{1} / {2}]", pm.RawName, Count, Capacity);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsFull)
|
||||
{
|
||||
var q = b.GetMatchQueue(Capacity - Count);
|
||||
|
||||
for (var i = 0; i < Capacity && q.Count > 0; i++)
|
||||
{
|
||||
var pm = Players[i];
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
Players[i] = pm = q.Dequeue();
|
||||
Dead[i] = false;
|
||||
Statistics[i].SetAll(0);
|
||||
|
||||
Broadcast(0x55, "{0} has joined the match! [{1} / {2}]", pm.RawName, Count, Capacity);
|
||||
}
|
||||
}
|
||||
|
||||
q.Free(true);
|
||||
}
|
||||
|
||||
if (IsFull)
|
||||
{
|
||||
Broadcast("The match is now full, prepare to fight!");
|
||||
|
||||
Start(b, t);
|
||||
}
|
||||
}
|
||||
else if (b.IsRunning && Expire > TimeSpan.Zero) // Tick
|
||||
{
|
||||
var started = !IsDelayed;
|
||||
|
||||
if (_MessageBuffer <= Core.TickCount)
|
||||
{
|
||||
var time = started ? Expire : ((DateStart + Delay) - DateTime.UtcNow);
|
||||
|
||||
if (time.Hours > 0 && time.Hours <= 3)
|
||||
{
|
||||
_MessageBuffer = Core.TickCount + (Math.Min(15, time.Minutes) * 60000);
|
||||
}
|
||||
else if (time.Minutes > 0 && time.Minutes <= 5)
|
||||
{
|
||||
_MessageBuffer = Core.TickCount + (Math.Min(15, time.Seconds) * 1000);
|
||||
}
|
||||
else if (time.Seconds > 0 && time.Seconds <= 10)
|
||||
{
|
||||
_MessageBuffer = Core.TickCount + Math.Min(1000, time.Milliseconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
_MessageBuffer = Int64.MaxValue;
|
||||
}
|
||||
|
||||
var seconds = (int)Math.Ceiling(time.TotalSeconds);
|
||||
|
||||
if (seconds > 5)
|
||||
{
|
||||
Broadcast(
|
||||
"The match will {0} in {1} second{2}!",
|
||||
!started ? "begin" : "end",
|
||||
seconds,
|
||||
seconds != 1 ? "s" : String.Empty);
|
||||
}
|
||||
else if (seconds > 0)
|
||||
{
|
||||
Broadcast("{0}...", seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
Broadcast(!started ? "FIGHT!" : "CEASE!");
|
||||
}
|
||||
}
|
||||
|
||||
PlayerMobile o = null;
|
||||
|
||||
var count = 0;
|
||||
|
||||
foreach (var pm in Players.Where(p => IsAlive(p) && b.IsAliveParticipant(p)).OrderBy(ComputeScore))
|
||||
{
|
||||
o = pm;
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count <= 1)
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
Broadcast("Not enough players to start the match.");
|
||||
}
|
||||
|
||||
Winner = o;
|
||||
|
||||
if (Winner != null)
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
Broadcast("{0} has won the match by default!", Winner.RawName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Broadcast("{0} has won the match!", Winner.RawName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Broadcast("No winner has been declared.");
|
||||
}
|
||||
|
||||
End(b, t);
|
||||
}
|
||||
}
|
||||
else if ((b.IsRunning || b.IsEnded) && DateEnd == DateTime.MaxValue) // End
|
||||
{
|
||||
var started = !IsDelayed;
|
||||
|
||||
if (started)
|
||||
{
|
||||
Broadcast("TIME UP!");
|
||||
}
|
||||
|
||||
Winner = Players.Where(p => IsAlive(p) && b.IsAliveParticipant(p)).Highest(ComputeScore);
|
||||
|
||||
if (Winner != null)
|
||||
{
|
||||
if (!started)
|
||||
{
|
||||
Broadcast("{0} has won the match by default!", Winner.RawName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Broadcast("{0} has won the match!", Winner.RawName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Broadcast("No winner has been declared.");
|
||||
}
|
||||
|
||||
End(b, t);
|
||||
}
|
||||
}
|
||||
|
||||
private void Start(TournamentBattle b, TournamentTeam t)
|
||||
{
|
||||
DateStart = DateTime.UtcNow;
|
||||
|
||||
foreach (var pm in Players.Where(p => IsAlive(p) && b.IsParticipant(p)))
|
||||
{
|
||||
b.RefreshStats(pm, true, true);
|
||||
b.TeleportToSpawnPoint(t, pm);
|
||||
}
|
||||
}
|
||||
|
||||
private void End(TournamentBattle b, TournamentTeam t)
|
||||
{
|
||||
DateEnd = DateTime.UtcNow;
|
||||
|
||||
foreach (var pm in Players.Where(p => IsAlive(p) && b.IsParticipant(p)))
|
||||
{
|
||||
b.RefreshStats(pm, true, true);
|
||||
b.TeleportToHomeBase(t, pm);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Players.Clear();
|
||||
|
||||
Records = null;
|
||||
Players = null;
|
||||
Dead = null;
|
||||
Statistics = null;
|
||||
Winner = null;
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Index);
|
||||
writer.Write(Team);
|
||||
|
||||
writer.WriteArray(Players, (w, o) => w.Write(o));
|
||||
writer.WriteArray(Dead, (w, o) => w.Write(o));
|
||||
writer.WriteArray(Statistics, (w, o) => w.WriteArray(o, (w1, o1) => w1.Write(o1)));
|
||||
|
||||
writer.Write(Delay);
|
||||
writer.Write(Duration);
|
||||
|
||||
writer.Write(DateStart);
|
||||
writer.Write(DateEnd);
|
||||
|
||||
writer.Write(Winner);
|
||||
|
||||
Records.Serialize(writer);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Index = reader.ReadInt();
|
||||
Team = reader.ReadInt();
|
||||
|
||||
Players = reader.ReadArray(r => r.ReadMobile<PlayerMobile>(), Players);
|
||||
Dead = reader.ReadArray(r => r.ReadBool(), Dead);
|
||||
Statistics = reader.ReadArray(r => r.ReadArray(r1 => r1.ReadInt()), Statistics);
|
||||
|
||||
Delay = reader.ReadTimeSpan();
|
||||
Duration = reader.ReadTimeSpan();
|
||||
|
||||
DateStart = reader.ReadDateTime();
|
||||
DateEnd = reader.ReadDateTime();
|
||||
|
||||
Winner = reader.ReadMobile<PlayerMobile>();
|
||||
|
||||
Records = new TournamentRecords(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TournamentTeam : PvPTeam
|
||||
{
|
||||
public TournamentTeam(
|
||||
PvPBattle battle,
|
||||
string name = "Incognito",
|
||||
int minCapacity = 10,
|
||||
int maxCapacity = 50,
|
||||
int color = 12)
|
||||
: base(battle, name, minCapacity, maxCapacity, color)
|
||||
{
|
||||
RespawnOnStart = false;
|
||||
RespawnOnDeath = false;
|
||||
KickOnDeath = true;
|
||||
}
|
||||
|
||||
public TournamentTeam(PvPBattle battle, GenericReader reader)
|
||||
: base(battle, reader)
|
||||
{ }
|
||||
|
||||
public override void AddMember(PlayerMobile pm, bool teleport)
|
||||
{
|
||||
if (IsMember(pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Members[pm] = DateTime.UtcNow;
|
||||
|
||||
if (teleport)
|
||||
{
|
||||
Battle.TeleportToHomeBase(this, pm);
|
||||
}
|
||||
|
||||
OnMemberAdded(pm);
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,609 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TournamentBattle : PvPBattle, IEnumerable<TournamentMatch>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public List<TournamentMatch> Matches { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public TimeSpan MatchDelay { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public TimeSpan MatchDuration { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public double MatchSuddenDeath { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int FinalBestOfCur { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public int FinalBestOfMax { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsFinalMatch => State == PvPBattleState.Running && Matches.Count > 0 && CurrentCapacity <= 2;
|
||||
|
||||
public TournamentBattle()
|
||||
{
|
||||
Name = "Tournament";
|
||||
Category = "Tournaments";
|
||||
Description = "An elimination tournament!";
|
||||
|
||||
AddTeam("Contenders", 10, 50, 85);
|
||||
|
||||
Schedule.Info.Months = ScheduleMonths.All;
|
||||
Schedule.Info.Days = ScheduleDays.Sunday;
|
||||
Schedule.Info.Times = ScheduleTimes.Noon;
|
||||
|
||||
Options.Timing.QueuePeriod = TimeSpan.FromHours(1.0);
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(10.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromHours(5.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromHours(5.0);
|
||||
|
||||
Options.Rules.AllowBeneficial = true;
|
||||
Options.Rules.AllowHarmful = true;
|
||||
Options.Rules.AllowHousing = false;
|
||||
Options.Rules.AllowPets = false;
|
||||
Options.Rules.AllowSpawn = false;
|
||||
Options.Rules.AllowSpeech = true;
|
||||
Options.Rules.CanBeDamaged = true;
|
||||
Options.Rules.CanDamageEnemyTeam = true;
|
||||
Options.Rules.CanDamageOwnTeam = true;
|
||||
Options.Rules.CanDie = false;
|
||||
Options.Rules.CanHeal = true;
|
||||
Options.Rules.CanHealEnemyTeam = true;
|
||||
Options.Rules.CanHealOwnTeam = true;
|
||||
Options.Rules.CanMount = false;
|
||||
Options.Rules.CanFly = false;
|
||||
Options.Rules.CanResurrect = false;
|
||||
Options.Rules.CanUseStuckMenu = false;
|
||||
Options.Rules.CanMoveThrough = true;
|
||||
Options.Rules.CanEquip = true;
|
||||
|
||||
RequireCapacity = true;
|
||||
|
||||
UseTeamColors = false;
|
||||
|
||||
IdleKick = false;
|
||||
IdleThreshold = TimeSpan.FromMinutes(30.0);
|
||||
|
||||
LogoutDelay = TimeSpan.FromMinutes(5.0);
|
||||
|
||||
MatchDelay = TimeSpan.FromSeconds(10.0);
|
||||
MatchDuration = TimeSpan.FromMinutes(15.0);
|
||||
|
||||
MatchSuddenDeath = 0.25;
|
||||
|
||||
FinalBestOfCur = 0;
|
||||
FinalBestOfMax = 3;
|
||||
}
|
||||
|
||||
public TournamentBattle(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
protected override void EnsureConstructDefaults()
|
||||
{
|
||||
base.EnsureConstructDefaults();
|
||||
|
||||
Matches = new List<TournamentMatch>();
|
||||
}
|
||||
|
||||
protected override void RegisterSubCommands()
|
||||
{
|
||||
base.RegisterSubCommands();
|
||||
|
||||
RegisterSubCommand(
|
||||
"scores",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateUI(state.Mobile, false);
|
||||
|
||||
return true;
|
||||
},
|
||||
"Display the current match rankings.");
|
||||
}
|
||||
|
||||
public override bool Validate(Mobile viewer, List<string> errors, bool pop = true)
|
||||
{
|
||||
if (!base.Validate(viewer, errors, pop) && pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Teams.All(t => t is TournamentTeam))
|
||||
{
|
||||
errors.Add("One or more teams are not of the TournamentTeam type.");
|
||||
errors.Add("[Options] -> [View Teams]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void GetAssignPriority(PvPTeam team, out double weight)
|
||||
{
|
||||
base.GetAssignPriority(team, out weight);
|
||||
|
||||
if (weight > Double.MinValue && weight < Double.MaxValue)
|
||||
{
|
||||
weight = team.Members.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool AddTeam(string name, int minCapacity, int capacity, int color)
|
||||
{
|
||||
return AddTeam(new TournamentTeam(this, name, minCapacity, capacity, color));
|
||||
}
|
||||
|
||||
public override bool AddTeam(PvPTeam team)
|
||||
{
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (team is TournamentTeam)
|
||||
{
|
||||
return base.AddTeam(team);
|
||||
}
|
||||
|
||||
var added = AddTeam(team.Name, team.MinCapacity, team.MinCapacity, team.Color);
|
||||
|
||||
team.Delete();
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
public override void OnTeamMemberDeath(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
base.OnTeamMemberDeath(team, pm);
|
||||
|
||||
if (team is TournamentTeam)
|
||||
{
|
||||
var o = FindActiveMatch(pm);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
o.HandleDeath(pm);
|
||||
|
||||
o.Sync(this, (TournamentTeam)team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAfterTeamMemberDeath(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
base.OnAfterTeamMemberDeath(team, pm);
|
||||
|
||||
UpdateUI(true);
|
||||
}
|
||||
|
||||
public override bool TryKickOnDeath(PvPTeam team, PlayerMobile pm, bool isLoss)
|
||||
{
|
||||
if (isLoss && IsFinalMatch && FinalBestOfCur < FinalBestOfMax)
|
||||
{
|
||||
var o = FindActiveMatch(pm);
|
||||
|
||||
int min = 0, max = 0;
|
||||
|
||||
if (o == null)
|
||||
{
|
||||
++FinalBestOfCur;
|
||||
}
|
||||
else if (o.CountAlive <= 1)
|
||||
{
|
||||
foreach (var c in o.Players.Where(p => p != null).Select(CountMatchesWon))
|
||||
{
|
||||
min = Math.Min(min, c);
|
||||
max = Math.Max(max, c);
|
||||
}
|
||||
|
||||
++FinalBestOfCur;
|
||||
}
|
||||
|
||||
if (max - min < (FinalBestOfMax / 2) + 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return base.TryKickOnDeath(team, pm, isLoss);
|
||||
}
|
||||
|
||||
public void UpdateUI(bool refreshOnly)
|
||||
{
|
||||
foreach (var pm in GetLocalBroadcastList())
|
||||
{
|
||||
UpdateUI(pm, refreshOnly);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateUI(PlayerMobile pm, bool refreshOnly)
|
||||
{
|
||||
var g = pm.FindGump<TournamentArchiveUI>();
|
||||
|
||||
if (g == null)
|
||||
{
|
||||
if (refreshOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g = new TournamentArchiveUI(pm, this);
|
||||
}
|
||||
|
||||
g.Refresh(true);
|
||||
}
|
||||
|
||||
public override void OnHeal(Mobile healer, Mobile healed, int heal)
|
||||
{
|
||||
base.OnHeal(healer, healed, heal);
|
||||
|
||||
if (heal > 0 && healer == healed && healer is PlayerMobile)
|
||||
{
|
||||
var p = (PlayerMobile)healer;
|
||||
var o = FindActiveMatch(p);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
if (MatchSuddenDeath > 0)
|
||||
{
|
||||
var t = o.Expire.TotalSeconds / o.Duration.TotalSeconds;
|
||||
|
||||
if (t < MatchSuddenDeath)
|
||||
{
|
||||
heal = (int)(heal * (t / MatchSuddenDeath));
|
||||
}
|
||||
}
|
||||
|
||||
o.RecordHeal(p, heal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDamage(Mobile attacker, Mobile damaged, int damage)
|
||||
{
|
||||
base.OnDamage(attacker, damaged, damage);
|
||||
|
||||
if (damage > 0 && attacker != damaged && attacker is PlayerMobile)
|
||||
{
|
||||
var p = (PlayerMobile)attacker;
|
||||
var o = FindActiveMatch(p);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
o.RecordDamage(p, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnSpectatorAdded(PlayerMobile pm)
|
||||
{
|
||||
base.OnSpectatorAdded(pm);
|
||||
|
||||
UpdateUI(pm, false);
|
||||
}
|
||||
|
||||
protected override void OnBattleStarted()
|
||||
{
|
||||
base.OnBattleStarted();
|
||||
|
||||
SyncMatches();
|
||||
|
||||
UpdateUI(true);
|
||||
}
|
||||
|
||||
protected override void OnBattleEnded()
|
||||
{
|
||||
SyncMatches();
|
||||
|
||||
Matches.RemoveAll(o => o.IsEmpty);
|
||||
|
||||
if (Matches.Count > 0)
|
||||
{
|
||||
var a = TournamentArchives.CreateArchive(this);
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
foreach (var pm in GetLocalBroadcastList())
|
||||
{
|
||||
new TournamentArchiveUI(pm, a).Send();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateUI(false);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnBattleEnded();
|
||||
}
|
||||
|
||||
protected override void OnBattleCancelled()
|
||||
{
|
||||
SyncMatches();
|
||||
|
||||
Matches.RemoveAll(o => o.IsEmpty);
|
||||
|
||||
base.OnBattleCancelled();
|
||||
}
|
||||
|
||||
protected override void OnReset()
|
||||
{
|
||||
Matches.Clear();
|
||||
|
||||
FinalBestOfCur = 0;
|
||||
|
||||
base.OnReset();
|
||||
}
|
||||
|
||||
protected override void OnMicroSync()
|
||||
{
|
||||
base.OnMicroSync();
|
||||
|
||||
SyncMatches();
|
||||
}
|
||||
|
||||
protected void SyncMatches()
|
||||
{
|
||||
if (!IsInternal)
|
||||
{
|
||||
ForEachTeam<TournamentTeam>(SyncMatch);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SyncMatch(TournamentTeam team)
|
||||
{
|
||||
var m = Matches.Find(o => o.Team == team.Serial.ValueHash && !o.IsComplete);
|
||||
|
||||
if (m == null || m.IsDisposed)
|
||||
{
|
||||
if (!IsRunning || CurrentCapacity <= CountActiveParticipants())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Matches.Add(m = new TournamentMatch(Matches.Count, this, team));
|
||||
}
|
||||
|
||||
m.Sync(this, team);
|
||||
}
|
||||
|
||||
public Queue<PlayerMobile> GetMatchQueue(int capacity)
|
||||
{
|
||||
var list = GetParticipants();
|
||||
|
||||
list = list.ToLookup(CountMatchesCompleted).Lowest(o => o.Key);
|
||||
|
||||
list = list.Not(InActiveMatch);
|
||||
list = list.Randomize();
|
||||
|
||||
if (capacity > 0)
|
||||
{
|
||||
list = list.Take(capacity);
|
||||
}
|
||||
|
||||
return list.ToQueue();
|
||||
}
|
||||
|
||||
public IEnumerable<PlayerMobile> GetMatchParticipants()
|
||||
{
|
||||
return Matches.SelectMany(o => o.Players.Where(p => p != null)).Distinct();
|
||||
}
|
||||
|
||||
public int CountActiveParticipants()
|
||||
{
|
||||
return Matches.Where(o => !o.IsComplete).Aggregate(0, (v, o) => v + o.CountAlive);
|
||||
}
|
||||
|
||||
public int CountMatchesWon(PlayerMobile pm)
|
||||
{
|
||||
return Matches.Count(o => o.IsComplete && o.Winner == pm);
|
||||
}
|
||||
|
||||
public int CountMatchesCompleted(PlayerMobile pm)
|
||||
{
|
||||
return Matches.Count(o => o.IsComplete && o.Contains(pm));
|
||||
}
|
||||
|
||||
public bool InActiveMatch(PlayerMobile pm)
|
||||
{
|
||||
return Matches.Any(o => !o.IsComplete && o.Contains(pm));
|
||||
}
|
||||
|
||||
public TournamentMatch FindActiveMatch(PlayerMobile pm)
|
||||
{
|
||||
return Matches.Find(o => !o.IsComplete && o.Contains(pm));
|
||||
}
|
||||
|
||||
public override bool CheckSkillUse(Mobile user, int skill)
|
||||
{
|
||||
if (!base.CheckSkillUse(user, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var o = FindActiveMatch(user as PlayerMobile);
|
||||
|
||||
return o != null && !o.IsDelayed;
|
||||
}
|
||||
|
||||
public override bool CheckSpellCast(Mobile caster, ISpell spell)
|
||||
{
|
||||
if (!base.CheckSpellCast(caster, spell))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var o = FindActiveMatch(caster as PlayerMobile);
|
||||
|
||||
return o != null && !o.IsDelayed;
|
||||
}
|
||||
|
||||
public override bool CheckAllowHarmful(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
var val = base.CheckAllowHarmful(m, target, out handled);
|
||||
|
||||
if (!handled || !val || !m.InRegion(BattleRegion) || !target.InRegion(BattleRegion))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
var pmS = m as PlayerMobile;
|
||||
var pmT = target as PlayerMobile;
|
||||
|
||||
var o = FindActiveMatch(pmS);
|
||||
|
||||
return o != null && !o.IsDelayed && o == FindActiveMatch(pmT);
|
||||
}
|
||||
|
||||
public override bool CheckAllowBeneficial(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
var val = base.CheckAllowBeneficial(m, target, out handled);
|
||||
|
||||
if (!handled || !val || !m.InRegion(BattleRegion) || !target.InRegion(BattleRegion))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
var pmS = m as PlayerMobile;
|
||||
var pmT = target as PlayerMobile;
|
||||
|
||||
var o = FindActiveMatch(pmS);
|
||||
|
||||
return o != null && !o.IsDelayed && o == FindActiveMatch(pmT);
|
||||
}
|
||||
|
||||
protected override int NotorietyHandler(PlayerMobile source, PlayerMobile target, out bool handled)
|
||||
{
|
||||
var noto = base.NotorietyHandler(source, target, out handled);
|
||||
|
||||
if (!handled || noto == BattleNotoriety.Bubble || noto == Notoriety.Invulnerable)
|
||||
{
|
||||
return noto;
|
||||
}
|
||||
|
||||
if (!source.InRegion(BattleRegion) || !target.InRegion(BattleRegion))
|
||||
{
|
||||
return noto;
|
||||
}
|
||||
|
||||
var o = FindActiveMatch(source);
|
||||
|
||||
if (o != null && !o.IsDelayed && o == FindActiveMatch(target))
|
||||
{
|
||||
return noto;
|
||||
}
|
||||
|
||||
return Notoriety.Invulnerable;
|
||||
}
|
||||
|
||||
public override TimeSpan GetStateTimeLeft(DateTime when, PvPBattleState state)
|
||||
{
|
||||
var time = base.GetStateTimeLeft(when, state);
|
||||
|
||||
if (state == PvPBattleState.Running)
|
||||
{
|
||||
var o = Matches.Where(r => r.IsRunning).Highest(r => r.Expire);
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
if (o.Expire > time)
|
||||
{
|
||||
time = o.Expire;
|
||||
}
|
||||
}
|
||||
else if (HasCapacity())
|
||||
{
|
||||
time = MatchDelay + MatchDuration;
|
||||
}
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
public IEnumerator<TournamentMatch> GetEnumerator()
|
||||
{
|
||||
return Matches.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(1);
|
||||
|
||||
writer.Write(MatchSuddenDeath);
|
||||
|
||||
writer.Write(MatchDelay);
|
||||
writer.Write(MatchDuration);
|
||||
|
||||
writer.Write(FinalBestOfCur);
|
||||
writer.Write(FinalBestOfMax);
|
||||
|
||||
writer.WriteBlockList(Matches, (w, r) => r.Serialize(w));
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var v = reader.GetVersion();
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
MatchSuddenDeath = reader.ReadDouble();
|
||||
}
|
||||
else
|
||||
{
|
||||
MatchSuddenDeath = 0.25;
|
||||
}
|
||||
|
||||
MatchDelay = reader.ReadTimeSpan();
|
||||
MatchDuration = reader.ReadTimeSpan();
|
||||
|
||||
FinalBestOfCur = reader.ReadInt();
|
||||
FinalBestOfMax = reader.ReadInt();
|
||||
|
||||
Matches = reader.ReadBlockList(r => new TournamentMatch(r), Matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TvTBattle : PvPBattle
|
||||
{
|
||||
public TvTBattle()
|
||||
{
|
||||
Name = "Team vs Team";
|
||||
Category = "Team vs Team";
|
||||
Description = "The last team alive wins!";
|
||||
|
||||
Ranked = true;
|
||||
RewardTeam = true;
|
||||
|
||||
AddTeam(NameList.RandomName("daemon"), 1, 1, 0x22);
|
||||
AddTeam(NameList.RandomName("daemon"), 1, 1, 0x55);
|
||||
|
||||
Schedule.Info.Months = ScheduleMonths.All;
|
||||
Schedule.Info.Days = ScheduleDays.All;
|
||||
Schedule.Info.Times = ScheduleTimes.EveryQuarterHour;
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(2.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(8.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(1.0);
|
||||
|
||||
Options.Rules.AllowBeneficial = true;
|
||||
Options.Rules.AllowHarmful = true;
|
||||
Options.Rules.AllowHousing = false;
|
||||
Options.Rules.AllowPets = false;
|
||||
Options.Rules.AllowSpawn = false;
|
||||
Options.Rules.AllowSpeech = true;
|
||||
Options.Rules.CanBeDamaged = true;
|
||||
Options.Rules.CanDamageEnemyTeam = true;
|
||||
Options.Rules.CanDamageOwnTeam = false;
|
||||
Options.Rules.CanDie = false;
|
||||
Options.Rules.CanHeal = true;
|
||||
Options.Rules.CanHealEnemyTeam = false;
|
||||
Options.Rules.CanHealOwnTeam = true;
|
||||
Options.Rules.CanMount = false;
|
||||
Options.Rules.CanFly = false;
|
||||
Options.Rules.CanResurrect = false;
|
||||
Options.Rules.CanUseStuckMenu = false;
|
||||
Options.Rules.CanEquip = true;
|
||||
}
|
||||
|
||||
public TvTBattle(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(1);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var v = reader.GetVersion();
|
||||
|
||||
if (v < 1)
|
||||
{
|
||||
RewardTeam = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TvTBattle1v1 : TvTBattle
|
||||
{
|
||||
private static readonly TimeSpan[] _Times =
|
||||
{
|
||||
new TimeSpan(0, 30, 0), new TimeSpan(2, 30, 0), new TimeSpan(4, 30, 0), new TimeSpan(6, 30, 0),
|
||||
new TimeSpan(8, 30, 0), new TimeSpan(10, 30, 0), new TimeSpan(13, 30, 0), new TimeSpan(15, 30, 0),
|
||||
new TimeSpan(17, 30, 0), new TimeSpan(19, 30, 0), new TimeSpan(21, 30, 0), new TimeSpan(23, 45, 0)
|
||||
};
|
||||
|
||||
public TvTBattle1v1()
|
||||
{
|
||||
Name = "1 vs 1";
|
||||
|
||||
Teams[0].MinCapacity = 1;
|
||||
Teams[0].MaxCapacity = 1;
|
||||
|
||||
Teams[1].MinCapacity = 1;
|
||||
Teams[1].MaxCapacity = 1;
|
||||
|
||||
Schedule.Info.Times.Clear();
|
||||
Schedule.Info.Times.Add(_Times);
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(4.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(8.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(10.0);
|
||||
}
|
||||
|
||||
public TvTBattle1v1(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TvTBattle2v2 : TvTBattle
|
||||
{
|
||||
private static readonly TimeSpan[] _Times =
|
||||
{
|
||||
new TimeSpan(0, 30, 0), new TimeSpan(2, 30, 0), new TimeSpan(4, 30, 0), new TimeSpan(6, 30, 0),
|
||||
new TimeSpan(8, 30, 0), new TimeSpan(10, 30, 0), new TimeSpan(13, 30, 0), new TimeSpan(15, 30, 0),
|
||||
new TimeSpan(17, 30, 0), new TimeSpan(19, 30, 0), new TimeSpan(21, 30, 0), new TimeSpan(23, 45, 0)
|
||||
};
|
||||
|
||||
public TvTBattle2v2()
|
||||
{
|
||||
Name = "2 vs 2";
|
||||
|
||||
Teams[0].MinCapacity = 1;
|
||||
Teams[0].MaxCapacity = 2;
|
||||
|
||||
Teams[1].MinCapacity = 1;
|
||||
Teams[1].MaxCapacity = 2;
|
||||
|
||||
Schedule.Info.Times.Clear();
|
||||
Schedule.Info.Times.Add(_Times);
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(4.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(8.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(10.0);
|
||||
}
|
||||
|
||||
public TvTBattle2v2(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TvTBattle3v3 : TvTBattle
|
||||
{
|
||||
private static readonly TimeSpan[] _Times =
|
||||
{
|
||||
new TimeSpan(1, 30, 0), new TimeSpan(3, 30, 0), new TimeSpan(5, 30, 0), new TimeSpan(7, 30, 0),
|
||||
new TimeSpan(9, 30, 0), new TimeSpan(11, 30, 0), new TimeSpan(14, 30, 0), new TimeSpan(16, 30, 0),
|
||||
new TimeSpan(18, 30, 0), new TimeSpan(20, 30, 0)
|
||||
};
|
||||
|
||||
public TvTBattle3v3()
|
||||
{
|
||||
Name = "3 vs 3";
|
||||
|
||||
Teams[0].MinCapacity = 1;
|
||||
Teams[0].MaxCapacity = 3;
|
||||
|
||||
Teams[1].MinCapacity = 1;
|
||||
Teams[1].MaxCapacity = 3;
|
||||
|
||||
Schedule.Info.Times.Clear();
|
||||
Schedule.Info.Times.Add(_Times);
|
||||
|
||||
Options.Broadcasts.World.MessageHue = 891;
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(3.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(12.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(10.0);
|
||||
}
|
||||
|
||||
public TvTBattle3v3(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP.Battles
|
||||
{
|
||||
public class TvTBattle5v5 : TvTBattle
|
||||
{
|
||||
private static readonly TimeSpan[] _Times =
|
||||
{
|
||||
new TimeSpan(1, 30, 0), new TimeSpan(3, 30, 0), new TimeSpan(5, 30, 0), new TimeSpan(7, 30, 0),
|
||||
new TimeSpan(9, 30, 0), new TimeSpan(11, 30, 0), new TimeSpan(14, 30, 0), new TimeSpan(16, 30, 0),
|
||||
new TimeSpan(18, 30, 0), new TimeSpan(20, 30, 0)
|
||||
};
|
||||
|
||||
public TvTBattle5v5()
|
||||
{
|
||||
Name = "5 vs 5";
|
||||
|
||||
Teams[0].MinCapacity = 1;
|
||||
Teams[0].MaxCapacity = 5;
|
||||
|
||||
Teams[1].MinCapacity = 1;
|
||||
Teams[1].MaxCapacity = 5;
|
||||
|
||||
Schedule.Info.Times.Clear();
|
||||
Schedule.Info.Times.Add(_Times);
|
||||
|
||||
Options.Broadcasts.World.MessageHue = 891;
|
||||
|
||||
Options.Timing.PreparePeriod = TimeSpan.FromMinutes(3.0);
|
||||
Options.Timing.RunningPeriod = TimeSpan.FromMinutes(12.0);
|
||||
Options.Timing.EndedPeriod = TimeSpan.FromMinutes(10.0);
|
||||
}
|
||||
|
||||
public TvTBattle5v5(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum AutoPvPStoneCommand
|
||||
{
|
||||
ViewBattles,
|
||||
ViewProfiles,
|
||||
GlobalConfig
|
||||
}
|
||||
|
||||
public class AutoPvPStone : Item
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public AutoPvPStoneCommand Command { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public KnownColor UsageColor { get; set; }
|
||||
|
||||
public override bool DisplayLootType => false;
|
||||
public override bool DisplayWeight => false;
|
||||
|
||||
[Constructable]
|
||||
public AutoPvPStone()
|
||||
: this(4963)
|
||||
{ }
|
||||
|
||||
[Constructable]
|
||||
public AutoPvPStone(int itemID)
|
||||
: base(itemID)
|
||||
{
|
||||
Command = AutoPvPStoneCommand.ViewBattles;
|
||||
UsageColor = KnownColor.SkyBlue;
|
||||
|
||||
Name = "PvP Battle Stone";
|
||||
LootType = LootType.Blessed;
|
||||
Weight = 0;
|
||||
}
|
||||
|
||||
public AutoPvPStone(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
public override void GetProperties(ObjectPropertyList list)
|
||||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
var color = Color.FromKnownColor(UsageColor).ToRgb();
|
||||
|
||||
switch (Command)
|
||||
{
|
||||
case AutoPvPStoneCommand.ViewBattles:
|
||||
{
|
||||
list.Add("<basefont color=#{0:X6}>Opens the PvP battles menu<basefont color=#ffffff>", color);
|
||||
}
|
||||
break;
|
||||
case AutoPvPStoneCommand.ViewProfiles:
|
||||
{
|
||||
list.Add("<basefont color=#{0:X6}>Opens the PvP profiles menu<basefont color=#ffffff>", color);
|
||||
}
|
||||
break;
|
||||
case AutoPvPStoneCommand.GlobalConfig:
|
||||
{
|
||||
list.Add("<basefont color=#{0:X6}>Use: Opens the PvP control panel<basefont color=#ffffff>", color);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDoubleClick(Mobile from)
|
||||
{
|
||||
if (from == null || from.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (Command)
|
||||
{
|
||||
case AutoPvPStoneCommand.ViewBattles:
|
||||
{
|
||||
AutoPvP.CMOptions.Advanced.Commands.InvokeBattlesCommand(from);
|
||||
}
|
||||
break;
|
||||
case AutoPvPStoneCommand.ViewProfiles:
|
||||
{
|
||||
AutoPvP.CMOptions.Advanced.Commands.InvokeProfilesCommand(from);
|
||||
}
|
||||
break;
|
||||
case AutoPvPStoneCommand.GlobalConfig:
|
||||
{
|
||||
AutoPvP.CMOptions.Advanced.Commands.InvokeConfigCommand(from);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteFlag(Command);
|
||||
writer.WriteFlag(UsageColor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Command = reader.ReadFlag<AutoPvPStoneCommand>();
|
||||
UsageColor = reader.ReadFlag<KnownColor>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.Items;
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPSpectatorGate : FloorTile<PlayerMobile>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPBattle Battle { get; set; }
|
||||
|
||||
public override bool ForceShowProperties => true;
|
||||
|
||||
public PvPSpectatorGate(PvPBattle battle)
|
||||
{
|
||||
Battle = battle;
|
||||
|
||||
Name = "PvP Battle Gate";
|
||||
ItemID = 19343;
|
||||
Hue = 51;
|
||||
Visible = true;
|
||||
Movable = false;
|
||||
}
|
||||
|
||||
public PvPSpectatorGate(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
public override void OnAosSingleClick(Mobile from)
|
||||
{
|
||||
OnSingleClick(from);
|
||||
}
|
||||
|
||||
public override void OnSingleClick(Mobile from)
|
||||
{
|
||||
base.OnSingleClick(from);
|
||||
|
||||
if (Battle == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LabelTo(from, "Battle: {0}", Battle.Name);
|
||||
LabelTo(from, "Status: {0} ({1})", Battle.State, Battle.GetStateTimeLeft().ToSimpleString("h:m:s"));
|
||||
}
|
||||
|
||||
public override void GetProperties(ObjectPropertyList list)
|
||||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
if (Battle != null)
|
||||
{
|
||||
list.Add(
|
||||
"Battle: {0}\nStatus: {1} ({2})",
|
||||
Battle.Name,
|
||||
Battle.State,
|
||||
Battle.GetStateTimeLeft().ToSimpleString("h:m:s"));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMoveOver(PlayerMobile mob)
|
||||
{
|
||||
if (!base.OnMoveOver(mob) || mob == null || Battle == null || Battle.Deleted || !Battle.SpectateAllowed || Battle.IsInternal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Timer.DelayCall(
|
||||
TimeSpan.FromSeconds(1),
|
||||
() =>
|
||||
{
|
||||
if (mob.X == X && mob.Y == Y)
|
||||
{
|
||||
new ConfirmDialogGump(mob)
|
||||
{
|
||||
Title = "Join as Spectator?",
|
||||
Html = "Join " + Battle.Name +
|
||||
" as a spectator.\nYou will be teleported to a safe area where you can watch the battle.\nClick OK to join!",
|
||||
AcceptHandler = b => Battle.AddSpectator(mob, true)
|
||||
}.Send();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.Items;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPTeamGate : FloorTile<PlayerMobile>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPTeam Team { get; set; }
|
||||
|
||||
public override bool ForceShowProperties => true;
|
||||
|
||||
public PvPTeamGate(PvPTeam team)
|
||||
{
|
||||
Team = team;
|
||||
|
||||
Name = "PvP Team Gate";
|
||||
Hue = Team.Color;
|
||||
ItemID = 19343;
|
||||
Visible = true;
|
||||
Movable = false;
|
||||
}
|
||||
|
||||
public PvPTeamGate(Serial serial)
|
||||
: base(serial)
|
||||
{ }
|
||||
|
||||
public override void OnAosSingleClick(Mobile from)
|
||||
{
|
||||
OnSingleClick(from);
|
||||
}
|
||||
|
||||
public override void OnSingleClick(Mobile from)
|
||||
{
|
||||
base.OnSingleClick(from);
|
||||
|
||||
if (Team == null || Team.Battle == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LabelTo(from, "Battle: {0}", Team.Battle.Name);
|
||||
LabelTo(from, "Team: {0}", Team.Name);
|
||||
LabelTo(from, "Status: {0} ({1})", Team.Battle.State, Team.Battle.GetStateTimeLeft().ToSimpleString("h:m:s"));
|
||||
}
|
||||
|
||||
public override void GetProperties(ObjectPropertyList list)
|
||||
{
|
||||
base.GetProperties(list);
|
||||
|
||||
if (Team != null && Team.Battle != null)
|
||||
{
|
||||
list.Add(
|
||||
"Battle: {0}\nTeam: {1}\nStatus: {2} ({3})",
|
||||
Team.Battle.Name,
|
||||
Team.Name,
|
||||
Team.Battle.State,
|
||||
Team.Battle.GetStateTimeLeft().ToSimpleString("h:m:s"));
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnMoveOver(PlayerMobile mob)
|
||||
{
|
||||
if (mob == null || !base.OnMoveOver(mob))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Team == null || Team.Deleted || Team.Battle == null || Team.Battle.Deleted || !Team.Battle.QueueAllowed || Team.Battle.IsInternal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Timer.DelayCall(
|
||||
TimeSpan.FromSeconds(1),
|
||||
() =>
|
||||
{
|
||||
if (mob.X == X && mob.Y == Y)
|
||||
{
|
||||
Team.Battle.Enqueue(mob, Team);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,858 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells;
|
||||
using Server.Targeting;
|
||||
|
||||
using VitaNex.Network;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public void Teleport(Mobile m, Point3D destLoc, Map destMap)
|
||||
{
|
||||
if (m == null || m.Deleted || destLoc == Point3D.Zero || destMap == Map.Internal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldLoc = m.Location;
|
||||
var oldMap = m.Map;
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null && !IsOnline(pm))
|
||||
{
|
||||
pm.LogoutLocation = destLoc;
|
||||
pm.LogoutMap = destMap;
|
||||
|
||||
if (!pm.Alive && pm.Corpse != null && !pm.Corpse.Deleted)
|
||||
{
|
||||
pm.Corpse.MoveToWorld(destLoc, destMap);
|
||||
}
|
||||
|
||||
OnTeleported(pm, oldLoc, oldMap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m.Hidden)
|
||||
{
|
||||
var fx = EffectItem.Create(m.Location, m.Map, EffectItem.DefaultDuration);
|
||||
|
||||
Effects.SendLocationParticles(fx, 0x3728, 10, 10, 5023);
|
||||
}
|
||||
|
||||
m.MoveToWorld(destLoc, destMap);
|
||||
m.Send(VNScreenLightFlash.Instance);
|
||||
SendSound(Options.Sounds.Teleport);
|
||||
|
||||
if (!m.Hidden)
|
||||
{
|
||||
var fx = EffectItem.Create(destLoc, destMap, EffectItem.DefaultDuration);
|
||||
|
||||
Effects.SendLocationParticles(fx, 0x3728, 10, 10, 5023);
|
||||
}
|
||||
|
||||
if (m.Location != destLoc || m.Map != destMap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m.Alive && m.Corpse != null && !m.Corpse.Deleted)
|
||||
{
|
||||
m.Corpse.MoveToWorld(m.Location, m.Map);
|
||||
}
|
||||
|
||||
OnTeleported(m, oldLoc, oldMap);
|
||||
}
|
||||
|
||||
public virtual void TeleportToEjectLocation(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted && !Options.Locations.Eject.Zero && !Options.Locations.Eject.Internal)
|
||||
{
|
||||
Teleport(pm, Options.Locations.Eject, Options.Locations.Eject);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void TeleportToSpectateLocation(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted && Options.Locations.SpectateJoin != Point3D.Zero && Options.Locations.Map != null &&
|
||||
Options.Locations.Map != Map.Internal)
|
||||
{
|
||||
Teleport(pm, Options.Locations.SpectateJoin, Options.Locations.Map);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void TeleportToHomeBase(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted && team != null && !team.Deleted)
|
||||
{
|
||||
Teleport(pm, team.HomeBase, Options.Locations.Map);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void TeleportToSpawnPoint(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted || team == null || team.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var p = team.SpawnPoint;
|
||||
|
||||
if (team.RespawnRangeMin > 0 && team.RespawnRangeMax > 0)
|
||||
{
|
||||
p = p.GetRandomPoint3D(team.RespawnRangeMin, team.RespawnRangeMax);
|
||||
}
|
||||
|
||||
Teleport(pm, p, Options.Locations.Map);
|
||||
}
|
||||
|
||||
protected virtual void OnTeleported(Mobile m, Point3D oldLocation, Map oldMap)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m.UpdateRegion();
|
||||
|
||||
if (m is PlayerMobile)
|
||||
{
|
||||
OnTeleported((PlayerMobile)m, oldLocation, oldMap);
|
||||
}
|
||||
|
||||
m.Delta(MobileDelta.Noto);
|
||||
m.ProcessDelta();
|
||||
}
|
||||
|
||||
protected virtual void OnTeleported(PlayerMobile pm, Point3D oldLocation, Map oldMap)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var old = Region.Find(oldLocation, oldMap);
|
||||
|
||||
if (!old.IsPartOf(BattleRegion))
|
||||
{
|
||||
if (pm.InRegion(BattleRegion))
|
||||
{
|
||||
Negate(pm);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!pm.InRegion(BattleRegion))
|
||||
{
|
||||
Negate(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckMounted(Mobile m, bool dismount)
|
||||
{
|
||||
if (m == null || m.Deleted || !m.Mounted || m.Mount == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dismount)
|
||||
{
|
||||
CheckDismount(m);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool CheckDismount(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (State == PvPBattleState.Internal || Hidden)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.Flying && Options.Rules.CanFly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.Mounted)
|
||||
{
|
||||
if (m.Mount is EtherealMount && Options.Rules.CanMountEthereal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Options.Rules.CanMount)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Dismount(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void Dismount(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.Flying)
|
||||
{
|
||||
m.Flying = false;
|
||||
OnDismounted(m, null);
|
||||
}
|
||||
else if (m.Mount != null)
|
||||
{
|
||||
var mount = m.Mount;
|
||||
mount.Rider = null;
|
||||
OnDismounted(m, mount);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDismounted(Mobile m, IMount mount)
|
||||
{
|
||||
if (m != null && !m.Deleted)
|
||||
{
|
||||
m.SendMessage(mount != null ? "You have been dismounted." : "You have been wing clipped.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowSpawn()
|
||||
{
|
||||
if (CheckAllowSpawn())
|
||||
{
|
||||
OnAllowSpawnAccept();
|
||||
return true;
|
||||
}
|
||||
|
||||
OnAllowSpawnDeny();
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckAllowSpawn()
|
||||
{
|
||||
return Options.Rules.AllowSpawn;
|
||||
}
|
||||
|
||||
protected virtual void OnAllowSpawnAccept()
|
||||
{ }
|
||||
|
||||
protected virtual void OnAllowSpawnDeny()
|
||||
{ }
|
||||
|
||||
public bool CanMoveThrough(Mobile m, IEntity e)
|
||||
{
|
||||
if (CheckCanMoveThrough(m, e))
|
||||
{
|
||||
OnCanMoveThroughAccept(m, e);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnCanMoveThroughDeny(m, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckCanMoveThrough(Mobile m, IEntity e)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if NEWENTITY
|
||||
return e == null || e.Deleted || Options.Rules.CanMoveThrough;
|
||||
#else
|
||||
return e == null || (e is Mobile && ((Mobile)e).Deleted) || (e is Item && ((Item)e).Deleted) ||
|
||||
Options.Rules.CanMoveThrough;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected virtual void OnCanMoveThroughAccept(Mobile m, IEntity e)
|
||||
{ }
|
||||
|
||||
protected virtual void OnCanMoveThroughDeny(Mobile m, IEntity e)
|
||||
{ }
|
||||
|
||||
public bool AllowHousing(Mobile m, Point3D p)
|
||||
{
|
||||
if (CheckAllowHousing(m, p))
|
||||
{
|
||||
OnAllowHousingAccept(m, p);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnAllowHousingDeny(m, p);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckAllowHousing(Mobile m, Point3D p)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Options.Rules.AllowHousing;
|
||||
}
|
||||
|
||||
protected virtual void OnAllowHousingAccept(Mobile m, Point3D p)
|
||||
{ }
|
||||
|
||||
protected virtual void OnAllowHousingDeny(Mobile m, Point3D p)
|
||||
{
|
||||
if (m != null && !m.Deleted)
|
||||
{
|
||||
m.SendMessage("You can not place structures here at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanUseStuckMenu(Mobile m)
|
||||
{
|
||||
if (CheckCanUseStuckMenu(m))
|
||||
{
|
||||
OnCanUseStuckMenuAccept(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnCanUseStuckMenuDeny(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckCanUseStuckMenu(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Options.Rules.CanUseStuckMenu;
|
||||
}
|
||||
|
||||
protected virtual void OnCanUseStuckMenuAccept(Mobile m)
|
||||
{ }
|
||||
|
||||
protected virtual void OnCanUseStuckMenuDeny(Mobile m)
|
||||
{
|
||||
if (m != null && !m.Deleted)
|
||||
{
|
||||
m.SendMessage("You can not use the stuck menu at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnBeforeDeath(Mobile m)
|
||||
{
|
||||
if (CheckDeath(m))
|
||||
{
|
||||
OnDeathAccept(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnDeathDeny(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckDeath(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Options.Rules.CanDie;
|
||||
}
|
||||
|
||||
protected virtual void OnDeathAccept(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team) && team != null && !team.Deleted)
|
||||
{
|
||||
team.OnMemberDeath(pm);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDeathDeny(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team) && team != null && !team.Deleted)
|
||||
{
|
||||
team.OnMemberDeath(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDeath(Mobile m)
|
||||
{ }
|
||||
|
||||
public bool OnSkillUse(Mobile user, int skill)
|
||||
{
|
||||
if (CheckSkillUse(user, skill))
|
||||
{
|
||||
OnSkillUseAccept(user, skill);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnSkillUseDeny(user, skill);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckSkillUse(Mobile user, int skill)
|
||||
{
|
||||
if (user == null || user.Deleted || skill < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && user.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return !Options.Restrictions.Skills.IsRestricted(skill);
|
||||
}
|
||||
|
||||
protected virtual void OnSkillUseAccept(Mobile user, int skill)
|
||||
{ }
|
||||
|
||||
protected virtual void OnSkillUseDeny(Mobile user, int skill)
|
||||
{
|
||||
if (user != null && !user.Deleted && skill >= 0)
|
||||
{
|
||||
user.SendMessage("You can not use that skill at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnBeginSpellCast(Mobile caster, ISpell spell)
|
||||
{
|
||||
if (CheckSpellCast(caster, spell))
|
||||
{
|
||||
OnSpellCastAccept(caster, spell);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnSpellCastDeny(caster, spell);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckSpellCast(Mobile caster, ISpell spell)
|
||||
{
|
||||
if (caster == null || caster.Deleted || spell == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && caster.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(spell is Spell))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Options.Rules.CanFly)
|
||||
{
|
||||
var t = spell.GetType();
|
||||
|
||||
if (t.Name.ContainsAny("FlySpell", "FlightSpell"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !Options.Restrictions.Spells.IsRestricted((Spell)spell);
|
||||
}
|
||||
|
||||
protected virtual void OnSpellCastAccept(Mobile caster, ISpell spell)
|
||||
{ }
|
||||
|
||||
protected virtual void OnSpellCastDeny(Mobile caster, ISpell spell)
|
||||
{
|
||||
if (caster != null && !caster.Deleted && spell != null)
|
||||
{
|
||||
caster.SendMessage("You can not use that spell at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckDamage(Mobile damaged, ref int damage)
|
||||
{
|
||||
if (damaged == null || damaged.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && damaged.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Options.Rules.CanBeDamaged;
|
||||
}
|
||||
|
||||
public virtual void OnDamage(Mobile damager, Mobile damaged, int damage)
|
||||
{
|
||||
if (damage <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerMobile pm;
|
||||
PvPTeam t;
|
||||
|
||||
if (damager != null && !damager.Deleted && damager is PlayerMobile)
|
||||
{
|
||||
pm = (PlayerMobile)damager;
|
||||
|
||||
if (IsParticipant(pm, out t))
|
||||
{
|
||||
var d = damage;
|
||||
|
||||
UpdateStatistics(t, pm, o => o.DamageDone += d);
|
||||
}
|
||||
}
|
||||
|
||||
if (damaged != null && !damaged.Deleted && damaged is PlayerMobile)
|
||||
{
|
||||
pm = (PlayerMobile)damaged;
|
||||
|
||||
if (IsParticipant(pm, out t))
|
||||
{
|
||||
var d = damage;
|
||||
|
||||
UpdateStatistics(t, pm, o => o.DamageTaken += d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CheckHeal(Mobile healed, ref int heal)
|
||||
{
|
||||
if (healed == null || healed.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && healed.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Options.Rules.CanHeal;
|
||||
}
|
||||
|
||||
public virtual void OnHeal(Mobile healer, Mobile healed, int heal)
|
||||
{
|
||||
if (heal <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerMobile pm;
|
||||
PvPTeam t;
|
||||
|
||||
if (healer != null && !healer.Deleted && healer is PlayerMobile)
|
||||
{
|
||||
pm = (PlayerMobile)healer;
|
||||
|
||||
if (IsParticipant(pm, out t))
|
||||
{
|
||||
var h = heal;
|
||||
|
||||
UpdateStatistics(t, pm, o => o.HealingDone += h);
|
||||
}
|
||||
}
|
||||
|
||||
if (healed != null && !healed.Deleted && healed is PlayerMobile)
|
||||
{
|
||||
pm = (PlayerMobile)healed;
|
||||
|
||||
if (IsParticipant(pm, out t))
|
||||
{
|
||||
var h = heal;
|
||||
|
||||
UpdateStatistics(t, pm, o => o.HealingTaken += h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnResurrect(Mobile m)
|
||||
{
|
||||
if (CheckResurrect(m))
|
||||
{
|
||||
OnResurrectAccept(m);
|
||||
return true;
|
||||
}
|
||||
|
||||
OnResurrectDeny(m);
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckResurrect(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && m.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Options.Rules.CanResurrect;
|
||||
}
|
||||
|
||||
protected virtual void OnResurrectAccept(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team) && team != null && !team.Deleted)
|
||||
{
|
||||
team.OnMemberResurrected(pm);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnResurrectDeny(Mobile m)
|
||||
{
|
||||
if (m != null && !m.Deleted)
|
||||
{
|
||||
m.SendMessage("You can not be resurrected at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSpeech(SpeechEventArgs args)
|
||||
{
|
||||
if (args.Mobile is PlayerMobile)
|
||||
{
|
||||
var pm = (PlayerMobile)args.Mobile;
|
||||
|
||||
if (HandleSubCommand(pm, args.Speech))
|
||||
{
|
||||
args.Handled = true;
|
||||
args.Blocked = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckSpeech(args))
|
||||
{
|
||||
OnSpeechAccept(args);
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
args.Blocked = true;
|
||||
|
||||
OnSpeechDeny(args);
|
||||
}
|
||||
|
||||
public virtual bool CheckSpeech(SpeechEventArgs e)
|
||||
{
|
||||
if (e.Mobile == null || e.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DebugMode && e.Mobile.AccessLevel >= AccessLevel.Counselor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Options.Rules.AllowSpeech;
|
||||
}
|
||||
|
||||
protected virtual void OnSpeechAccept(SpeechEventArgs args)
|
||||
{ }
|
||||
|
||||
protected virtual void OnSpeechDeny(SpeechEventArgs args)
|
||||
{
|
||||
if (args.Mobile != null && !args.Mobile.Deleted)
|
||||
{
|
||||
args.Mobile.SendMessage("You can not talk at this time.");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnSpellCast(Mobile m, ISpell s)
|
||||
{ }
|
||||
|
||||
public virtual void OnAggressed(Mobile aggressor, Mobile aggressed, bool criminal)
|
||||
{ }
|
||||
|
||||
public virtual void OnBeneficialAction(Mobile helper, Mobile target)
|
||||
{ }
|
||||
|
||||
public virtual bool OnCombatantChange(Mobile m, Mobile oldMob, Mobile newMob)
|
||||
{
|
||||
return m != null && !m.Deleted;
|
||||
}
|
||||
|
||||
public virtual void OnCriminalAction(Mobile m, bool message)
|
||||
{ }
|
||||
|
||||
public virtual void OnDidHarmful(Mobile harmer, Mobile harmed)
|
||||
{ }
|
||||
|
||||
public virtual bool OnDoubleClick(Mobile m, object o)
|
||||
{
|
||||
if (o is Item)
|
||||
{
|
||||
return OnDoubleClick(m, (Item)o);
|
||||
}
|
||||
|
||||
if (o is Mobile)
|
||||
{
|
||||
return OnDoubleClick(m, (Mobile)o);
|
||||
}
|
||||
|
||||
return m != null && !m.Deleted && o != null;
|
||||
}
|
||||
|
||||
public virtual bool OnDoubleClick(Mobile m, Item item)
|
||||
{
|
||||
return m != null && !m.Deleted && item != null && !item.Deleted && CanUseItem(m, item, true);
|
||||
}
|
||||
|
||||
public virtual bool OnDoubleClick(Mobile m, Mobile target)
|
||||
{
|
||||
if (target is BaseCreature)
|
||||
{
|
||||
return OnDoubleClick(m, (BaseCreature)target);
|
||||
}
|
||||
|
||||
return m != null && !m.Deleted && target != null && !target.Deleted && CanUseMobile(m, target, true);
|
||||
}
|
||||
|
||||
public virtual bool OnDoubleClick(Mobile m, BaseCreature target)
|
||||
{
|
||||
return m != null && !m.Deleted && target != null && !target.Deleted && CanUseMobile(m, target, true);
|
||||
}
|
||||
|
||||
public virtual bool OnSingleClick(Mobile m, object o)
|
||||
{
|
||||
if (o is Item)
|
||||
{
|
||||
return OnSingleClick(m, (Item)o);
|
||||
}
|
||||
|
||||
if (o is Mobile)
|
||||
{
|
||||
return OnSingleClick(m, (Mobile)o);
|
||||
}
|
||||
|
||||
return m != null && !m.Deleted && o != null;
|
||||
}
|
||||
|
||||
public virtual bool OnSingleClick(Mobile m, Item item)
|
||||
{
|
||||
return m != null && !m.Deleted && item != null && !item.Deleted && CanUseItem(m, item, false);
|
||||
}
|
||||
|
||||
public virtual bool OnSingleClick(Mobile m, Mobile target)
|
||||
{
|
||||
if (target is BaseCreature)
|
||||
{
|
||||
return OnSingleClick(m, (BaseCreature)target);
|
||||
}
|
||||
|
||||
return m != null && !m.Deleted && target != null && !target.Deleted && CanUseMobile(m, target, false);
|
||||
}
|
||||
|
||||
public virtual bool OnSingleClick(Mobile m, BaseCreature target)
|
||||
{
|
||||
return m != null && !m.Deleted && target != null && !target.Deleted && CanUseMobile(m, target, false);
|
||||
}
|
||||
|
||||
public virtual void OnGotBeneficialAction(Mobile helper, Mobile target)
|
||||
{ }
|
||||
|
||||
public virtual void OnGotHarmful(Mobile harmer, Mobile harmed)
|
||||
{ }
|
||||
|
||||
public virtual bool OnTarget(Mobile m, Target target, object o)
|
||||
{
|
||||
return m != null && !m.Deleted && target != null && o != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual TimeSpan LogoutDelay { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual TimeSpan IdleThreshold { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool IdleKick { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool InviteWhileRunning { get; set; }
|
||||
|
||||
public virtual Dictionary<PlayerMobile, MapPoint> BounceInfo { get; private set; }
|
||||
|
||||
public virtual bool InOtherBattle(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var battle = AutoPvP.FindBattle(pm);
|
||||
|
||||
return battle != null && battle != this && battle.IsParticipant(pm);
|
||||
}
|
||||
|
||||
public bool IsAliveParticipant(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsParticipant(pm, out var team) && !team.IsDead(pm);
|
||||
}
|
||||
|
||||
public bool IsDeadParticipant(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsParticipant(pm, out var team) && team.IsDead(pm);
|
||||
}
|
||||
|
||||
public bool IsParticipant(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsParticipant(pm, out var team);
|
||||
}
|
||||
|
||||
public bool IsParticipant(PlayerMobile pm, out PvPTeam team)
|
||||
{
|
||||
if (pm == null)
|
||||
{
|
||||
team = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
team = FindTeam(pm);
|
||||
|
||||
return team != null;
|
||||
}
|
||||
|
||||
public virtual TimeSpan GetLogoutDelay(Mobile m)
|
||||
{
|
||||
return LogoutDelay;
|
||||
}
|
||||
|
||||
public IEnumerable<PlayerMobile> GetParticipants()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted).SelectMany(t => t.Where(p => p != null && !p.Deleted));
|
||||
}
|
||||
|
||||
public virtual bool CanSendInvites()
|
||||
{
|
||||
return !Hidden && (IsPreparing || (IsRunning && InviteWhileRunning)) && CurrentCapacity < MaxCapacity;
|
||||
}
|
||||
|
||||
public virtual bool CanSendInvite(PlayerMobile pm)
|
||||
{
|
||||
return pm != null && !pm.Deleted && pm.Alive && !pm.InRegion<Jail>() && pm.DesignContext == null && IsOnline(pm) &&
|
||||
!InCombat(pm) && IsQueued(pm) && !IsParticipant(pm) && !InOtherBattle(pm) && !AutoPvP.IsDeserter(pm);
|
||||
}
|
||||
|
||||
public virtual void SendInvites()
|
||||
{
|
||||
foreach (var pm in Queue.Keys)
|
||||
{
|
||||
var invites = SuperGump.GetInstances<PvPInviteGump>(pm);
|
||||
|
||||
if (CanSendInvite(pm))
|
||||
{
|
||||
var sendNew = invites.All(invite => !invite.IsOpen || invite.Battle != this);
|
||||
|
||||
if (sendNew)
|
||||
{
|
||||
OnSendInvite(pm, new PvPInviteGump(pm, this));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var invite in invites.Where(invite => invite.IsOpen && invite.Battle == this))
|
||||
{
|
||||
invite.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnSendInvite(PlayerMobile pm, PvPInviteGump invite)
|
||||
{
|
||||
if (pm == null || pm.Deleted || !pm.Alive || invite == null || invite.IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
invite.Send();
|
||||
|
||||
SendSound(pm, Options.Sounds.InviteSend);
|
||||
}
|
||||
|
||||
public void AcceptInvite(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted || !pm.Alive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (CanSendInvites() && CanSendInvite(pm))
|
||||
{
|
||||
OnInviteAccept(pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnInviteRejected(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeclineInvite(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted)
|
||||
{
|
||||
OnInviteDecline(pm);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnInviteAccept(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted || !IsOnline(pm) || IsInternal || Hidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsQueued(pm))
|
||||
{
|
||||
pm.SendMessage("You are not queued for {0}", Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm))
|
||||
{
|
||||
pm.SendMessage("You are already participating in {0}", Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (InOtherBattle(pm))
|
||||
{
|
||||
pm.SendMessage("You cannot join {0} while you are in another battle.", Name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsFull)
|
||||
{
|
||||
pm.SendMessage("The battle is full, you will be sent an invite if someone leaves.");
|
||||
return;
|
||||
}
|
||||
|
||||
var team = Queue[pm];
|
||||
|
||||
if (team == null || team.Deleted) // Assume AutoAssign is true
|
||||
{
|
||||
if (Teams.Count == 1)
|
||||
{
|
||||
team = Teams[0];
|
||||
}
|
||||
else if (AutoAssign) // Make sure AutoAssign is true
|
||||
{
|
||||
team = GetAutoAssignTeam(pm);
|
||||
}
|
||||
}
|
||||
|
||||
if (team == null || team.Deleted) // Fallback to most empty, or random team
|
||||
{
|
||||
team = GetMostEmptyTeam();
|
||||
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
team = GetRandomTeam();
|
||||
}
|
||||
}
|
||||
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
pm.SendMessage("The team you've chosen seems to have vanished in the void, sorry about that.");
|
||||
Queue.Remove(pm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (team.IsFull)
|
||||
{
|
||||
pm.SendMessage("The team you've chosen is full, you will be sent an invite if someone leaves.");
|
||||
return;
|
||||
}
|
||||
|
||||
Queue.Remove(pm);
|
||||
|
||||
RecordBounce(pm);
|
||||
|
||||
team.AddMember(pm, true);
|
||||
|
||||
SendSound(pm, Options.Sounds.InviteAccept);
|
||||
}
|
||||
|
||||
protected virtual void OnInviteDecline(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage("You decide not to join {0}", Name);
|
||||
SendSound(pm, Options.Sounds.InviteCancel);
|
||||
|
||||
if (IsQueued(pm))
|
||||
{
|
||||
Dequeue(pm);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnInviteRejected(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage("You can not join {0} at this time.", Name);
|
||||
SendSound(pm, Options.Sounds.InviteCancel);
|
||||
}
|
||||
|
||||
public void RecordBounce(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted || pm.InRegion<PvPBattleRegion>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bounce = pm.ToMapPoint();
|
||||
|
||||
if (bounce != null && !bounce.InternalOrZero)
|
||||
{
|
||||
BounceInfo[pm] = bounce;
|
||||
}
|
||||
}
|
||||
|
||||
public void Quit(Mobile m, bool teleport)
|
||||
{
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
if (IsParticipant(pm, out var team))
|
||||
{
|
||||
UpdateStatistics(team, pm, o => ++o.Losses);
|
||||
|
||||
if (IsRunning)
|
||||
{
|
||||
OnDeserted(team, pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Eject(m, teleport);
|
||||
}
|
||||
|
||||
public void Eject(Mobile m, bool teleport)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m is PlayerMobile)
|
||||
{
|
||||
Eject((PlayerMobile)m, teleport);
|
||||
}
|
||||
else if (m is BaseCreature)
|
||||
{
|
||||
Eject((BaseCreature)m, teleport);
|
||||
}
|
||||
}
|
||||
|
||||
public void Eject(PlayerMobile pm, bool teleport)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team))
|
||||
{
|
||||
if (IsRunning || IsEnded)
|
||||
{
|
||||
UpdateStatistics(team, pm, o => ++o.Battles);
|
||||
}
|
||||
|
||||
team.RemoveMember(pm, false);
|
||||
}
|
||||
else if (IsSpectator(pm))
|
||||
{
|
||||
RemoveSpectator(pm, false);
|
||||
}
|
||||
|
||||
if (teleport)
|
||||
{
|
||||
var bounce = BounceInfo.GetValue(pm);
|
||||
|
||||
if (bounce != null && !bounce.InternalOrZero)
|
||||
{
|
||||
Teleport(pm, bounce, bounce);
|
||||
|
||||
BounceInfo.Remove(pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Teleport(pm, Options.Locations.Eject, Options.Locations.Eject.Map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Eject(BaseCreature bc, bool teleportOrStable)
|
||||
{
|
||||
if (bc == null || bc.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pet = bc.IsControlled<PlayerMobile>();
|
||||
|
||||
if (!teleportOrStable)
|
||||
{
|
||||
if (!pet)
|
||||
{
|
||||
bc.Delete();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pet || !bc.Stable())
|
||||
{
|
||||
Teleport(bc, Options.Locations.Eject, Options.Locations.Eject);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnEjected(Mobile m)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m is PlayerMobile)
|
||||
{
|
||||
OnEjected((PlayerMobile)m);
|
||||
}
|
||||
else if (m is BaseCreature)
|
||||
{
|
||||
OnEjected((BaseCreature)m);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnEjected(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted)
|
||||
{
|
||||
pm.SendMessage("You have been ejected from the battle.");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnEjected(BaseCreature bc)
|
||||
{ }
|
||||
|
||||
protected virtual void OnDeserted(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage(0x22, "You have deserted {0}!", Name);
|
||||
|
||||
AutoPvP.AddDeserter(pm);
|
||||
|
||||
RevokePoints(pm);
|
||||
|
||||
UpdateStatistics(team, pm, o => ++o["Deserted"]);
|
||||
|
||||
WorldBroadcast("{0} has deserted {1}!", pm.RawName, Name);
|
||||
}
|
||||
|
||||
public virtual void InvalidateStray(Mobile m)
|
||||
{
|
||||
if (IsInternal || Hidden || m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m is PlayerMobile)
|
||||
{
|
||||
InvalidateStrayPlayer((PlayerMobile)m);
|
||||
}
|
||||
else if (m is BaseCreature)
|
||||
{
|
||||
var bc = (BaseCreature)m;
|
||||
|
||||
if (bc.IsControlled())
|
||||
{
|
||||
InvalidateStrayPet(bc);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidateStraySpawn(bc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateStrayPlayer(PlayerMobile player)
|
||||
{
|
||||
if (IsInternal || Hidden || player == null || player.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.InRegion(SpectateRegion))
|
||||
{
|
||||
if (IsParticipant(player))
|
||||
{
|
||||
Eject(player, false);
|
||||
}
|
||||
|
||||
if (!IsSpectator(player))
|
||||
{
|
||||
AddSpectator(player, false);
|
||||
}
|
||||
}
|
||||
else if (player.InRegion(BattleRegion))
|
||||
{
|
||||
if (IsParticipant(player))
|
||||
{
|
||||
if (IsSpectator(player))
|
||||
{
|
||||
RemoveSpectator(player, false);
|
||||
}
|
||||
|
||||
Queue.Remove(player);
|
||||
|
||||
if (DebugMode || player.AccessLevel < AccessLevel.Counselor)
|
||||
{
|
||||
if (player.Flying && !Options.Rules.CanFly)
|
||||
{
|
||||
player.Flying = false;
|
||||
}
|
||||
|
||||
if (player.Mounted)
|
||||
{
|
||||
var canMount = Options.Rules.CanMount;
|
||||
|
||||
if (player.Mount is EtherealMount)
|
||||
{
|
||||
canMount = Options.Rules.CanMountEthereal;
|
||||
}
|
||||
|
||||
if (!canMount)
|
||||
{
|
||||
player.Dismount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!IsSpectator(player))
|
||||
{
|
||||
if ((DebugMode || player.AccessLevel < AccessLevel.Counselor) && SpectateAllowed)
|
||||
{
|
||||
AddSpectator(player, true);
|
||||
}
|
||||
else if (DebugMode || player.AccessLevel < AccessLevel.Counselor)
|
||||
{
|
||||
Eject(player, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateStraySpawn(BaseCreature mob)
|
||||
{
|
||||
if (IsInternal || Hidden || mob == null || mob.Deleted || !mob.InRegion(BattleRegion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AllowSpawn())
|
||||
{
|
||||
Eject(mob, mob.IsControlled<PlayerMobile>());
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateStrayPet(BaseCreature pet)
|
||||
{
|
||||
if (IsInternal || Hidden || pet == null || pet.Deleted || !pet.InRegion(BattleRegion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Options.Rules.AllowPets)
|
||||
{
|
||||
var master = pet.GetMaster<PlayerMobile>();
|
||||
|
||||
if (master == null || DebugMode || master.AccessLevel < AccessLevel.Counselor)
|
||||
{
|
||||
Eject(pet, master != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateGates()
|
||||
{
|
||||
InvalidateSpectateGate();
|
||||
InvalidateTeamGates();
|
||||
}
|
||||
|
||||
public virtual void InvalidateSpectateGate()
|
||||
{
|
||||
if (!SpectateAllowed || SpectateRegion == null || IsInternal || Options.Locations.SpectateGate.InternalOrZero)
|
||||
{
|
||||
if (Gate != null)
|
||||
{
|
||||
Gate.Delete();
|
||||
Gate = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Gate == null || Gate.Deleted)
|
||||
{
|
||||
Gate = new PvPSpectatorGate(this);
|
||||
|
||||
if (!Options.Locations.SpectateGate.MoveToWorld(Gate))
|
||||
{
|
||||
Gate.MoveToWorld(Options.Locations.SpectateGate, Options.Locations.SpectateGate);
|
||||
}
|
||||
}
|
||||
|
||||
if (Gate.Battle == null)
|
||||
{
|
||||
Gate.Battle = this;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void InvalidateTeamGates()
|
||||
{
|
||||
ForEachTeam(t => t.InvalidateGate());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#if ServUO58
|
||||
#define ServUOX
|
||||
#endif
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Commands;
|
||||
using Server.Mobiles;
|
||||
using Server.Network;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum PvPBattleWarning
|
||||
{
|
||||
Starting,
|
||||
Ending,
|
||||
WBStarting,
|
||||
WBEnding
|
||||
}
|
||||
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public virtual IEnumerable<PlayerMobile> GetLocalBroadcastList()
|
||||
{
|
||||
if (BattleRegion != null)
|
||||
{
|
||||
#if ServUOX
|
||||
foreach (var pm in BattleRegion.AllPlayers.OfType<PlayerMobile>().Where(IsOnline))
|
||||
#else
|
||||
foreach (var pm in BattleRegion.GetMobiles().OfType<PlayerMobile>().Where(IsOnline))
|
||||
#endif
|
||||
{
|
||||
yield return pm;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpectateRegion != null)
|
||||
{
|
||||
#if ServUOX
|
||||
foreach (var pm in SpectateRegion.AllPlayers.OfType<PlayerMobile>().Where(IsOnline))
|
||||
#else
|
||||
foreach (var pm in SpectateRegion.GetMobiles().OfType<PlayerMobile>().Where(IsOnline))
|
||||
#endif
|
||||
{
|
||||
yield return pm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<PlayerMobile> GetWorldBroadcastList()
|
||||
{
|
||||
return NetState.Instances.Where(state => state != null)
|
||||
.Select(state => state.Mobile as PlayerMobile)
|
||||
.Where(pm => pm != null && !pm.Deleted)
|
||||
.Where(pm => IsOnline(pm) && AutoPvP.EnsureProfile(pm).IsSubscribed(this));
|
||||
}
|
||||
|
||||
public virtual void LocalBroadcast(string message, params object[] args)
|
||||
{
|
||||
var text = String.Format(message, args);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.Mode == PvPBattleLocalBroadcastMode.Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AutoPvP.InvokeBattleLocalBroadcast(this, text);
|
||||
|
||||
foreach (var pm in GetLocalBroadcastList())
|
||||
{
|
||||
pm.SendMessage(IsParticipant(pm, out var team) ? team.Color : Options.Broadcasts.Local.MessageHue, text);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void WorldBroadcast(string message, params object[] args)
|
||||
{
|
||||
var text = String.Format(message, args);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.World.Mode == PvPBattleWorldBroadcastMode.Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AutoPvP.InvokeBattleWorldBroadcast(this, text);
|
||||
|
||||
switch (Options.Broadcasts.World.Mode)
|
||||
{
|
||||
case PvPBattleWorldBroadcastMode.Notify:
|
||||
{
|
||||
foreach (var pm in GetWorldBroadcastList())
|
||||
{
|
||||
pm.SendNotification(text, true, 0.5, 10.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PvPBattleWorldBroadcastMode.Broadcast:
|
||||
{
|
||||
var p = new AsciiMessage(Server.Serial.MinusOne, -1, MessageType.Regular, Options.Broadcasts.World.MessageHue, 3, "System", text);
|
||||
|
||||
p.Acquire();
|
||||
|
||||
foreach (var pm in GetWorldBroadcastList())
|
||||
{
|
||||
pm.Send(p);
|
||||
}
|
||||
|
||||
p.Release();
|
||||
|
||||
NetState.FlushAll();
|
||||
}
|
||||
break;
|
||||
case PvPBattleWorldBroadcastMode.TownCrier:
|
||||
{
|
||||
foreach (var tc in TownCrier.Instances)
|
||||
{
|
||||
tc.PublicOverheadMessage(MessageType.Yell, Options.Broadcasts.World.MessageHue, true, String.Format(message, args));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BroadcastStateHandler()
|
||||
{
|
||||
if (Hidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var state = State;
|
||||
var timeLeft = GetStateTimeLeft(DateTime.UtcNow).Add(TimeSpan.FromSeconds(1.0));
|
||||
|
||||
if (timeLeft <= TimeSpan.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PvPBattleState.Ended:
|
||||
BroadcastOpenMessage(timeLeft);
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
BroadcastStartMessage(timeLeft);
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
BroadcastEndMessage(timeLeft);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BroadcastOpenMessage(TimeSpan timeLeft)
|
||||
{
|
||||
if (timeLeft.Minutes > 5 || timeLeft.Minutes == 0 || timeLeft.Seconds != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = String.Format("{0} {1}", timeLeft.Minutes, timeLeft.Minutes != 1 ? "minutes" : "minute");
|
||||
|
||||
if (String.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.OpenNotify)
|
||||
{
|
||||
LocalBroadcast("{0} will open in {1}!", Name, msg);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.World.OpenNotify)
|
||||
{
|
||||
var cmd = String.Empty;
|
||||
|
||||
if (QueueAllowed)
|
||||
{
|
||||
cmd = AutoPvP.CMOptions.Advanced.Commands.BattlesCommand;
|
||||
cmd = String.Format("Use {0}{1} to join!", CommandSystem.Prefix, cmd);
|
||||
}
|
||||
|
||||
WorldBroadcast("{0} will open in {1}! {2}", Name, msg, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BroadcastStartMessage(TimeSpan timeLeft)
|
||||
{
|
||||
if ((timeLeft.Minutes == 0 && timeLeft.Seconds > 10) || timeLeft.Minutes > 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = String.Empty;
|
||||
|
||||
if (timeLeft.Minutes > 0)
|
||||
{
|
||||
if (timeLeft.Seconds == 0)
|
||||
{
|
||||
msg = String.Format("{0} {1}", timeLeft.Minutes, timeLeft.Minutes != 1 ? "minutes" : "minute");
|
||||
}
|
||||
}
|
||||
else if (timeLeft.Seconds > 0)
|
||||
{
|
||||
msg = String.Format("{0} {1}", timeLeft.Seconds, timeLeft.Seconds != 1 ? "seconds" : "second");
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.StartNotify)
|
||||
{
|
||||
LocalBroadcast("{0} will start in {1}!", Name, msg);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.World.StartNotify && timeLeft.Minutes > 0)
|
||||
{
|
||||
var cmd = String.Empty;
|
||||
|
||||
if (QueueAllowed)
|
||||
{
|
||||
cmd = AutoPvP.CMOptions.Advanced.Commands.BattlesCommand;
|
||||
cmd = String.Format("Use {0}{1} to join!", CommandSystem.Prefix, cmd);
|
||||
}
|
||||
|
||||
WorldBroadcast("{0} will start in {1}! {2}", Name, msg, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void BroadcastEndMessage(TimeSpan timeLeft)
|
||||
{
|
||||
if ((timeLeft.Minutes == 0 && timeLeft.Seconds > 10) || timeLeft.Minutes > 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = String.Empty;
|
||||
|
||||
if (timeLeft.Minutes > 0)
|
||||
{
|
||||
if (timeLeft.Seconds == 0)
|
||||
{
|
||||
msg = String.Format("{0} {1}", timeLeft.Minutes, timeLeft.Minutes != 1 ? "minutes" : "minute");
|
||||
}
|
||||
}
|
||||
else if (timeLeft.Seconds > 0)
|
||||
{
|
||||
msg = String.Format("{0} {1}", timeLeft.Seconds, timeLeft.Seconds != 1 ? "seconds" : "second");
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(msg))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.EndNotify)
|
||||
{
|
||||
LocalBroadcast("{0} will end in {1}!", Name, msg);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.World.EndNotify && timeLeft.Minutes > 0)
|
||||
{
|
||||
WorldBroadcast("{0} will end in {1}", Name, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int KillPoints { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int PointsBase { get; set; }
|
||||
|
||||
public virtual int GetAwardPoints(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
if (!IsRunning || team == null || team.Deleted || (pm != null && !team.IsMember(pm)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PointsBase;
|
||||
}
|
||||
|
||||
public virtual void AwardPoints(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null)
|
||||
{
|
||||
AwardPoints(pm, GetAwardPoints(FindTeam(pm), pm));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AwardPoints(PlayerMobile pm, int points)
|
||||
{
|
||||
if (pm != null && points > 0)
|
||||
{
|
||||
if (IsParticipant(pm, out var t))
|
||||
{
|
||||
UpdateStatistics(t, pm, o => o.PointsGained += points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RevokePoints(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null)
|
||||
{
|
||||
RevokePoints(pm, GetAwardPoints(FindTeam(pm), pm));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RevokePoints(PlayerMobile pm, int points)
|
||||
{
|
||||
if (pm != null && points > 0)
|
||||
{
|
||||
if (IsParticipant(pm, out var t))
|
||||
{
|
||||
UpdateStatistics(t, pm, o => o.PointsLost += points);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AwardTeamPoints(PvPTeam team)
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
team.ForEachMember(pm => UpdateStatistics(team, pm, o => o.PointsGained += GetAwardPoints(team, pm)));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AwardTeamPoints(PvPTeam team, int points)
|
||||
{
|
||||
if (team != null && points > 0)
|
||||
{
|
||||
team.ForEachMember(pm => UpdateStatistics(team, pm, o => o.PointsGained += points));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RevokeTeamPoints(PvPTeam team)
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
team.ForEachMember(pm => UpdateStatistics(team, pm, o => o.PointsLost += GetAwardPoints(team, pm)));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RevokeTeamPoints(PvPTeam team, int points)
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
team.ForEachMember(pm => UpdateStatistics(team, pm, o => o.PointsLost += points));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Engines.PartySystem;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public Dictionary<PlayerMobile, PvPTeam> Queue { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool QueueAllowed { get; set; }
|
||||
|
||||
public virtual IEnumerable<PlayerMobile> GetQueued(PvPTeam team)
|
||||
{
|
||||
foreach (var kvp in Queue.Where(kvp => kvp.Value == team))
|
||||
{
|
||||
yield return kvp.Key;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void EnqueueParty(PlayerMobile pm, Party party, PvPTeam team = null)
|
||||
{
|
||||
if (pm == null || pm.Deleted || party == null || !party.Active || party.Leader != pm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var players = party.Members.Where(pmi => pmi != null && pmi.Mobile != pm)
|
||||
.Select(pmi => pmi.Mobile as PlayerMobile)
|
||||
.Where(m => m != null);
|
||||
|
||||
foreach (var m in players)
|
||||
{
|
||||
Enqueue(m, team, false);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Enqueue(PlayerMobile pm, PvPTeam team = null, bool party = true)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanQueue(pm))
|
||||
{
|
||||
OnQueueReject(pm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (team != null && team.Deleted)
|
||||
{
|
||||
team = null;
|
||||
}
|
||||
|
||||
if (!IsQueued(pm))
|
||||
{
|
||||
Queue.Add(pm, team);
|
||||
OnQueueJoin(pm, team);
|
||||
}
|
||||
else
|
||||
{
|
||||
Queue[pm] = team;
|
||||
OnQueueUpdate(pm, team);
|
||||
}
|
||||
|
||||
if (party)
|
||||
{
|
||||
EnqueueParty(pm, Party.Get(pm), team);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Dequeue(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var team = Queue.GetValue(pm);
|
||||
|
||||
if (Queue.Remove(pm))
|
||||
{
|
||||
OnQueueLeave(pm, team);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanQueue(PlayerMobile pm)
|
||||
{
|
||||
return !IsInternal && QueueAllowed && IsOnline(pm) && pm.Alive && !InCombat(pm) && !IsQueued(pm) &&
|
||||
!InOtherBattle(pm) && !AutoPvP.IsDeserter(pm) && !pm.InRegion<Jail>();
|
||||
}
|
||||
|
||||
public virtual bool IsQueued(PlayerMobile pm)
|
||||
{
|
||||
return pm != null && Queue.ContainsKey(pm);
|
||||
}
|
||||
|
||||
protected virtual void OnQueueJoin(PlayerMobile pm, PvPTeam team)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage(
|
||||
"You have joined the queue for {0}{1}",
|
||||
Name,
|
||||
team != null ? ", your team is " + team.Name : String.Empty);
|
||||
|
||||
SendSound(pm, Options.Sounds.QueueJoin);
|
||||
|
||||
AutoPvP.InvokeQueueJoin(this, team, pm);
|
||||
}
|
||||
|
||||
protected virtual void OnQueueUpdate(PlayerMobile pm, PvPTeam team)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage(
|
||||
"Your queue status for {0} has changed{1}",
|
||||
Name,
|
||||
team != null ? ", your team is " + team.Name : String.Empty);
|
||||
|
||||
SendSound(pm, Options.Sounds.QueueJoin);
|
||||
|
||||
AutoPvP.InvokeQueueUpdate(this, team, pm);
|
||||
}
|
||||
|
||||
protected virtual void OnQueueLeave(PlayerMobile pm, PvPTeam team)
|
||||
{
|
||||
if (pm == null || pm.Deleted || IsParticipant(pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pm.SendMessage("You have left the queue for {0}", Name);
|
||||
|
||||
SendSound(pm, Options.Sounds.QueueLeave);
|
||||
|
||||
AutoPvP.InvokeQueueLeave(this, team, pm);
|
||||
}
|
||||
|
||||
protected virtual void OnQueueReject(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsQueued(pm))
|
||||
{
|
||||
pm.SendMessage("You are already in the queue for {0}", Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
pm.SendMessage("You can not queue for {0} at this time.", Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
private PvPBattleRegion _BattleRegion;
|
||||
private PvPSpectateRegion _SpectateRegion;
|
||||
|
||||
private bool _FloorItemDelete = true;
|
||||
private int _LightLevel = 100;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleRegion BattleRegion
|
||||
{
|
||||
get => _BattleRegion;
|
||||
set
|
||||
{
|
||||
if (_BattleRegion == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_BattleRegion = value;
|
||||
|
||||
if (!Deserializing)
|
||||
{
|
||||
InvalidateBattleRegion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPSpectateRegion SpectateRegion
|
||||
{
|
||||
get => _SpectateRegion;
|
||||
set
|
||||
{
|
||||
if (_SpectateRegion == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_SpectateRegion = value;
|
||||
|
||||
if (!Deserializing)
|
||||
{
|
||||
InvalidateSpectateRegion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool FloorItemDelete
|
||||
{
|
||||
get => _BattleRegion == null ? _FloorItemDelete : (_FloorItemDelete = _BattleRegion.FloorItemDelete);
|
||||
set => _FloorItemDelete = _BattleRegion == null ? value : (_BattleRegion.FloorItemDelete = value);
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int LightLevel { get => _LightLevel; set => _LightLevel = Math.Max(0, Math.Min(100, value)); }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual string BattleRegionName => String.Format("{0} ({1})", Name, Serial);
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual string SpectateRegionName => String.Format("{0} (Safe) ({1})", Name, Serial);
|
||||
|
||||
public virtual void InvalidateRegions()
|
||||
{
|
||||
if (Deserializing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateBattleRegion();
|
||||
InvalidateSpectateRegion();
|
||||
}
|
||||
|
||||
public virtual void InvalidateBattleRegion()
|
||||
{
|
||||
if (Deserializing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_BattleRegion != null)
|
||||
{
|
||||
if (_BattleRegion.Map == Map &&
|
||||
_BattleRegion.Area.GetBoundsHashCode() == Options.Locations.BattleBounds.GetBoundsHashCode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_BattleRegion.Unregister();
|
||||
}
|
||||
|
||||
if (Options.Locations.BattleFixedPoint == Point3D.Zero)
|
||||
{
|
||||
_BattleRegion = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_BattleRegion = _BattleRegion != null ? _BattleRegion.Clone(this) : RegionExtUtility.Create<PvPBattleRegion>(this);
|
||||
|
||||
if (_BattleRegion == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_BattleRegion.GoLocation = Options.Locations.BattleFixedPoint;
|
||||
_BattleRegion.Register();
|
||||
}
|
||||
|
||||
public virtual void InvalidateSpectateRegion()
|
||||
{
|
||||
if (Deserializing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_SpectateRegion != null)
|
||||
{
|
||||
if (_SpectateRegion.Map == Map && _SpectateRegion.Area.GetBoundsHashCode() ==
|
||||
Options.Locations.SpectateBounds.GetBoundsHashCode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_SpectateRegion.Unregister();
|
||||
}
|
||||
|
||||
if (Options.Locations.SpectateFixedPoint == Point3D.Zero)
|
||||
{
|
||||
_SpectateRegion = null;
|
||||
return;
|
||||
}
|
||||
|
||||
_SpectateRegion = _SpectateRegion != null
|
||||
? _SpectateRegion.Clone(this)
|
||||
: RegionExtUtility.Create<PvPSpectateRegion>(this);
|
||||
|
||||
if (_SpectateRegion == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_SpectateRegion.GoLocation = Options.Locations.SpectateFixedPoint;
|
||||
_SpectateRegion.Register();
|
||||
}
|
||||
|
||||
public virtual int NotorietyHandler(Mobile source, Mobile target, out bool handled)
|
||||
{
|
||||
handled = false;
|
||||
|
||||
if (IsInternal || Hidden)
|
||||
{
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
if (source == null || source.Deleted || target == null || target.Deleted)
|
||||
{
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
||||
if (NotoUtility.Resolve(source, target, out PlayerMobile x, out PlayerMobile y))
|
||||
{
|
||||
var noto = NotorietyHandler(x, y, out handled);
|
||||
|
||||
if (handled || noto != BattleNotoriety.Bubble)
|
||||
{
|
||||
return noto;
|
||||
}
|
||||
}
|
||||
|
||||
var xrs = source.InRegion(SpectateRegion);
|
||||
var xrb = source.InRegion(BattleRegion);
|
||||
|
||||
var yrs = target.InRegion(SpectateRegion);
|
||||
var yrb = target.InRegion(BattleRegion);
|
||||
|
||||
if (xrs || xrb || yrs || yrb)
|
||||
{
|
||||
return Notoriety.Invulnerable;
|
||||
}
|
||||
|
||||
handled = false;
|
||||
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
protected virtual int NotorietyHandler(PlayerMobile source, PlayerMobile target, out bool handled)
|
||||
{
|
||||
handled = false;
|
||||
|
||||
if (IsInternal || Hidden)
|
||||
{
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
if (source == null || source.Deleted || target == null || target.Deleted)
|
||||
{
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
if (IsParticipant(source, out var teamA) && IsParticipant(target, out var teamB))
|
||||
{
|
||||
handled = true;
|
||||
|
||||
if (!IsRunning)
|
||||
{
|
||||
return Notoriety.Invulnerable;
|
||||
}
|
||||
|
||||
if (teamA == teamB)
|
||||
{
|
||||
if (CanDamageOwnTeam(source, target))
|
||||
{
|
||||
return Notoriety.Enemy;
|
||||
}
|
||||
|
||||
return Notoriety.Ally;
|
||||
}
|
||||
|
||||
if (CanDamageEnemyTeam(source, target))
|
||||
{
|
||||
return Notoriety.Enemy;
|
||||
}
|
||||
|
||||
return Notoriety.Invulnerable;
|
||||
}
|
||||
|
||||
return BattleNotoriety.Bubble;
|
||||
}
|
||||
|
||||
public virtual bool AcceptsSpawnsFrom(Region region)
|
||||
{
|
||||
return AllowSpawn() && region != null && (region.IsPartOf(BattleRegion) || region.IsPartOf(SpectateRegion));
|
||||
}
|
||||
|
||||
public virtual void AlterLightLevel(Mobile m, ref int global, ref int personal)
|
||||
{
|
||||
personal = personal != LightLevel ? LightLevel : personal;
|
||||
}
|
||||
|
||||
public virtual void OnLocationChanged(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
if (m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.Region != null && (m.Region.IsPartOf(SpectateRegion) || m.Region.IsPartOf(BattleRegion)))
|
||||
{
|
||||
CheckDismount(m);
|
||||
InvalidateStray(m);
|
||||
}
|
||||
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team) && team != null && !team.Deleted)
|
||||
{
|
||||
team.UpdateActivity(pm);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void OnEnter(PvPRegion region, Mobile m)
|
||||
{
|
||||
if (region == null || m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (region.IsPartOf(BattleRegion) && m.InRegion(BattleRegion))
|
||||
{
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
pm.SendMessage("You have entered {0}", Name);
|
||||
}
|
||||
|
||||
AutoPvP.InvokeEnterBattle(this, region, m);
|
||||
}
|
||||
else if (region.IsPartOf(SpectateRegion) && m.InRegion(SpectateRegion))
|
||||
{
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
pm.SendMessage("You have entered {0} spectator area.", Name);
|
||||
|
||||
if (!IsSpectator(pm))
|
||||
{
|
||||
AddSpectator(pm, false);
|
||||
}
|
||||
}
|
||||
|
||||
AutoPvP.InvokeEnterBattle(this, region, m);
|
||||
}
|
||||
|
||||
m.Delta(MobileDelta.Noto);
|
||||
}
|
||||
|
||||
public virtual void OnExit(PvPRegion region, Mobile m)
|
||||
{
|
||||
if (region == null || m == null || m.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (region.IsPartOf(BattleRegion) && !m.InRegion(BattleRegion))
|
||||
{
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
if (IsParticipant(pm))
|
||||
{
|
||||
Quit(pm, false);
|
||||
}
|
||||
|
||||
pm.SendMessage("You have left {0}", Name);
|
||||
}
|
||||
|
||||
AutoPvP.InvokeExitBattle(this, region, m);
|
||||
}
|
||||
else if (region.IsPartOf(SpectateRegion) && !m.InRegion(SpectateRegion))
|
||||
{
|
||||
var pm = m as PlayerMobile;
|
||||
|
||||
if (pm != null)
|
||||
{
|
||||
pm.SendMessage("You have left {0} spectator area", Name);
|
||||
|
||||
if (IsSpectator(pm))
|
||||
{
|
||||
RemoveSpectator(pm, false);
|
||||
}
|
||||
}
|
||||
|
||||
AutoPvP.InvokeExitBattle(this, region, m);
|
||||
}
|
||||
|
||||
m.Delta(MobileDelta.Noto);
|
||||
}
|
||||
|
||||
public bool AllowBeneficial(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
if (CheckAllowBeneficial(m, target, out handled))
|
||||
{
|
||||
if (handled)
|
||||
{
|
||||
OnAllowBeneficialAccept(m, target);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
OnAllowBeneficialDeny(m, target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckAllowBeneficial(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
handled = false;
|
||||
|
||||
if (m == null || m.Deleted || target == null || target.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Deleted || State == PvPBattleState.Internal || Hidden)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
||||
if (NotoUtility.Resolve(m, target, out PlayerMobile x, out PlayerMobile y))
|
||||
{
|
||||
if (IsParticipant(x, out var teamA) && IsParticipant(y, out var teamB))
|
||||
{
|
||||
if (State != PvPBattleState.Running)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Options.Rules.AllowBeneficial)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (teamA == teamB)
|
||||
{
|
||||
if (!CanHealOwnTeam(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!CanHealEnemyTeam(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var xrs = m.InRegion(SpectateRegion);
|
||||
var xrb = m.InRegion(BattleRegion);
|
||||
|
||||
var yrs = target.InRegion(SpectateRegion);
|
||||
var yrb = target.InRegion(BattleRegion);
|
||||
|
||||
if (xrs || xrb || yrs || yrb)
|
||||
{
|
||||
if (xrs && yrs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (xrb && yrb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handled = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void OnAllowBeneficialAccept(Mobile m, Mobile target)
|
||||
{ }
|
||||
|
||||
protected virtual void OnAllowBeneficialDeny(Mobile m, Mobile target)
|
||||
{
|
||||
if (m != null && !m.Deleted && target != null && !target.Deleted && m != target)
|
||||
{
|
||||
m.SendMessage("You can not perform beneficial actions on your target.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllowHarmful(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
if (CheckAllowHarmful(m, target, out handled))
|
||||
{
|
||||
if (handled)
|
||||
{
|
||||
OnAllowHarmfulAccept(m, target);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
OnAllowHarmfulDeny(m, target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool CheckAllowHarmful(Mobile m, Mobile target, out bool handled)
|
||||
{
|
||||
handled = false;
|
||||
|
||||
if (m == null || m.Deleted || target == null || target.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Deleted || State == PvPBattleState.Internal || Hidden)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
||||
if (NotoUtility.Resolve(m, target, out PlayerMobile x, out PlayerMobile y))
|
||||
{
|
||||
if (IsParticipant(x, out var teamA) && IsParticipant(y, out var teamB))
|
||||
{
|
||||
if (!Options.Rules.AllowHarmful)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (State != PvPBattleState.Running)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (teamA == teamB)
|
||||
{
|
||||
if (!CanDamageOwnTeam(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!CanDamageEnemyTeam(x, y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var xrs = m.InRegion(SpectateRegion);
|
||||
var xrb = m.InRegion(BattleRegion);
|
||||
|
||||
var yrs = target.InRegion(SpectateRegion);
|
||||
var yrb = target.InRegion(BattleRegion);
|
||||
|
||||
if (xrs || xrb || yrs || yrb)
|
||||
{
|
||||
if (xrs && yrs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (xrb && yrb)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
handled = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void OnAllowHarmfulAccept(Mobile m, Mobile target)
|
||||
{ }
|
||||
|
||||
protected virtual void OnAllowHarmfulDeny(Mobile m, Mobile target)
|
||||
{
|
||||
if (m != null && !m.Deleted && target != null && !target.Deleted && m != target)
|
||||
{
|
||||
m.SendMessage("You can not perform harmful actions on your target.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
using Server.Items;
|
||||
|
||||
using VitaNex.Crypto;
|
||||
using VitaNex.Schedules;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public sealed class PvPSerial : CryptoHashCode
|
||||
{
|
||||
public static CryptoHashType Algorithm = CryptoHashType.MD5;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public override string Value => base.Value.Replace("-", String.Empty);
|
||||
|
||||
public PvPSerial()
|
||||
: this(TimeStamp.UtcNow + "+" + Utility.RandomDouble())
|
||||
{ }
|
||||
|
||||
public PvPSerial(string seed)
|
||||
: base(Algorithm, seed)
|
||||
{ }
|
||||
|
||||
public PvPSerial(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
}
|
||||
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public PvPSerial Serial { get; private set; }
|
||||
|
||||
protected bool Deserialized { get; private set; }
|
||||
protected bool Deserializing { get; private set; }
|
||||
|
||||
private PvPBattle(bool deserializing)
|
||||
{
|
||||
Deserialized = deserializing;
|
||||
|
||||
EnsureConstructDefaults();
|
||||
}
|
||||
|
||||
public PvPBattle(GenericReader reader)
|
||||
: this(true)
|
||||
{
|
||||
Deserializing = true;
|
||||
|
||||
Deserialize(reader);
|
||||
|
||||
Deserializing = false;
|
||||
}
|
||||
|
||||
public virtual void SerializeRegion(GenericWriter w, PvPRegion r)
|
||||
{
|
||||
if (r != null)
|
||||
{
|
||||
r.Serialize(w);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(10);
|
||||
|
||||
if (version > 5)
|
||||
{
|
||||
writer.WriteBlock(
|
||||
w =>
|
||||
{
|
||||
if (version > 6)
|
||||
{
|
||||
Serial.Serialize(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.WriteType(Serial, t => Serial.Serialize(w));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 10:
|
||||
writer.Write(Shortcut);
|
||||
goto case 9;
|
||||
case 9:
|
||||
writer.Write(RewardTeam);
|
||||
goto case 8;
|
||||
case 8:
|
||||
writer.Write(RequireCapacity);
|
||||
goto case 7;
|
||||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
writer.Write(Hidden);
|
||||
goto case 4;
|
||||
case 4:
|
||||
writer.Write(_FloorItemDelete);
|
||||
goto case 3;
|
||||
case 3:
|
||||
case 2:
|
||||
writer.Write(Gate);
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
writer.Write(Category);
|
||||
writer.Write(Ranked);
|
||||
writer.Write(InviteWhileRunning);
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
if (version < 6)
|
||||
{
|
||||
writer.WriteBlock(w => w.WriteType(Serial, t => Serial.Serialize(w)));
|
||||
}
|
||||
|
||||
writer.Write(DebugMode);
|
||||
writer.WriteFlag(_State);
|
||||
writer.Write(_Name);
|
||||
writer.Write(Description);
|
||||
writer.Write(AutoAssign);
|
||||
writer.Write(UseTeamColors);
|
||||
writer.Write(false); // IgnoreCapacity
|
||||
writer.Write(_SubCommandPrefix);
|
||||
writer.Write(QueueAllowed);
|
||||
writer.Write(SpectateAllowed);
|
||||
writer.Write(KillPoints);
|
||||
writer.Write(PointsBase);
|
||||
writer.Write(0.0); // PointsRankFactor
|
||||
writer.Write(IdleKick);
|
||||
writer.Write(IdleThreshold);
|
||||
writer.WriteFlag(LastState);
|
||||
writer.Write(LastStateChange);
|
||||
writer.Write(_LightLevel);
|
||||
writer.Write(LogoutDelay);
|
||||
|
||||
writer.WriteItemList(Doors, true);
|
||||
|
||||
writer.WriteBlock(w => w.WriteType(Options, t => Options.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Schedule, t => Schedule.Serialize(w)));
|
||||
|
||||
writer.WriteBlock(w => w.WriteType(BattleRegion, t => SerializeRegion(w, BattleRegion)));
|
||||
writer.WriteBlock(w => w.WriteType(SpectateRegion, t => SerializeRegion(w, SpectateRegion)));
|
||||
|
||||
writer.WriteBlockList(Teams, (w, team) => w.WriteType(team, t => team.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
var v = reader.GetVersion();
|
||||
|
||||
if (v > 5)
|
||||
{
|
||||
Serial = reader.ReadBlock(r => v > 6 ? new PvPSerial(r) : r.ReadTypeCreate<PvPSerial>(r)) ?? new PvPSerial();
|
||||
}
|
||||
|
||||
switch (v)
|
||||
{
|
||||
case 10:
|
||||
Shortcut = reader.ReadString();
|
||||
goto case 9;
|
||||
case 9:
|
||||
RewardTeam = reader.ReadBool();
|
||||
goto case 8;
|
||||
case 8:
|
||||
RequireCapacity = reader.ReadBool();
|
||||
goto case 7;
|
||||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
Hidden = reader.ReadBool();
|
||||
goto case 4;
|
||||
case 4:
|
||||
_FloorItemDelete = reader.ReadBool();
|
||||
goto case 3;
|
||||
case 3:
|
||||
case 2:
|
||||
{
|
||||
Gate = reader.ReadItem<PvPSpectatorGate>();
|
||||
|
||||
if (Gate != null)
|
||||
{
|
||||
Gate.Battle = this;
|
||||
}
|
||||
}
|
||||
goto case 1;
|
||||
case 1:
|
||||
{
|
||||
Category = reader.ReadString();
|
||||
Ranked = reader.ReadBool();
|
||||
InviteWhileRunning = reader.ReadBool();
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
if (v < 6)
|
||||
{
|
||||
Serial = reader.ReadBlock(r => r.ReadTypeCreate<PvPSerial>(r)) ?? new PvPSerial();
|
||||
}
|
||||
|
||||
DebugMode = reader.ReadBool();
|
||||
_State = reader.ReadFlag<PvPBattleState>();
|
||||
_Name = reader.ReadString();
|
||||
Description = reader.ReadString();
|
||||
AutoAssign = reader.ReadBool();
|
||||
UseTeamColors = reader.ReadBool();
|
||||
reader.ReadBool(); // IgnoreCapacity
|
||||
_SubCommandPrefix = reader.ReadChar();
|
||||
QueueAllowed = reader.ReadBool();
|
||||
SpectateAllowed = reader.ReadBool();
|
||||
KillPoints = v < 3 ? (reader.ReadBool() ? 1 : 0) : reader.ReadInt();
|
||||
PointsBase = reader.ReadInt();
|
||||
reader.ReadDouble(); //PointsRankFactor
|
||||
IdleKick = reader.ReadBool();
|
||||
IdleThreshold = reader.ReadTimeSpan();
|
||||
LastState = reader.ReadFlag<PvPBattleState>();
|
||||
LastStateChange = reader.ReadDateTime();
|
||||
_LightLevel = reader.ReadInt();
|
||||
LogoutDelay = reader.ReadTimeSpan();
|
||||
|
||||
Doors.AddRange(reader.ReadStrongItemList<BaseDoor>());
|
||||
|
||||
Options = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleOptions>(r)) ?? new PvPBattleOptions();
|
||||
|
||||
if (Schedule != null && Schedule.Running)
|
||||
{
|
||||
Schedule.Stop();
|
||||
}
|
||||
|
||||
Schedule = reader.ReadBlock(r => r.ReadTypeCreate<Schedule>(r)) ?? new Schedule(Name, false);
|
||||
|
||||
if (Schedule.Name != Name && Name != null)
|
||||
{
|
||||
Schedule.Name = Name;
|
||||
}
|
||||
|
||||
BattleRegion = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleRegion>(this, r));
|
||||
SpectateRegion = reader.ReadBlock(r => r.ReadTypeCreate<PvPSpectateRegion>(this, r));
|
||||
|
||||
Teams = reader.ReadBlockList(r => r.ReadTypeCreate<PvPTeam>(this, r), Teams);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public void SendGlobalSound(int soundID)
|
||||
{
|
||||
if (!Options.Sounds.Enabled || soundID <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var m in GetLocalBroadcastList())
|
||||
{
|
||||
SendSound(m, soundID);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendSound(int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && soundID > 0)
|
||||
{
|
||||
ForEachTeam(t => SendSound(t, soundID));
|
||||
}
|
||||
}
|
||||
|
||||
public void SendSound(PvPTeam t, int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && t != null && !t.Deleted && soundID > 0)
|
||||
{
|
||||
t.SendSound(soundID);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SendSound(Mobile m, int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && m != null && !m.Deleted && soundID > 0)
|
||||
{
|
||||
m.SendSound(soundID);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayGlobalSound(int soundID)
|
||||
{
|
||||
if (!Options.Sounds.Enabled || soundID <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var m in GetLocalBroadcastList())
|
||||
{
|
||||
PlaySound(m, soundID);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaySound(int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && soundID > 0)
|
||||
{
|
||||
ForEachTeam(t => PlaySound(t, soundID));
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaySound(PvPTeam t, int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && t != null && !t.Deleted && soundID > 0)
|
||||
{
|
||||
t.PlaySound(soundID);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void PlaySound(Mobile m, int soundID)
|
||||
{
|
||||
if (Options.Sounds.Enabled && m != null && !m.Deleted && soundID > 0)
|
||||
{
|
||||
m.PlaySound(soundID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public List<PlayerMobile> Spectators { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool SpectateAllowed { get; set; }
|
||||
|
||||
public IEnumerable<PlayerMobile> GetSpectators()
|
||||
{
|
||||
return Spectators;
|
||||
}
|
||||
|
||||
public void AddSpectator(PlayerMobile pm, bool teleport)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CanSpectate(pm))
|
||||
{
|
||||
OnSpectateReject(pm);
|
||||
return;
|
||||
}
|
||||
|
||||
Spectators.Add(pm);
|
||||
|
||||
if (teleport)
|
||||
{
|
||||
RecordBounce(pm);
|
||||
|
||||
Teleport(pm, Options.Locations.SpectateJoin, Options.Locations.Map);
|
||||
}
|
||||
|
||||
OnSpectatorAdded(pm);
|
||||
}
|
||||
|
||||
public void RemoveSpectator(PlayerMobile pm, bool teleport)
|
||||
{
|
||||
if (!IsSpectator(pm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Spectators.Remove(pm);
|
||||
|
||||
if (teleport)
|
||||
{
|
||||
var bounce = BounceInfo.GetValue(pm);
|
||||
|
||||
if (bounce != null && !bounce.InternalOrZero)
|
||||
{
|
||||
Teleport(pm, bounce, bounce);
|
||||
|
||||
BounceInfo.Remove(pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
Teleport(pm, Options.Locations.Eject, Options.Locations.Eject);
|
||||
}
|
||||
}
|
||||
|
||||
OnSpectatorRemoved(pm);
|
||||
}
|
||||
|
||||
public virtual bool CanSpectate(PlayerMobile pm)
|
||||
{
|
||||
return SpectateAllowed && State != PvPBattleState.Internal && pm != null && !pm.Deleted && pm.Alive &&
|
||||
(pm.Region == null || !pm.Region.IsPartOf<Jail>()) && IsOnline(pm) && !InCombat(pm) && !InOtherBattle(pm);
|
||||
}
|
||||
|
||||
public bool IsSpectator(PlayerMobile pm)
|
||||
{
|
||||
return Spectators.Contains(pm);
|
||||
}
|
||||
|
||||
protected virtual void OnSpectatorAdded(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted)
|
||||
{
|
||||
pm.SendMessage("You have been granted a front-row seat.");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnSpectatorRemoved(PlayerMobile pm)
|
||||
{ }
|
||||
|
||||
protected virtual void OnSpectateReject(PlayerMobile pm)
|
||||
{
|
||||
if (pm != null && !pm.Deleted && IsSpectator(pm))
|
||||
{
|
||||
pm.SendMessage("You can not spectate {0} at this time.", Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum PvPBattleState
|
||||
{
|
||||
Internal,
|
||||
Queueing,
|
||||
Preparing,
|
||||
Running,
|
||||
Ended
|
||||
}
|
||||
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
private bool _StateTransition;
|
||||
|
||||
private PvPBattleState _State = PvPBattleState.Internal;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleState State
|
||||
{
|
||||
get => _State;
|
||||
set
|
||||
{
|
||||
if (_State == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldState = _State;
|
||||
|
||||
_State = value;
|
||||
|
||||
if (!Deserializing)
|
||||
{
|
||||
OnStateChanged(oldState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public virtual PvPBattleState LastState { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public virtual DateTime LastStateChange { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsInternal => State == PvPBattleState.Internal;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsQueueing => State == PvPBattleState.Queueing;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsPreparing => State == PvPBattleState.Preparing;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsRunning => State == PvPBattleState.Running;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool IsEnded => State == PvPBattleState.Ended;
|
||||
|
||||
public void InvalidateState()
|
||||
{
|
||||
if (_StateTransition || Deleted || Hidden || IsInternal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var state = State;
|
||||
var reset = false;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PvPBattleState.Queueing:
|
||||
{
|
||||
if (CanPrepareBattle(ref reset))
|
||||
{
|
||||
state = PvPBattleState.Preparing;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
{
|
||||
if (CanStartBattle(ref reset))
|
||||
{
|
||||
state = PvPBattleState.Running;
|
||||
}
|
||||
else if (!reset && GetStateTimeLeft(state) <= TimeSpan.Zero)
|
||||
{
|
||||
state = PvPBattleState.Ended;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
{
|
||||
if (CanEndBattle(ref reset))
|
||||
{
|
||||
state = PvPBattleState.Ended;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PvPBattleState.Ended:
|
||||
{
|
||||
if (CanOpenBattle(ref reset))
|
||||
{
|
||||
state = PvPBattleState.Queueing;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (reset)
|
||||
{
|
||||
Options.Timing.SetTime(State, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
State = state;
|
||||
}
|
||||
|
||||
protected virtual void OnStateChanged(PvPBattleState oldState)
|
||||
{
|
||||
_StateTransition = true;
|
||||
|
||||
LastState = oldState;
|
||||
|
||||
if (IsInternal)
|
||||
{
|
||||
Options.Timing.SetAllTimes(DateTime.MinValue);
|
||||
|
||||
OnBattleInternalized();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LastState == PvPBattleState.Internal)
|
||||
{
|
||||
InvalidateRegions();
|
||||
}
|
||||
|
||||
Options.Timing.SetTime(State, DateTime.UtcNow);
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case PvPBattleState.Queueing:
|
||||
OnBattleOpened();
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
OnBattlePreparing();
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
OnBattleStarted();
|
||||
break;
|
||||
case PvPBattleState.Ended:
|
||||
OnBattleEnded();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LastStateChange = DateTime.UtcNow;
|
||||
|
||||
_StateTransition = false;
|
||||
|
||||
AutoPvP.InvokeBattleStateChanged(this);
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected bool CanOpenBattle(ref bool timeReset)
|
||||
{
|
||||
if (!IsEnded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetStateTimeLeft(PvPBattleState.Ended) > TimeSpan.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool CanPrepareBattle(ref bool timeReset)
|
||||
{
|
||||
if (!IsQueueing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetStateTimeLeft(PvPBattleState.Queueing) > TimeSpan.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RequireCapacity || Queue.Count >= MinCapacity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Schedule == null || !Schedule.Enabled || Schedule.NextGlobalTick == null)
|
||||
{
|
||||
timeReset = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool CanStartBattle(ref bool timeReset)
|
||||
{
|
||||
if (!IsPreparing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetStateTimeLeft(PvPBattleState.Preparing) > TimeSpan.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Teams.All(t => t.IsReady()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Schedule == null || !Schedule.Enabled || Schedule.NextGlobalTick == null)
|
||||
{
|
||||
timeReset = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool CanEndBattle(ref bool timeReset)
|
||||
{
|
||||
if (!IsRunning)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetStateTimeLeft(PvPBattleState.Running) <= TimeSpan.Zero)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!InviteWhileRunning && !HasCapacity())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Teams.All(t => !t.RespawnOnDeath && !t.IsAlive))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (CheckMissions())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void OnBattleInternalized()
|
||||
{
|
||||
if (LastState == PvPBattleState.Running)
|
||||
{
|
||||
OnBattleCancelled();
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
foreach (var p in AutoPvP.Profiles.Values.Where(p => p != null && !p.Deleted && p.IsSubscribed(this)))
|
||||
{
|
||||
p.Unsubscribe(this);
|
||||
}
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected virtual void OnBattleOpened()
|
||||
{
|
||||
Hidden = false;
|
||||
|
||||
if (Options.Broadcasts.World.OpenNotify)
|
||||
{
|
||||
WorldBroadcast("{0} is queueing volunteers!", Name);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.OpenNotify)
|
||||
{
|
||||
LocalBroadcast("{0} is queueing volunteers!", Name);
|
||||
}
|
||||
|
||||
SendGlobalSound(Options.Sounds.BattleOpened);
|
||||
|
||||
Reset();
|
||||
|
||||
ForEachTeam(t => t.OnBattleOpened());
|
||||
|
||||
if (LastState == PvPBattleState.Internal)
|
||||
{
|
||||
foreach (var p in AutoPvP.Profiles.Values.Where(p => p != null && !p.Deleted && !p.IsSubscribed(this)))
|
||||
{
|
||||
p.Subscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected virtual void OnBattlePreparing()
|
||||
{
|
||||
Hidden = false;
|
||||
|
||||
if (Options.Broadcasts.World.StartNotify)
|
||||
{
|
||||
WorldBroadcast("{0} is preparing!", Name);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.StartNotify)
|
||||
{
|
||||
LocalBroadcast("{0} is preparing!", Name);
|
||||
}
|
||||
|
||||
SendGlobalSound(Options.Sounds.BattlePreparing);
|
||||
|
||||
ForEachTeam(t => t.OnBattlePreparing());
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected virtual void OnBattleStarted()
|
||||
{
|
||||
Hidden = false;
|
||||
|
||||
if (Options.Broadcasts.World.StartNotify)
|
||||
{
|
||||
WorldBroadcast("{0} has begun!", Name);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.StartNotify)
|
||||
{
|
||||
LocalBroadcast("{0} has begun!", Name);
|
||||
}
|
||||
|
||||
SendGlobalSound(Options.Sounds.BattleStarted);
|
||||
|
||||
OpendDoors(true);
|
||||
|
||||
ForEachTeam(t => t.OnBattleStarted());
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected virtual void OnBattleEnded()
|
||||
{
|
||||
if (LastState == PvPBattleState.Preparing)
|
||||
{
|
||||
OnBattleCancelled();
|
||||
}
|
||||
|
||||
Hidden = false;
|
||||
|
||||
if (Options.Broadcasts.World.EndNotify)
|
||||
{
|
||||
WorldBroadcast("{0} has ended!", Name);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.EndNotify)
|
||||
{
|
||||
LocalBroadcast("{0} has ended!", Name);
|
||||
}
|
||||
|
||||
SendGlobalSound(Options.Sounds.BattleEnded);
|
||||
|
||||
CloseDoors(true);
|
||||
|
||||
ProcessRanks();
|
||||
|
||||
ForEachTeam(t => t.OnBattleEnded());
|
||||
|
||||
TransferStatistics();
|
||||
|
||||
PvPBattlesUI.RefreshAll(this);
|
||||
}
|
||||
|
||||
protected virtual void OnBattleCancelled()
|
||||
{
|
||||
Hidden = false;
|
||||
|
||||
if (Options.Broadcasts.World.EndNotify)
|
||||
{
|
||||
WorldBroadcast("{0} has been cancelled!", Name);
|
||||
}
|
||||
|
||||
if (Options.Broadcasts.Local.EndNotify)
|
||||
{
|
||||
LocalBroadcast("{0} has been cancelled!", Name);
|
||||
}
|
||||
|
||||
SendGlobalSound(Options.Sounds.BattleCanceled);
|
||||
|
||||
CloseDoors(true);
|
||||
|
||||
if (!Deleted && !IsInternal && QueueAllowed)
|
||||
{
|
||||
ForEachTeam(t => t.ForEachMember(o => Queue[o] = t));
|
||||
}
|
||||
|
||||
ForEachTeam(t => t.OnBattleCancelled());
|
||||
}
|
||||
|
||||
public TimeSpan GetStateTimeLeft()
|
||||
{
|
||||
return GetStateTimeLeft(DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public TimeSpan GetStateTimeLeft(DateTime when)
|
||||
{
|
||||
return GetStateTimeLeft(when, State);
|
||||
}
|
||||
|
||||
public TimeSpan GetStateTimeLeft(PvPBattleState state)
|
||||
{
|
||||
return GetStateTimeLeft(DateTime.UtcNow, state);
|
||||
}
|
||||
|
||||
public virtual TimeSpan GetStateTimeLeft(DateTime when, PvPBattleState state)
|
||||
{
|
||||
var time = 0.0;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PvPBattleState.Queueing:
|
||||
{
|
||||
time = (Options.Timing.OpenedWhen.Add(Options.Timing.QueuePeriod) - when).TotalSeconds;
|
||||
|
||||
if (Schedule != null && Schedule.Enabled)
|
||||
{
|
||||
if (Schedule.CurrentGlobalTick != null)
|
||||
{
|
||||
time = (Schedule.CurrentGlobalTick.Value - when).TotalSeconds;
|
||||
}
|
||||
else if (Schedule.NextGlobalTick != null)
|
||||
{
|
||||
time = (Schedule.NextGlobalTick.Value - when).TotalSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
time = (Options.Timing.PreparedWhen.Add(Options.Timing.PreparePeriod) - when).TotalSeconds;
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
time = (Options.Timing.StartedWhen.Add(Options.Timing.RunningPeriod) - when).TotalSeconds;
|
||||
break;
|
||||
case PvPBattleState.Ended:
|
||||
time = (Options.Timing.EndedWhen.Add(Options.Timing.EndedPeriod) - when).TotalSeconds;
|
||||
break;
|
||||
}
|
||||
|
||||
if (time > 0)
|
||||
{
|
||||
return TimeSpan.FromSeconds(time);
|
||||
}
|
||||
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool Ranked { get; set; }
|
||||
|
||||
public long GetStatistic(PlayerMobile pm, Func<PvPProfileHistoryEntry, long> fetch)
|
||||
{
|
||||
if (pm == null || fetch == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Teams.Aggregate(0L, (v, t) => v + GetStatistic(t, pm, fetch));
|
||||
}
|
||||
|
||||
public long GetStatistic(PvPTeam t, PlayerMobile pm, Func<PvPProfileHistoryEntry, long> fetch)
|
||||
{
|
||||
if (t == null || t.Deleted || pm == null || fetch == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var s = t.GetStatistics(pm);
|
||||
|
||||
if (s != null)
|
||||
{
|
||||
return VitaNexCore.TryCatchGet(fetch, s, AutoPvP.CMOptions.ToConsole);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool UpdateStatistics(PvPTeam t, PlayerMobile pm, Action<PvPProfileHistoryEntry> update)
|
||||
{
|
||||
if (t == null || t.Deleted || pm == null || update == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var s = t.GetStatistics(pm);
|
||||
|
||||
if (s == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var success = true;
|
||||
|
||||
VitaNexCore.TryCatch(
|
||||
update,
|
||||
s,
|
||||
x =>
|
||||
{
|
||||
AutoPvP.CMOptions.ToConsole(x);
|
||||
success = false;
|
||||
});
|
||||
|
||||
if (t.IsMember(pm))
|
||||
{
|
||||
t.UpdateActivity(pm);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
{
|
||||
ForEachTeam(ResetStatistics);
|
||||
}
|
||||
|
||||
public void ResetStatistics(PvPTeam t)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
t.Statistics.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void TransferStatistics()
|
||||
{
|
||||
ForEachTeam(TransferStatistics);
|
||||
}
|
||||
|
||||
private void TransferStatistics(PvPTeam t)
|
||||
{
|
||||
if (t != null)
|
||||
{
|
||||
t.Statistics.ForEachReverse(o => TransferStatistics(o.Key, o.Value));
|
||||
}
|
||||
}
|
||||
|
||||
private void TransferStatistics(PlayerMobile pm, PvPProfileHistoryEntry e)
|
||||
{
|
||||
var profile = AutoPvP.EnsureProfile(pm);
|
||||
|
||||
OnTransferStatistics(profile, e);
|
||||
OnTransferPoints(profile, e.Points);
|
||||
}
|
||||
|
||||
protected virtual void OnTransferStatistics(PvPProfile profile, PvPProfileHistoryEntry stats)
|
||||
{
|
||||
if (Ranked && profile != null && stats != null)
|
||||
{
|
||||
stats.AddTo(profile.Statistics, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnTransferPoints(PvPProfile profile, long points)
|
||||
{
|
||||
if (!Ranked || profile == null || points == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
profile.RawPoints += points;
|
||||
|
||||
if (IsOnline(profile.Owner))
|
||||
{
|
||||
profile.Owner.SendMessage(
|
||||
"You have {0} {1:#,0} Battle Point{2} from {3}!",
|
||||
points > 0 ? "gained" : "lost",
|
||||
points,
|
||||
points != 1 ? "s" : String.Empty,
|
||||
Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Gumps;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.SuperGumps.UI;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleCommandState
|
||||
{
|
||||
public virtual PvPBattle Battle { get; protected set; }
|
||||
public virtual PlayerMobile Mobile { get; protected set; }
|
||||
public virtual string Command { get; protected set; }
|
||||
public virtual string Speech { get; protected set; }
|
||||
public virtual string[] Args { get; protected set; }
|
||||
|
||||
public PvPBattleCommandState(PvPBattle battle, PlayerMobile from, string command, string[] args)
|
||||
{
|
||||
Battle = battle;
|
||||
Mobile = from;
|
||||
Command = command;
|
||||
Args = args;
|
||||
Speech = String.Join(" ", args);
|
||||
}
|
||||
}
|
||||
|
||||
public class PvPBattleCommandInfo
|
||||
{
|
||||
public virtual string Command { get; protected set; }
|
||||
public virtual string Usage { get; set; }
|
||||
public virtual string Description { get; set; }
|
||||
public virtual AccessLevel Access { get; set; }
|
||||
public virtual Func<PvPBattleCommandState, bool> Handler { get; set; }
|
||||
|
||||
public PvPBattleCommandInfo(
|
||||
string command,
|
||||
string desc,
|
||||
string usage,
|
||||
AccessLevel access,
|
||||
Func<PvPBattleCommandState, bool> handler)
|
||||
{
|
||||
Command = command;
|
||||
Description = desc;
|
||||
Usage = usage;
|
||||
Access = access;
|
||||
Handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
private char _SubCommandPrefix = '@';
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual char SubCommandPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Char.IsSymbol(_SubCommandPrefix))
|
||||
{
|
||||
_SubCommandPrefix = '@';
|
||||
}
|
||||
|
||||
return _SubCommandPrefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (!Char.IsSymbol(value))
|
||||
{
|
||||
value = '@';
|
||||
}
|
||||
|
||||
_SubCommandPrefix = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<string, PvPBattleCommandInfo> SubCommandHandlers { get; private set; }
|
||||
|
||||
protected virtual void RegisterSubCommands()
|
||||
{
|
||||
RegisterSubCommand(
|
||||
"help",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var ci in SubCommandHandlers.Keys.Select(cmd => SubCommandHandlers[cmd])
|
||||
.Where(ci => state.Mobile.AccessLevel >= ci.Access))
|
||||
{
|
||||
state.Mobile.SendMessage("{0}{1} {2}", SubCommandPrefix, ci.Command, ci.Usage);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"Displays a list of available commands for this battle.",
|
||||
"[?]",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommandAlias("help", "commands");
|
||||
|
||||
RegisterSubCommand(
|
||||
"battle",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LocalBroadcast("[{0}]: {1}", state.Mobile.RawName, state.Speech);
|
||||
return true;
|
||||
},
|
||||
"Broadcasts a message to all battle participants and spectators.",
|
||||
"<message>",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommandAlias("battle", "b");
|
||||
|
||||
RegisterSubCommand(
|
||||
"team",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsParticipant(state.Mobile, out var team))
|
||||
{
|
||||
state.Mobile.SendMessage("You must be a participant to use that command.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
state.Mobile.SendMessage("You are not a member of a team.");
|
||||
return true;
|
||||
}
|
||||
|
||||
team.Broadcast("[{0}]: {1}", state.Mobile.RawName, state.Speech);
|
||||
return true;
|
||||
},
|
||||
"Sends a message to your team.",
|
||||
"<message>",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommandAlias("team", "t");
|
||||
|
||||
RegisterSubCommand(
|
||||
"join",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsParticipant(state.Mobile))
|
||||
{
|
||||
state.Mobile.SendMessage("You are already participating in this battle.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsQueued(state.Mobile))
|
||||
{
|
||||
state.Mobile.SendMessage("You are already queued for this battle.");
|
||||
return true;
|
||||
}
|
||||
|
||||
Enqueue(state.Mobile);
|
||||
return true;
|
||||
},
|
||||
"Joins the queue for the battle.",
|
||||
"",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommandAlias("join", "j");
|
||||
|
||||
RegisterSubCommand(
|
||||
"leave",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Quit(state.Mobile, true);
|
||||
return true;
|
||||
},
|
||||
"Removes you from the battle.",
|
||||
"",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommandAlias("leave", "quit");
|
||||
|
||||
RegisterSubCommand(
|
||||
"time",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var timeLeft = GetStateTimeLeft(DateTime.UtcNow);
|
||||
|
||||
if (timeLeft > TimeSpan.Zero)
|
||||
{
|
||||
var time = timeLeft.ToSimpleString(@"h\:m\:s");
|
||||
var nextState = "";
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case PvPBattleState.Queueing:
|
||||
nextState = "Preparation";
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
nextState = "Start";
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
nextState = "End";
|
||||
break;
|
||||
case PvPBattleState.Ended:
|
||||
nextState = "Open";
|
||||
break;
|
||||
}
|
||||
|
||||
state.Mobile.SendMessage("Battle is currently {0}. Time left until {1}: {2}", State.ToString(), nextState, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.Mobile.SendMessage("Time unavailable.");
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"Displays the amount of time left until the next battle state.",
|
||||
"",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommand(
|
||||
"menu",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
new PvPBattleUI(state.Mobile, battle: this).Send();
|
||||
return true;
|
||||
},
|
||||
"Opens the interface for this battle.",
|
||||
"",
|
||||
AccessLevel.Player);
|
||||
|
||||
RegisterSubCommand(
|
||||
"config",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
state.Mobile.SendGump(new PropertiesGump(state.Mobile, this));
|
||||
return true;
|
||||
},
|
||||
"Opens the configuration for this battle.");
|
||||
|
||||
RegisterSubCommand(
|
||||
"scores",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var stats = new StringBuilder();
|
||||
|
||||
ForEachTeam(
|
||||
t =>
|
||||
{
|
||||
stats.AppendLine("Team: {0}", t.Name);
|
||||
stats.AppendLine();
|
||||
|
||||
t.GetHtmlStatistics(state.Mobile, stats);
|
||||
|
||||
stats.AppendLine();
|
||||
});
|
||||
|
||||
new NoticeDialogGump(state.Mobile)
|
||||
{
|
||||
Width = 600,
|
||||
Height = 400,
|
||||
Title = "Battle Statistics",
|
||||
Html = stats.ToString()
|
||||
}.Send();
|
||||
|
||||
return true;
|
||||
},
|
||||
"Display the statistics for all teams.");
|
||||
|
||||
RegisterSubCommand(
|
||||
"hidden",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Hidden = !Hidden;
|
||||
return true;
|
||||
},
|
||||
"Toggle the battle visiblility.");
|
||||
|
||||
RegisterSubCommand(
|
||||
"state",
|
||||
state =>
|
||||
{
|
||||
if (state == null || state.Mobile == null || state.Mobile.Deleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var oldState = State;
|
||||
|
||||
if (!Validate())
|
||||
{
|
||||
State = PvPBattleState.Internal;
|
||||
state.Mobile.SendMessage("This battle has failed validation and has been internalized.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Hidden = false;
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case PvPBattleState.Internal:
|
||||
State = PvPBattleState.Queueing;
|
||||
break;
|
||||
case PvPBattleState.Queueing:
|
||||
State = PvPBattleState.Preparing;
|
||||
break;
|
||||
case PvPBattleState.Preparing:
|
||||
State = PvPBattleState.Running;
|
||||
break;
|
||||
case PvPBattleState.Running:
|
||||
State = PvPBattleState.Ended;
|
||||
break;
|
||||
case PvPBattleState.Ended:
|
||||
State = PvPBattleState.Queueing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (State != oldState)
|
||||
{
|
||||
state.Mobile.SendMessage("The state of the battle has been changed to {0}", State.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
"Toggles the state of the current battle.");
|
||||
}
|
||||
|
||||
public void RegisterSubCommand(
|
||||
string command,
|
||||
Func<PvPBattleCommandState, bool> handler,
|
||||
string desc = "",
|
||||
string usage = "",
|
||||
AccessLevel access = AutoPvP.Access)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(command) && handler != null)
|
||||
{
|
||||
RegisterSubCommand(new PvPBattleCommandInfo(command, desc, usage, access, handler));
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterSubCommand(PvPBattleCommandInfo info)
|
||||
{
|
||||
if (info != null)
|
||||
{
|
||||
SubCommandHandlers[info.Command] = info;
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterSubCommandAlias(string command, params string[] alias)
|
||||
{
|
||||
if (!IsCommand(command))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var info = SubCommandHandlers[command];
|
||||
|
||||
foreach (var cmd in alias)
|
||||
{
|
||||
RegisterSubCommand(cmd, info.Handler, info.Description, info.Usage, info.Access);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HandleSubCommand(PlayerMobile pm, string speech)
|
||||
{
|
||||
if (pm == null || pm.Deleted || String.IsNullOrWhiteSpace(speech))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
speech = speech.Trim();
|
||||
|
||||
if (!speech.StartsWith(SubCommandPrefix.ToString(CultureInfo.InvariantCulture)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var command = String.Empty;
|
||||
var args = new string[0];
|
||||
|
||||
speech = speech.TrimStart(SubCommandPrefix);
|
||||
|
||||
var split = speech.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (split.Length > 0)
|
||||
{
|
||||
command = split[0];
|
||||
args = new string[split.Length - 1];
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
args[i] = split[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsCommand(command))
|
||||
{
|
||||
pm.SendMessage("Command not found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
var info = SubCommandHandlers[command];
|
||||
|
||||
if (pm.AccessLevel < info.Access)
|
||||
{
|
||||
pm.SendMessage("You do not have access to that command.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.Length > 0 && (args[0] == "?" || Insensitive.Equals(args[0], "help")))
|
||||
{
|
||||
pm.SendMessage("Usage: {0}{1} {2}", SubCommandPrefix, info.Command, info.Usage);
|
||||
pm.SendMessage(info.Description);
|
||||
return true;
|
||||
}
|
||||
|
||||
var state = new PvPBattleCommandState(this, pm, command, args);
|
||||
|
||||
if (info.Handler.Invoke(state))
|
||||
{
|
||||
OnCommand(state);
|
||||
return true;
|
||||
}
|
||||
|
||||
pm.SendMessage("Usage: {0}{1} {2}", SubCommandPrefix, info.Command, info.Usage);
|
||||
pm.SendMessage(info.Description);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsCommand(string command)
|
||||
{
|
||||
return !String.IsNullOrWhiteSpace(command) && SubCommandHandlers.ContainsKey(command);
|
||||
}
|
||||
|
||||
protected virtual void OnCommand(PvPBattleCommandState state)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
using Server.Spells.Fifth;
|
||||
|
||||
using VitaNex.Collections;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public List<PvPTeam> Teams { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool AutoAssign { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool UseTeamColors { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool UseIncognito { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool RequireCapacity { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool RewardTeam { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int MinCapacity => GetMinCapacity();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int MaxCapacity => GetMaxCapacity();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int CurrentCapacity => GetCurrentCapacity();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool IsFull => (CurrentCapacity >= MaxCapacity);
|
||||
|
||||
public int GetMinCapacity()
|
||||
{
|
||||
return Teams.Aggregate(0, (v, t) => v + t.MinCapacity);
|
||||
}
|
||||
|
||||
public int GetMaxCapacity()
|
||||
{
|
||||
return Teams.Aggregate(0, (v, t) => v + t.MaxCapacity);
|
||||
}
|
||||
|
||||
public int GetCurrentCapacity()
|
||||
{
|
||||
return Teams.Aggregate(0, (v, t) => v + t.Count);
|
||||
}
|
||||
|
||||
public bool HasCapacity()
|
||||
{
|
||||
return HasCapacity(0);
|
||||
}
|
||||
|
||||
public bool HasCapacity(int min)
|
||||
{
|
||||
if (min <= 0)
|
||||
{
|
||||
return CurrentCapacity > 1;
|
||||
}
|
||||
|
||||
if (!RequireCapacity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return CurrentCapacity >= min;
|
||||
}
|
||||
|
||||
public virtual bool AddTeam(string name, int capacity, int color)
|
||||
{
|
||||
return AddTeam(name, 0, capacity, color);
|
||||
}
|
||||
|
||||
public virtual bool AddTeam(string name, int minCapacity, int capacity, int color)
|
||||
{
|
||||
return AddTeam(new PvPTeam(this, name, minCapacity, capacity, color));
|
||||
}
|
||||
|
||||
public virtual bool AddTeam(PvPTeam team)
|
||||
{
|
||||
if (!ContainsTeam(team))
|
||||
{
|
||||
Teams.Add(team);
|
||||
OnTeamAdded(team);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool RemoveTeam(PvPTeam team)
|
||||
{
|
||||
if (Teams.Remove(team))
|
||||
{
|
||||
OnTeamRemoved(team);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ContainsTeam(PvPTeam team)
|
||||
{
|
||||
return Teams.Contains(team);
|
||||
}
|
||||
|
||||
public void ResetTeam(PvPTeam team)
|
||||
{
|
||||
if (team != null && !team.Deleted)
|
||||
{
|
||||
team.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void ForEachTeam(Action<PvPTeam> action)
|
||||
{
|
||||
Teams.ForEachReverse(
|
||||
t =>
|
||||
{
|
||||
if (t != null && !t.Deleted)
|
||||
{
|
||||
action(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
Teams.Remove(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ForEachTeam<T>(Action<T> action)
|
||||
where T : PvPTeam
|
||||
{
|
||||
Teams.ForEachReverse(
|
||||
t =>
|
||||
{
|
||||
if (t != null && !t.Deleted)
|
||||
{
|
||||
if (t is T)
|
||||
{
|
||||
action((T)t);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Teams.Remove(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public virtual bool CanDamageOwnTeam(PlayerMobile damager, PlayerMobile target)
|
||||
{
|
||||
return Options.Rules.CanDamageOwnTeam && State == PvPBattleState.Running;
|
||||
}
|
||||
|
||||
public virtual bool CanDamageEnemyTeam(PlayerMobile damager, PlayerMobile target)
|
||||
{
|
||||
return Options.Rules.CanDamageEnemyTeam && State == PvPBattleState.Running;
|
||||
}
|
||||
|
||||
public virtual bool CanHealOwnTeam(PlayerMobile healer, PlayerMobile target)
|
||||
{
|
||||
return Options.Rules.CanHealOwnTeam && (State == PvPBattleState.Preparing || State == PvPBattleState.Running);
|
||||
}
|
||||
|
||||
public virtual bool CanHealEnemyTeam(PlayerMobile healer, PlayerMobile target)
|
||||
{
|
||||
return Options.Rules.CanHealEnemyTeam && (State == PvPBattleState.Preparing || State == PvPBattleState.Running);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<PvPTeam> GetTeams()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted);
|
||||
}
|
||||
|
||||
public virtual IOrderedEnumerable<PvPTeam> GetTeamsRanked()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted).Order();
|
||||
}
|
||||
|
||||
public virtual int CompareTeam(PvPTeam a, PvPTeam b)
|
||||
{
|
||||
if (ReferenceEquals(a, b))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var result = 0;
|
||||
|
||||
if (a.CompareNull(b, ref result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return a.CompareTo(b);
|
||||
}
|
||||
|
||||
public virtual int CountAliveTeams()
|
||||
{
|
||||
return Teams.Count(t => t.Dead.Count < t.Count);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<PvPTeam> GetAliveTeams()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted && t.Dead.Count < t.Count);
|
||||
}
|
||||
|
||||
public virtual PvPTeam GetMostEmptyTeam()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted).Lowest(t => t.Count);
|
||||
}
|
||||
|
||||
public virtual PvPTeam GetMostFullTeam()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted).Lowest(t => t.Count);
|
||||
}
|
||||
|
||||
public virtual PvPTeam GetRandomTeam()
|
||||
{
|
||||
return Teams.Where(t => t != null && !t.Deleted).GetRandom();
|
||||
}
|
||||
|
||||
public virtual PvPTeam GetAutoAssignTeam(PlayerMobile pm)
|
||||
{
|
||||
if (pm == null || pm.Deleted)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (IsParticipant(pm, out var team) && team != null && !team.Deleted)
|
||||
{
|
||||
return team;
|
||||
}
|
||||
|
||||
return Teams.OrderBy(GetAssignPriority).FirstOrDefault();
|
||||
}
|
||||
|
||||
public virtual double GetAssignPriority(PvPTeam team)
|
||||
{
|
||||
GetAssignPriority(team, out var weight);
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
protected virtual void GetAssignPriority(PvPTeam team, out double weight)
|
||||
{
|
||||
if (team == null || team.Deleted)
|
||||
{
|
||||
weight = Double.MaxValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (team.IsEmpty)
|
||||
{
|
||||
weight = Double.MinValue;
|
||||
return;
|
||||
}
|
||||
|
||||
weight = team.Aggregate(0.0, (v, m) => v + (m.SkillsTotal * m.RawStatTotal));
|
||||
}
|
||||
|
||||
public PvPTeam FindTeam(PlayerMobile pm)
|
||||
{
|
||||
return FindTeam<PvPTeam>(pm);
|
||||
}
|
||||
|
||||
public T FindTeam<T>(PlayerMobile pm)
|
||||
where T : PvPTeam
|
||||
{
|
||||
return pm != null ? Teams.OfType<T>().FirstOrDefault(t => t.IsMember(pm)) : null;
|
||||
}
|
||||
|
||||
public virtual void TeamRespawn(PvPTeam team)
|
||||
{
|
||||
if (team != null && !team.Deleted)
|
||||
{
|
||||
team.ForEachMember(team.Respawn);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void TeamEject(PvPTeam team)
|
||||
{
|
||||
if (team != null && !team.Deleted)
|
||||
{
|
||||
team.ForEachMember(member => team.RemoveMember(member, true));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnTeamInit(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamSync(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamMicroSync(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamAdded(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamRemoved(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamFrozen(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamUnfrozen(PvPTeam team)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamMemberFrozen(PvPTeam team, PlayerMobile pm)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamMemberUnfrozen(PvPTeam team, PlayerMobile pm)
|
||||
{ }
|
||||
|
||||
public virtual void OnTeamMemberDeath(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
LocalBroadcast("{0} has died.", pm.RawName);
|
||||
|
||||
UpdateStatistics(team, pm, s => ++s.Deaths);
|
||||
|
||||
var pk = pm.FindMostRecentDamager(false) as PlayerMobile;
|
||||
|
||||
if (pk != null && !pk.Deleted)
|
||||
{
|
||||
if (IsParticipant(pk, out var pkt))
|
||||
{
|
||||
if (KillPoints > 0 && !pk.Account.IsSharedWith(pm.Account))
|
||||
{
|
||||
RevokePoints(pm, KillPoints);
|
||||
}
|
||||
|
||||
pm.LastKiller = pk;
|
||||
|
||||
UpdateStatistics(pkt, pk, s => ++s.Kills);
|
||||
|
||||
if (KillPoints > 0 && !pk.Account.IsSharedWith(pm.Account))
|
||||
{
|
||||
AwardPoints(pk, KillPoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TeleportToHomeBase(team, pm);
|
||||
}
|
||||
|
||||
public virtual void OnAfterTeamMemberDeath(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
RefreshStats(pm, true, true);
|
||||
|
||||
if (!TryKickOnDeath(team, pm, true) && !TryRespawnOnDeath(team, pm, true))
|
||||
{
|
||||
TeleportToHomeBase(team, pm);
|
||||
|
||||
team.Respawn(pm, false);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnTeamMemberResurrected(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
UpdateStatistics(team, pm, s => ++s.Resurrections);
|
||||
}
|
||||
|
||||
public virtual void OnAfterTeamMemberResurrected(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
team.Dead.Remove(pm);
|
||||
}
|
||||
|
||||
public virtual void OnTeamMemberAdded(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
team.Broadcast("{0} has joined the battle.", pm.RawName);
|
||||
|
||||
if (UseTeamColors)
|
||||
{
|
||||
pm.SolidHueOverride = team.Color;
|
||||
}
|
||||
|
||||
if (UseIncognito)
|
||||
{
|
||||
pm.BeginAction(typeof(IncognitoSpell));
|
||||
|
||||
pm.NameMod = NameList.RandomName(pm.Female ? "female" : "male");
|
||||
|
||||
var race = pm.Race ?? Race.DefaultRace;
|
||||
|
||||
pm.BodyMod = race.Body(pm);
|
||||
pm.HueMod = race.RandomSkinHue();
|
||||
|
||||
pm.SetHairMods(race.RandomHair(pm.Female), race.RandomFacialHair(pm.Female));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnTeamMemberRemoved(PvPTeam team, PlayerMobile pm)
|
||||
{
|
||||
team.Broadcast("{0} has left the battle.", pm.RawName);
|
||||
|
||||
pm.SolidHueOverride = -1;
|
||||
|
||||
pm.EndAction(typeof(IncognitoSpell));
|
||||
|
||||
pm.NameMod = null;
|
||||
pm.BodyMod = 0;
|
||||
pm.HueMod = -1;
|
||||
|
||||
pm.SetHairMods(-1, -1);
|
||||
}
|
||||
|
||||
public virtual bool TryKickOnDeath(PvPTeam team, PlayerMobile pm, bool isLoss)
|
||||
{
|
||||
if (team.KickOnDeath)
|
||||
{
|
||||
if (isLoss)
|
||||
{
|
||||
OnLose(pm);
|
||||
}
|
||||
|
||||
team.RemoveMember(pm, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TryRespawnOnDeath(PvPTeam team, PlayerMobile pm, bool isDelayed)
|
||||
{
|
||||
if (team.RespawnOnDeath)
|
||||
{
|
||||
if (isDelayed && team.RespawnDelay > TimeSpan.Zero)
|
||||
{
|
||||
Timer.DelayCall(team.RespawnDelay, team.Respawn, pm);
|
||||
}
|
||||
else
|
||||
{
|
||||
team.Respawn(pm);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void ProcessRanks()
|
||||
{
|
||||
if (Options.Missions.Enabled)
|
||||
{
|
||||
ProcessMissionRanks();
|
||||
}
|
||||
else if (RewardTeam)
|
||||
{
|
||||
ProcessTeamRanks(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessPlayerRanks(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ProcessMissionRanks()
|
||||
{
|
||||
var players = ListPool<PlayerMobile>.AcquireObject();
|
||||
|
||||
players.AddRange(GetParticipants());
|
||||
|
||||
if (CheckMissions(out var team, out var player))
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
WorldBroadcast("{0} has won {1}!", team.Name, Name);
|
||||
|
||||
team.ForEachMember(OnWin);
|
||||
|
||||
players.RemoveAll(team.IsMember);
|
||||
}
|
||||
else if (player != null)
|
||||
{
|
||||
WorldBroadcast("{0} has won {1}!", player.Name, Name);
|
||||
|
||||
OnWin(player);
|
||||
|
||||
players.Remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var p in players)
|
||||
{
|
||||
OnLose(p);
|
||||
}
|
||||
|
||||
ObjectPool.Free(ref players);
|
||||
}
|
||||
|
||||
protected virtual void ProcessTeamRanks(int limit)
|
||||
{
|
||||
if (limit <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var teams = GetTeams().Where(t => !t.IsEmpty).ToLookup(GetScore, o => o);
|
||||
|
||||
if (teams.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool win;
|
||||
|
||||
foreach (var o in teams.OrderByDescending(o => o.Key))
|
||||
{
|
||||
win = limit > 0 && o.Key > 0 && o.Any() && --limit >= 0;
|
||||
|
||||
foreach (var team in o)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
WorldBroadcast("{0} has won {1}!", team.Name, Name);
|
||||
|
||||
team.ForEachMember(OnWin);
|
||||
}
|
||||
else
|
||||
{
|
||||
team.ForEachMember(OnLose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ProcessPlayerRanks(int limit)
|
||||
{
|
||||
if (limit <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var players = GetParticipants().ToLookup(GetScore, o => o);
|
||||
|
||||
if (players.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool win;
|
||||
|
||||
foreach (var o in players.OrderByDescending(o => o.Key))
|
||||
{
|
||||
win = limit > 0 && o.Key > 0 && o.Any() && --limit >= 0;
|
||||
|
||||
foreach (var player in o)
|
||||
{
|
||||
if (win)
|
||||
{
|
||||
WorldBroadcast("{0} has won {1}!", player.Name, Name);
|
||||
|
||||
OnWin(player);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnLose(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected double GetScore(PvPTeam team)
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
return team.GetScore();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected double GetMissionsScore(PvPTeam team)
|
||||
{
|
||||
if (team != null)
|
||||
{
|
||||
return team.GetMissionsScore();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected double GetScore(PlayerMobile pm)
|
||||
{
|
||||
if (Options.Missions.Enabled)
|
||||
{
|
||||
return GetMissionsScore(pm);
|
||||
}
|
||||
|
||||
return GetStatistic(pm, e => e.Points);
|
||||
}
|
||||
|
||||
public double GetMissionsScore(PlayerMobile pm)
|
||||
{
|
||||
if (Options.Missions.Enabled)
|
||||
{
|
||||
return Options.Missions.ComputeScore(this, pm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
public bool Validate(Mobile viewer = null)
|
||||
{
|
||||
return Validate(viewer, new List<string>());
|
||||
}
|
||||
|
||||
public virtual bool Validate(Mobile viewer, List<string> errors, bool pop = true)
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
errors.Add("This battle has been deleted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
errors.Add("Select a valid Name.");
|
||||
errors.Add("[Options] -> [Edit Options]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(Description))
|
||||
{
|
||||
errors.Add("Select a valid Description.");
|
||||
errors.Add("[Options] -> [Edit Options]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpectateAllowed)
|
||||
{
|
||||
if (SpectateRegion == null)
|
||||
{
|
||||
errors.Add("Select a valid Spectate Region.");
|
||||
errors.Add("[Options] -> [Edit Spectage Region]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Options.Locations.SpectateBounds.Count == 0)
|
||||
{
|
||||
errors.Add("The Spectate Region has no zones.");
|
||||
errors.Add("[Options] -> [Edit Spectage Region]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (BattleRegion == null)
|
||||
{
|
||||
errors.Add("Select a valid Battle Region.");
|
||||
errors.Add("[Options] -> [Edit Battle Region]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Options.Locations.BattleBounds.Count == 0)
|
||||
{
|
||||
errors.Add("The Battle Region has no zones.");
|
||||
errors.Add("[Options] -> [Edit Battle Region]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.Locations.Map == Map.Internal)
|
||||
{
|
||||
errors.Add("The Battle Map must not be Internal.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.Locations.Eject.Internal)
|
||||
{
|
||||
errors.Add("The Eject Map must not be Internal.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.Locations.Eject == Point3D.Zero)
|
||||
{
|
||||
errors.Add("Select a valid Eject Location.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (BattleRegion != null &&
|
||||
BattleRegion.Contains(Options.Locations.Eject.Location, Options.Locations.Eject.Map))
|
||||
{
|
||||
errors.Add("Eject Location must be outside the Battle Region.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (SpectateAllowed)
|
||||
{
|
||||
if (Options.Locations.SpectateJoin == Point3D.Zero)
|
||||
{
|
||||
errors.Add("Select a valid Spectator Join location.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (SpectateRegion != null && !SpectateRegion.Contains(Options.Locations.SpectateJoin))
|
||||
{
|
||||
errors.Add("Spectate Join Location must be within the Spectate Region.");
|
||||
errors.Add("[Options] -> [Edit Advanced Options] -> [Locations]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Schedule == null)
|
||||
{
|
||||
errors.Add("No Schedule has been set for this battle.");
|
||||
errors.Add("[Options] -> [View Schedule]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Schedule.Enabled && Schedule.NextGlobalTick == null)
|
||||
{
|
||||
errors.Add("The Schedule has no more future dates.");
|
||||
errors.Add("[Options] -> [View Schedule]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (IdleKick && IdleThreshold.TotalSeconds < 10.0)
|
||||
{
|
||||
errors.Add("The Idle Threshold must be greater than, or equal to 10 seconds.");
|
||||
errors.Add("[Options] -> [Edit Options]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Teams.Count == 0)
|
||||
{
|
||||
errors.Add("There are no teams available for this Battle.");
|
||||
errors.Add("[Options] -> [View Teams]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (Teams.Any(t => !t.Validate(viewer)))
|
||||
{
|
||||
errors.Add("One or more teams did not pass validation.");
|
||||
errors.Add("[Options] -> [View Teams]");
|
||||
|
||||
if (pop)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Count == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#if ServUO58
|
||||
#define ServUOX
|
||||
#endif
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Server;
|
||||
using Server.Movement;
|
||||
|
||||
using VitaNex.FX;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public abstract partial class PvPBattle
|
||||
{
|
||||
private static readonly PvPBattleWeatherDirection[] _WeatherDirections = ((PvPBattleWeatherDirection)0)
|
||||
.GetValues<PvPBattleWeatherDirection>()
|
||||
.Not(d => d == PvPBattleWeatherDirection.None || d == PvPBattleWeatherDirection.Random)
|
||||
.ToArray();
|
||||
|
||||
public virtual bool CanUseWeather()
|
||||
{
|
||||
if (Options.Locations.BattleBounds == null || Options.Locations.BattleBounds.Count == 0 ||
|
||||
Options.Locations.Map == Map.Internal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Options.Weather.Density <= 0 || Options.Weather.EffectID <= 0 || Options.Weather.EffectSpeed <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Options.Weather.Force)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Options.Weather.Enabled || State == PvPBattleState.Internal)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if ServUOX
|
||||
if (BattleRegion != null && BattleRegion.MobileCount == 0)
|
||||
#else
|
||||
if (BattleRegion != null && BattleRegion.GetMobileCount() == 0)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Options.SuddenDeath.Enabled || !Options.SuddenDeath.Active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow - Options.SuddenDeath.StartedWhen < Options.SuddenDeath.Delay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual Point3D GetWeatherStartPoint(Point3D loc, PvPBattleWeatherDirection direction)
|
||||
{
|
||||
if (!Options.Weather.Enabled || Options.Locations.Map == null || Options.Locations.Map == Map.Internal ||
|
||||
loc == Point3D.Zero)
|
||||
{
|
||||
return loc;
|
||||
}
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case PvPBattleWeatherDirection.None:
|
||||
return loc.Clone3D(0, 0, 80);
|
||||
case PvPBattleWeatherDirection.Random:
|
||||
direction = _WeatherDirections.GetRandom(PvPBattleWeatherDirection.None);
|
||||
break;
|
||||
}
|
||||
|
||||
int x = 0, y = 0;
|
||||
|
||||
Movement.Offset((Direction)direction, ref x, ref y);
|
||||
|
||||
var rand = Utility.RandomMinMax(4, 8);
|
||||
|
||||
return loc.Clone3D(x * rand, y * rand, 80);
|
||||
}
|
||||
|
||||
protected virtual void WeatherCycle()
|
||||
{
|
||||
if (!CanUseWeather())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point3D start, end;
|
||||
|
||||
Parallel.ForEach(
|
||||
Options.Locations.BattleBounds,
|
||||
b =>
|
||||
{
|
||||
end = b.Start.Clone2D(Utility.Random(b.Width), Utility.Random(b.Height)).GetWorldTop(Options.Locations.Map);
|
||||
start = GetWeatherStartPoint(end, Options.Weather.Direction);
|
||||
|
||||
new MovingEffectInfo(
|
||||
start,
|
||||
end,
|
||||
Options.Locations.Map,
|
||||
Options.Weather.EffectID,
|
||||
Options.Weather.EffectHue,
|
||||
Options.Weather.EffectSpeed,
|
||||
Options.Weather.EffectRender,
|
||||
TimeSpan.FromMilliseconds(Utility.Random(500))).MovingImpact(WeatherImpactHandler);
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void WeatherImpactHandler(MovingEffectInfo info)
|
||||
{
|
||||
if (info == null || Deleted || !Options.Weather.Impacts || Options.Weather.ImpactEffectID <= 0 ||
|
||||
Options.Weather.ImpactEffectSpeed <= 0 || Options.Weather.ImpactEffectDuration <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnWeatherImpact(info);
|
||||
|
||||
if (Options.Weather.ImpactEffectID <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var effect = new EffectInfo(
|
||||
info.Target,
|
||||
info.Map,
|
||||
Options.Weather.ImpactEffectID,
|
||||
Options.Weather.ImpactEffectHue,
|
||||
Options.Weather.ImpactEffectSpeed,
|
||||
Options.Weather.ImpactEffectDuration,
|
||||
Options.Weather.ImpactEffectRender);
|
||||
|
||||
effect.Send();
|
||||
|
||||
OnWeatherImpact(effect);
|
||||
}
|
||||
|
||||
protected virtual void OnWeatherImpact(MovingEffectInfo info)
|
||||
{ }
|
||||
|
||||
protected virtual void OnWeatherImpact(EffectInfo info)
|
||||
{
|
||||
if (info == null || Deleted || !Options.Weather.Impacts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Options.Weather.ImpactEffectSound > 0)
|
||||
{
|
||||
Effects.PlaySound(info.Source, info.Map, Options.Weather.ImpactEffectSound);
|
||||
}
|
||||
|
||||
if (!Options.SuddenDeath.Enabled || !Options.SuddenDeath.Active || !Options.SuddenDeath.Damages)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var pm in info.Source.GetPlayersInRange(info.Map, Options.SuddenDeath.DamageRange))
|
||||
{
|
||||
Options.SuddenDeath.Damage(pm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPProfile : IEnumerable<PvPProfileHistoryEntry>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool Deleted { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PlayerMobile Owner { get; private set; }
|
||||
|
||||
private long _Points;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long RawPoints { get => _Points; set => _Points = Math.Max(0, value); }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long Points
|
||||
{
|
||||
get => RawPoints;
|
||||
set
|
||||
{
|
||||
var oldVal = RawPoints;
|
||||
|
||||
RawPoints = value;
|
||||
|
||||
if (oldVal != RawPoints)
|
||||
{
|
||||
OnPointsChanged(oldVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public List<PvPBattle> Subscriptions { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalDamageTaken => GetTotalDamageTaken();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalDamageDone => GetTotalDamageDone();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalHealingTaken => GetTotalHealingTaken();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalHealingDone => GetTotalHealingDone();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalDeaths => GetTotalDeaths();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalResurrections => GetTotalResurrections();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalKills => GetTotalKills();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalPointsGained => GetTotalPointsGained();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalPointsLost => GetTotalPointsLost();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalPoints => TotalPointsGained - TotalPointsLost;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalWins => GetTotalWins();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalLosses => GetTotalLosses();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long TotalBattles => GetTotalBattles();
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPProfileHistory History { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPProfileHistoryEntry Statistics => History.EnsureEntry();
|
||||
|
||||
public PvPProfile(PlayerMobile owner)
|
||||
{
|
||||
Owner = owner;
|
||||
|
||||
History = new PvPProfileHistory(this);
|
||||
|
||||
Subscriptions = new List<PvPBattle>();
|
||||
|
||||
SubscribeAllBattles();
|
||||
}
|
||||
|
||||
public PvPProfile(GenericReader reader)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public void SubscribeAllBattles()
|
||||
{
|
||||
foreach (var battle in AutoPvP.GetBattles())
|
||||
{
|
||||
Subscribe(battle);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSubscribed(PvPBattle battle)
|
||||
{
|
||||
return battle != null && !battle.Deleted && Subscriptions.Contains(battle);
|
||||
}
|
||||
|
||||
public void Subscribe(PvPBattle battle)
|
||||
{
|
||||
if (battle != null && !battle.Deleted && !battle.IsInternal)
|
||||
{
|
||||
Subscriptions.Update(battle);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(PvPBattle battle)
|
||||
{
|
||||
if (battle != null && !battle.Deleted)
|
||||
{
|
||||
Subscriptions.Remove(battle);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPointsChanged(long oldVal)
|
||||
{
|
||||
if (_Points > oldVal)
|
||||
{
|
||||
OnPointsGained(_Points - oldVal);
|
||||
}
|
||||
else if (_Points < oldVal)
|
||||
{
|
||||
OnPointsLost(oldVal - _Points);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPointsGained(long value)
|
||||
{
|
||||
Statistics.PointsGained += value;
|
||||
|
||||
Owner.SendMessage("You have gained {0:#,0} Battle Point{1}!", value, value != 1 ? "s" : String.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnPointsLost(long value)
|
||||
{
|
||||
Statistics.PointsLost += value;
|
||||
|
||||
Owner.SendMessage("You have lost {0:#,0} Battle Point{1}!", value, value != 1 ? "s" : String.Empty);
|
||||
}
|
||||
|
||||
public long GetTotalDamageTaken()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.DamageTaken);
|
||||
}
|
||||
|
||||
public long GetTotalDamageDone()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.DamageDone);
|
||||
}
|
||||
|
||||
public long GetTotalHealingTaken()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.HealingTaken);
|
||||
}
|
||||
|
||||
public long GetTotalHealingDone()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.HealingDone);
|
||||
}
|
||||
|
||||
public long GetTotalResurrections()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Resurrections);
|
||||
}
|
||||
|
||||
public long GetTotalDeaths()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Deaths);
|
||||
}
|
||||
|
||||
public long GetTotalKills()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Kills);
|
||||
}
|
||||
|
||||
public long GetTotalPointsGained()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.PointsGained);
|
||||
}
|
||||
|
||||
public long GetTotalPointsLost()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.PointsLost);
|
||||
}
|
||||
|
||||
public long GetTotalWins()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Wins);
|
||||
}
|
||||
|
||||
public long GetTotalLosses()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Losses);
|
||||
}
|
||||
|
||||
public long GetTotalBattles()
|
||||
{
|
||||
return History.Values.Aggregate(0L, (c, entry) => c + entry.Battles);
|
||||
}
|
||||
|
||||
public IEnumerable<KeyValuePair<string, long>> GetMiscStatisticTotals()
|
||||
{
|
||||
return History.Values.SelectMany(e => e.MiscStats)
|
||||
.ToLookup(o => o.Key, o => o.Value)
|
||||
.Select(o => new KeyValuePair<string, long>(o.Key, o.Aggregate(0L, (c, v) => c + v)));
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
if (AutoPvP.RemoveProfile(this))
|
||||
{
|
||||
OnRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
if (Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Deleted = true;
|
||||
|
||||
Remove();
|
||||
|
||||
History = null;
|
||||
|
||||
OnDeleted();
|
||||
}
|
||||
|
||||
protected virtual void OnDeleted()
|
||||
{ }
|
||||
|
||||
protected virtual void OnRemoved()
|
||||
{ }
|
||||
|
||||
public virtual void Init()
|
||||
{ }
|
||||
|
||||
public string ToHtmlString(Mobile viewer = null, bool big = true)
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
if (big)
|
||||
{
|
||||
html.Append("<BIG>");
|
||||
}
|
||||
|
||||
GetHtmlString(viewer, html);
|
||||
|
||||
if (big)
|
||||
{
|
||||
html.Append("</BIG>");
|
||||
}
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
public virtual void GetHtmlString(Mobile viewer, StringBuilder html)
|
||||
{
|
||||
html.Append("".WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false));
|
||||
|
||||
if (Deleted)
|
||||
{
|
||||
html.Append("<B>This profile has been deleted.</B>");
|
||||
return;
|
||||
}
|
||||
|
||||
html.AppendLine("<B>PvP Profile for {0}</B>", Owner.RawName);
|
||||
html.AppendLine();
|
||||
|
||||
html.Append("".WrapUOHtmlColor(Color.YellowGreen, false));
|
||||
html.AppendLine("<B>Statistics</B>");
|
||||
html.AppendLine();
|
||||
|
||||
html.AppendLine("* Overall Points: {0}", _Points.ToString("#,0"));
|
||||
html.AppendLine();
|
||||
|
||||
Statistics.GetHtmlString(viewer, html);
|
||||
|
||||
html.Append("".WrapUOHtmlColor(Color.Cyan, false));
|
||||
html.AppendLine("<B>Statisctics For All Seasons:</B>");
|
||||
html.AppendLine();
|
||||
|
||||
html.AppendLine("<B>Main Statistic Totals</B>");
|
||||
html.AppendLine();
|
||||
|
||||
html.AppendLine("* Battles Attended: {0}", TotalBattles.ToString("#,0"));
|
||||
html.AppendLine("* Battles Won: {0}", TotalWins.ToString("#,0"));
|
||||
html.AppendLine("* Battles Lost: {0}", TotalLosses.ToString("#,0"));
|
||||
html.AppendLine("* Points Gained: {0}", TotalPointsGained.ToString("#,0"));
|
||||
html.AppendLine("* Points Lost: {0}", TotalPointsLost.ToString("#,0"));
|
||||
html.AppendLine("* Kills: {0}", TotalKills.ToString("#,0"));
|
||||
html.AppendLine("* Deaths: {0}", TotalDeaths.ToString("#,0"));
|
||||
html.AppendLine("* Resurrections: {0}", TotalResurrections.ToString("#,0"));
|
||||
html.AppendLine("* Damage Taken: {0}", TotalDamageTaken.ToString("#,0"));
|
||||
html.AppendLine("* Damage Done: {0}", TotalDamageDone.ToString("#,0"));
|
||||
html.AppendLine("* Healing Taken: {0}", TotalHealingTaken.ToString("#,0"));
|
||||
html.AppendLine("* Healing Done: {0}", TotalHealingDone.ToString("#,0"));
|
||||
html.AppendLine();
|
||||
|
||||
html.Append("".WrapUOHtmlColor(Color.GreenYellow, false));
|
||||
html.AppendLine("<B>Misc Statistic Totals</B>");
|
||||
html.AppendLine();
|
||||
|
||||
foreach (var kvp in GetMiscStatisticTotals())
|
||||
{
|
||||
html.AppendLine("* {0}: {1}", kvp.Key, kvp.Value.ToString("#,0"));
|
||||
}
|
||||
|
||||
html.AppendLine();
|
||||
html.Append("".WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false));
|
||||
}
|
||||
|
||||
public IEnumerator<PvPProfileHistoryEntry> GetEnumerator()
|
||||
{
|
||||
return History.Values.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
writer.Write(Deleted);
|
||||
writer.Write(Owner);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.Write(_Points);
|
||||
|
||||
writer.WriteBlock(w => w.WriteType(History, t => History.Serialize(w)));
|
||||
|
||||
writer.WriteBlockList(Subscriptions, (w, b) => w.WriteType(b.Serial, t => b.Serial.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.ReadInt();
|
||||
|
||||
Deleted = reader.ReadBool();
|
||||
Owner = reader.ReadMobile<PlayerMobile>();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
_Points = reader.ReadLong();
|
||||
|
||||
History = reader.ReadBlock(r => r.ReadTypeCreate<PvPProfileHistory>(this, r)) ?? new PvPProfileHistory(this);
|
||||
|
||||
Subscriptions = reader.ReadBlockList(
|
||||
r =>
|
||||
{
|
||||
var serial = r.ReadTypeCreate<PvPSerial>(r) ?? new PvPSerial(r);
|
||||
|
||||
return AutoPvP.FindBattleByID(serial);
|
||||
},
|
||||
Subscriptions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPProfileHistory
|
||||
{
|
||||
public PvPProfile Profile { get; private set; }
|
||||
|
||||
public Dictionary<int, PvPProfileHistoryEntry> Entries { get; private set; }
|
||||
public Dictionary<int, PvPProfileHistoryEntry>.KeyCollection Keys => Entries.Keys;
|
||||
public Dictionary<int, PvPProfileHistoryEntry>.ValueCollection Values => Entries.Values;
|
||||
|
||||
private PvPProfileHistory(PvPProfile owner)
|
||||
{
|
||||
Profile = owner;
|
||||
}
|
||||
|
||||
public PvPProfileHistory(PvPProfile owner, params PvPProfileHistoryEntry[] entries)
|
||||
: this(owner)
|
||||
{
|
||||
if (entries == null)
|
||||
{
|
||||
Entries = new Dictionary<int, PvPProfileHistoryEntry>();
|
||||
}
|
||||
else
|
||||
{
|
||||
Entries = new Dictionary<int, PvPProfileHistoryEntry>(entries.Length);
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var season = AutoPvP.EnsureSeason(entry.Season);
|
||||
|
||||
if (season != null)
|
||||
{
|
||||
Entries[season.Number] = entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PvPProfileHistory(PvPProfile owner, IDictionary<int, PvPProfileHistoryEntry> dictionary)
|
||||
: this(owner)
|
||||
{
|
||||
Entries = new Dictionary<int, PvPProfileHistoryEntry>(dictionary);
|
||||
}
|
||||
|
||||
public PvPProfileHistory(PvPProfile owner, GenericReader reader)
|
||||
: this(owner)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public virtual PvPProfileHistoryEntry EnsureEntry(bool replace = false)
|
||||
{
|
||||
return EnsureEntry(AutoPvP.CurrentSeason, replace);
|
||||
}
|
||||
|
||||
public virtual PvPProfileHistoryEntry EnsureEntry(PvPSeason season, bool replace = false)
|
||||
{
|
||||
if (!Entries.TryGetValue(season.Number, out var entry) || entry == null || replace)
|
||||
{
|
||||
Entries[season.Number] = entry = new PvPProfileHistoryEntry(season.Number);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlockDictionary(Entries, (w, k, e) => w.WriteType(e, t => e.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.ReadInt();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Entries = reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var e = r.ReadTypeCreate<PvPProfileHistoryEntry>(r);
|
||||
|
||||
return new KeyValuePair<int, PvPProfileHistoryEntry>(e.Season, e);
|
||||
},
|
||||
Entries);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.SuperGumps;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPProfileHistoryEntry : PropertyObject, IEquatable<PvPProfileHistoryEntry>
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPSerial UID { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access, true)]
|
||||
public int Season { get; private set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Battles { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Wins { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Losses { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long PointsGained { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long PointsLost { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long Points => PointsGained - PointsLost;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long DamageTaken { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long DamageDone { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long HealingTaken { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long HealingDone { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Kills { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Deaths { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual long Resurrections { get; set; }
|
||||
|
||||
public virtual Dictionary<string, long> MiscStats { get; protected set; }
|
||||
|
||||
public virtual long this[string stat]
|
||||
{
|
||||
get => MiscStats.GetValue(stat);
|
||||
set
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(stat))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MiscStats.ContainsKey(stat))
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
MiscStats.Remove(stat);
|
||||
}
|
||||
else
|
||||
{
|
||||
MiscStats[stat] = value;
|
||||
}
|
||||
}
|
||||
else if (value >= 0)
|
||||
{
|
||||
MiscStats[stat] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PvPProfileHistoryEntry(int season)
|
||||
{
|
||||
Season = season;
|
||||
|
||||
UID = new PvPSerial(Season + "~" + TimeStamp.UtcNow + "~" + Utility.RandomDouble());
|
||||
|
||||
MiscStats = new Dictionary<string, long>();
|
||||
}
|
||||
|
||||
public PvPProfileHistoryEntry(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public virtual void SetDefaults()
|
||||
{
|
||||
Battles = 0;
|
||||
Wins = 0;
|
||||
Losses = 0;
|
||||
PointsGained = 0;
|
||||
PointsLost = 0;
|
||||
|
||||
DamageTaken = 0;
|
||||
DamageDone = 0;
|
||||
HealingTaken = 0;
|
||||
HealingDone = 0;
|
||||
Kills = 0;
|
||||
Deaths = 0;
|
||||
Resurrections = 0;
|
||||
|
||||
MiscStats.Clear();
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public virtual long GetMiscStat(string stat)
|
||||
{
|
||||
return this[stat];
|
||||
}
|
||||
|
||||
public virtual void SetMiscStat(string stat, long value)
|
||||
{
|
||||
this[stat] = value;
|
||||
}
|
||||
|
||||
public void MergeFrom(PvPProfileHistoryEntry e, bool points)
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
e.MergeTo(this, points);
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeTo(PvPProfileHistoryEntry e, bool points)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddTo(e, points);
|
||||
|
||||
Battles = 0;
|
||||
Wins = 0;
|
||||
Losses = 0;
|
||||
|
||||
if (points)
|
||||
{
|
||||
PointsGained = 0;
|
||||
PointsLost = 0;
|
||||
}
|
||||
|
||||
DamageTaken = 0;
|
||||
DamageDone = 0;
|
||||
HealingTaken = 0;
|
||||
HealingDone = 0;
|
||||
Kills = 0;
|
||||
Deaths = 0;
|
||||
Resurrections = 0;
|
||||
|
||||
MiscStats.Clear();
|
||||
}
|
||||
|
||||
public void TakeFrom(PvPProfileHistoryEntry e, bool points)
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
e.AddTo(this, points);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddTo(PvPProfileHistoryEntry e, bool points)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Battles += Battles;
|
||||
e.Wins += Wins;
|
||||
e.Losses += Losses;
|
||||
|
||||
if (points)
|
||||
{
|
||||
e.PointsGained += PointsGained;
|
||||
e.PointsLost += PointsLost;
|
||||
}
|
||||
|
||||
e.DamageTaken += DamageTaken;
|
||||
e.DamageDone += DamageDone;
|
||||
e.HealingTaken += HealingTaken;
|
||||
e.HealingDone += HealingDone;
|
||||
e.Kills += Kills;
|
||||
e.Deaths += Deaths;
|
||||
e.Resurrections += Resurrections;
|
||||
|
||||
foreach (var o in MiscStats)
|
||||
{
|
||||
e[o.Key] += o.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public string ToHtmlString(bool big)
|
||||
{
|
||||
return ToHtmlString(null, big);
|
||||
}
|
||||
|
||||
public string ToHtmlString(Mobile viewer, bool big)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (big)
|
||||
{
|
||||
sb.Append("<BIG>");
|
||||
}
|
||||
|
||||
GetHtmlString(viewer, sb);
|
||||
|
||||
if (big)
|
||||
{
|
||||
sb.Append("</BIG>");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public virtual void GetHtmlString(Mobile viewer, StringBuilder html)
|
||||
{
|
||||
html.Append(String.Empty.WrapUOHtmlColor(Color.Cyan, false));
|
||||
html.AppendLine("Statistics For Season: {0:#,0}".WrapUOHtmlBold(), Season);
|
||||
html.AppendLine();
|
||||
|
||||
html.AppendLine("Statistics:".WrapUOHtmlBold());
|
||||
html.AppendLine();
|
||||
|
||||
html.AppendLine("Battles Attended: {0:#,0}", Battles);
|
||||
html.AppendLine("Battles Won: {0:#,0}", Wins);
|
||||
html.AppendLine("Battles Lost: {0:#,0}", Losses);
|
||||
html.AppendLine();
|
||||
html.AppendLine("Points Total: {0:#,0}", Points);
|
||||
html.AppendLine("Points Gained: {0:#,0}", PointsGained);
|
||||
html.AppendLine("Points Lost: {0:#,0}", PointsLost);
|
||||
html.AppendLine();
|
||||
html.AppendLine("Kills: {0:#,0}", Kills);
|
||||
html.AppendLine("Deaths: {0:#,0}", Deaths);
|
||||
html.AppendLine("Resurrections: {0:#,0}", Resurrections);
|
||||
html.AppendLine("Damage Taken: {0:#,0}", DamageTaken);
|
||||
html.AppendLine("Damage Given: {0:#,0}", DamageDone);
|
||||
html.AppendLine("Healing Taken: {0:#,0}", HealingTaken);
|
||||
html.AppendLine("Healing Given: {0:#,0}", HealingDone);
|
||||
html.AppendLine();
|
||||
|
||||
html.Append(String.Empty.WrapUOHtmlColor(Color.GreenYellow, false));
|
||||
html.AppendLine("Misc Statistics:");
|
||||
|
||||
foreach (var kvp in MiscStats)
|
||||
{
|
||||
html.AppendLine("{0}: {1:#,0}", kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
html.AppendLine();
|
||||
html.Append(String.Empty.WrapUOHtmlColor(SuperGump.DefaultHtmlColor, false));
|
||||
}
|
||||
|
||||
public override sealed int GetHashCode()
|
||||
{
|
||||
return UID.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is PvPProfileHistoryEntry && Equals((PvPProfileHistoryEntry)obj);
|
||||
}
|
||||
|
||||
public virtual bool Equals(PvPProfileHistoryEntry other)
|
||||
{
|
||||
return !ReferenceEquals(other, null) && UID == other.UID;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
UID.Serialize(writer);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Season);
|
||||
|
||||
writer.Write(DamageTaken);
|
||||
writer.Write(DamageDone);
|
||||
writer.Write(HealingTaken);
|
||||
writer.Write(HealingDone);
|
||||
writer.Write(Kills);
|
||||
writer.Write(Deaths);
|
||||
writer.Write(Resurrections);
|
||||
|
||||
writer.Write(PointsGained);
|
||||
writer.Write(PointsLost);
|
||||
|
||||
writer.Write(Wins);
|
||||
writer.Write(Losses);
|
||||
writer.Write(Battles);
|
||||
|
||||
writer.WriteBlockDictionary(
|
||||
MiscStats,
|
||||
(w, k, v) =>
|
||||
{
|
||||
w.Write(k);
|
||||
w.Write(v);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
UID = version > 0 ? new PvPSerial(reader) : new PvPSerial(TimeStamp.UtcNow + "~" + Utility.RandomDouble());
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
case 0:
|
||||
{
|
||||
Season = reader.ReadInt();
|
||||
|
||||
DamageTaken = reader.ReadLong();
|
||||
DamageDone = reader.ReadLong();
|
||||
HealingTaken = reader.ReadLong();
|
||||
HealingDone = reader.ReadLong();
|
||||
Kills = reader.ReadLong();
|
||||
Deaths = reader.ReadLong();
|
||||
Resurrections = reader.ReadLong();
|
||||
|
||||
PointsGained = reader.ReadLong();
|
||||
PointsLost = reader.ReadLong();
|
||||
|
||||
Wins = reader.ReadLong();
|
||||
Losses = reader.ReadLong();
|
||||
Battles = reader.ReadLong();
|
||||
|
||||
MiscStats = reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var k = r.ReadString();
|
||||
var v = r.ReadLong();
|
||||
|
||||
return new KeyValuePair<string, long>(k, v);
|
||||
},
|
||||
MiscStats);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(PvPProfileHistoryEntry left, PvPProfileHistoryEntry right)
|
||||
{
|
||||
return ReferenceEquals(left, null) ? ReferenceEquals(right, null) : left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(PvPProfileHistoryEntry left, PvPProfileHistoryEntry right)
|
||||
{
|
||||
return ReferenceEquals(left, null) ? !ReferenceEquals(right, null) : !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum PvPProfileRankOrder
|
||||
{
|
||||
None,
|
||||
Points,
|
||||
Wins,
|
||||
Kills
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
using Server.Items;
|
||||
using Server.Mobiles;
|
||||
using Server.Regions;
|
||||
using Server.Targeting;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public abstract class PvPRegion : BaseRegion
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPBattle Battle { get; set; }
|
||||
|
||||
public PvPRegion(PvPBattle battle, string name, params Rectangle3D[] bounds)
|
||||
: base(name, battle.Options.Locations.Map, battle.Options.Locations.BattlePriority, bounds.Ensure().ZFix().ToArray())
|
||||
{
|
||||
Battle = battle;
|
||||
}
|
||||
|
||||
public PvPRegion(PvPBattle battle, string name, GenericReader reader)
|
||||
: this(battle, name)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public void MicroSync()
|
||||
{
|
||||
if (Battle != null)
|
||||
{
|
||||
OnMicroSync();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnMicroSync()
|
||||
{ }
|
||||
|
||||
public override bool AllowBeneficial(Mobile from, Mobile target)
|
||||
{
|
||||
if (Battle != null)
|
||||
{
|
||||
if (NotoUtility.Resolve(from, target, out PlayerMobile x, out PlayerMobile y))
|
||||
{
|
||||
var result = Battle.AllowBeneficial(x, y, out var handled);
|
||||
|
||||
if (handled)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.AllowBeneficial(from, target);
|
||||
}
|
||||
|
||||
public override bool AllowHarmful(Mobile from, IDamageable target)
|
||||
{
|
||||
if (target is Mobile mt)
|
||||
{
|
||||
return AllowHarmful(from, mt);
|
||||
}
|
||||
|
||||
return base.AllowHarmful(from, target);
|
||||
}
|
||||
|
||||
public virtual bool AllowHarmful(Mobile from, Mobile target)
|
||||
{
|
||||
if (Battle != null)
|
||||
{
|
||||
if (NotoUtility.Resolve(from, target, out PlayerMobile x, out PlayerMobile y))
|
||||
{
|
||||
var result = Battle.AllowHarmful(x, y, out var handled);
|
||||
|
||||
if (handled)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.AllowHarmful(from, target);
|
||||
}
|
||||
|
||||
public override bool AllowHousing(Mobile from, Point3D p)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.AllowHousing(from, p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.AllowHousing(from, p);
|
||||
}
|
||||
|
||||
public override bool AllowSpawn()
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.AllowSpawn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.AllowSpawn();
|
||||
}
|
||||
|
||||
public override bool CanUseStuckMenu(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.CanUseStuckMenu(m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CanUseStuckMenu(m);
|
||||
}
|
||||
|
||||
public override void OnAggressed(Mobile aggressor, Mobile aggressed, bool criminal)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnAggressed(aggressor, aggressed, criminal);
|
||||
}
|
||||
|
||||
base.OnAggressed(aggressor, aggressed, criminal);
|
||||
}
|
||||
|
||||
public override bool AcceptsSpawnsFrom(Region region)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.AcceptsSpawnsFrom(region))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.AcceptsSpawnsFrom(region);
|
||||
}
|
||||
|
||||
public override void AlterLightLevel(Mobile m, ref int global, ref int personal)
|
||||
{
|
||||
base.AlterLightLevel(m, ref global, ref personal);
|
||||
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.AlterLightLevel(m, ref global, ref personal);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CheckAccessibility(Item item, Mobile from)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.CheckAccessibility(item, from))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CheckAccessibility(item, from);
|
||||
}
|
||||
|
||||
public override TimeSpan GetLogoutDelay(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
return Battle.GetLogoutDelay(m);
|
||||
}
|
||||
|
||||
return base.GetLogoutDelay(m);
|
||||
}
|
||||
|
||||
public override bool OnDecay(Item item)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnDecay(item))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnDecay(item);
|
||||
}
|
||||
|
||||
public override bool OnBeginSpellCast(Mobile m, ISpell s)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnBeginSpellCast(m, s))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnBeginSpellCast(m, s);
|
||||
}
|
||||
|
||||
public override void OnBeneficialAction(Mobile helper, Mobile target)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnBeneficialAction(helper, target);
|
||||
}
|
||||
|
||||
base.OnBeneficialAction(helper, target);
|
||||
}
|
||||
|
||||
public override bool OnCombatantChange(Mobile m, IDamageable oldC, IDamageable newC)
|
||||
{
|
||||
if (oldC is Mobile || newC is Mobile)
|
||||
{
|
||||
return OnCombatantChange(m, oldC as Mobile, newC as Mobile);
|
||||
}
|
||||
|
||||
return base.OnCombatantChange(m, oldC, newC);
|
||||
}
|
||||
|
||||
public virtual bool OnCombatantChange(Mobile m, Mobile oldMob, Mobile newMob)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnCombatantChange(m, oldMob, newMob))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnCombatantChange(m, oldMob, newMob);
|
||||
}
|
||||
|
||||
public override void OnCriminalAction(Mobile m, bool message)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnCriminalAction(m, message);
|
||||
}
|
||||
|
||||
base.OnCriminalAction(m, message);
|
||||
}
|
||||
|
||||
public override bool OnDamage(Mobile m, ref int damage)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
if (!Battle.CheckDamage(m, ref damage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Timer.DelayCall(d => OnDamage(m, m.GetLastDamager(true), d), damage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnDamage(m, ref damage);
|
||||
}
|
||||
|
||||
protected void OnDamage(Mobile m, IEntity damager, int damage)
|
||||
{
|
||||
OnDamage(m, damager as Mobile, damage);
|
||||
}
|
||||
|
||||
protected virtual void OnDamage(Mobile m, Mobile damager, int damage)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnDamage(damager, m, damage);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnBeforeDeath(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnBeforeDeath(m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnBeforeDeath(m);
|
||||
}
|
||||
|
||||
public override void OnDeath(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnDeath(m);
|
||||
}
|
||||
|
||||
base.OnDeath(m);
|
||||
}
|
||||
|
||||
public override void OnDidHarmful(Mobile harmer, IDamageable harmed)
|
||||
{
|
||||
if (harmed is Mobile mh)
|
||||
{
|
||||
OnDidHarmful(harmer, mh);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnDidHarmful(harmer, harmed);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnDidHarmful(Mobile harmer, Mobile harmed)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnDidHarmful(harmer, harmed);
|
||||
}
|
||||
|
||||
base.OnDidHarmful(harmer, harmed);
|
||||
}
|
||||
|
||||
public override bool OnSingleClick(Mobile m, object o)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnSingleClick(m, o))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnSingleClick(m, o);
|
||||
}
|
||||
|
||||
public override bool OnDoubleClick(Mobile m, object o)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnDoubleClick(m, o))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnDoubleClick(m, o);
|
||||
}
|
||||
|
||||
public override void OnEnter(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnEnter(this, m);
|
||||
}
|
||||
|
||||
base.OnEnter(m);
|
||||
}
|
||||
|
||||
public override void OnExit(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnExit(this, m);
|
||||
}
|
||||
|
||||
base.OnExit(m);
|
||||
}
|
||||
|
||||
public override void OnGotBeneficialAction(Mobile helper, Mobile target)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnGotBeneficialAction(helper, target);
|
||||
}
|
||||
|
||||
base.OnGotBeneficialAction(helper, target);
|
||||
}
|
||||
|
||||
public override void OnGotHarmful(Mobile harmer, IDamageable harmed)
|
||||
{
|
||||
if (harmed is Mobile mh)
|
||||
{
|
||||
OnGotHarmful(harmer, mh);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.OnGotHarmful(harmer, harmed);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnGotHarmful(Mobile harmer, Mobile harmed)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnGotHarmful(harmer, harmed);
|
||||
}
|
||||
|
||||
base.OnGotHarmful(harmer, harmed);
|
||||
}
|
||||
|
||||
public override bool OnHeal(Mobile m, ref int heal)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
if (!Battle.CheckHeal(m, ref heal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Timer.DelayCall(h => OnHeal(m, m.GetLastHealer(true), h), heal);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnHeal(m, ref heal);
|
||||
}
|
||||
|
||||
protected void OnHeal(Mobile m, IEntity healer, int heal)
|
||||
{
|
||||
OnHeal(m, healer as Mobile, heal);
|
||||
}
|
||||
|
||||
protected virtual void OnHeal(Mobile m, Mobile healer, int heal)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnHeal(healer, m, heal);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnLocationChanged(Mobile m, Point3D oldLocation)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnLocationChanged(m, oldLocation);
|
||||
}
|
||||
|
||||
base.OnLocationChanged(m, oldLocation);
|
||||
}
|
||||
|
||||
public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnMoveInto(m, d, newLocation, oldLocation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnMoveInto(m, d, newLocation, oldLocation);
|
||||
}
|
||||
|
||||
public override bool OnResurrect(Mobile m)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnResurrect(m))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnResurrect(m);
|
||||
}
|
||||
|
||||
public override bool OnSkillUse(Mobile m, int skill)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnSkillUse(m, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnSkillUse(m, skill);
|
||||
}
|
||||
|
||||
public override void OnSpeech(SpeechEventArgs args)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnSpeech(args);
|
||||
}
|
||||
|
||||
base.OnSpeech(args);
|
||||
}
|
||||
|
||||
public override void OnSpellCast(Mobile m, ISpell s)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.OnSpellCast(m, s);
|
||||
}
|
||||
|
||||
base.OnSpellCast(m, s);
|
||||
}
|
||||
|
||||
public override bool OnTarget(Mobile m, Target t, object o)
|
||||
{
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden && !Battle.OnTarget(m, t, o))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.OnTarget(m, t, o);
|
||||
}
|
||||
|
||||
public override void SpellDamageScalar(Mobile caster, Mobile target, ref double damage)
|
||||
{
|
||||
base.SpellDamageScalar(caster, target, ref damage);
|
||||
|
||||
if (Battle != null && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
Battle.SpellDamageScalar(caster, target, ref damage);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PvPBattleRegion : PvPRegion
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool FloorItemDelete { get; set; }
|
||||
|
||||
public PvPBattleRegion(PvPBattle battle)
|
||||
: base(battle, battle.BattleRegionName, battle.Options.Locations.BattleBounds.ToArray())
|
||||
{ }
|
||||
|
||||
public PvPBattleRegion(PvPBattle battle, GenericReader reader)
|
||||
: base(battle, battle.BattleRegionName, reader)
|
||||
{ }
|
||||
|
||||
public override void OnMicroSync()
|
||||
{
|
||||
base.OnMicroSync();
|
||||
|
||||
if (FloorItemDelete && Battle.State != PvPBattleState.Internal && !Battle.Hidden)
|
||||
{
|
||||
var delete = Area.SelectMany(r => r.FindEntities<Item>(Map))
|
||||
.Not(i => i == null || i.Deleted || i is Static || i is LOSBlocker || i is Blocker)
|
||||
.Where(i => i.Movable && i.Visible && i.Decays);
|
||||
|
||||
foreach (var i in delete)
|
||||
{
|
||||
i.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
writer.Write(FloorItemDelete);
|
||||
goto case 0;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
FloorItemDelete = reader.ReadBool();
|
||||
goto case 0;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PvPSpectateRegion : PvPRegion
|
||||
{
|
||||
public PvPSpectateRegion(PvPBattle battle)
|
||||
: base(battle, battle.SpectateRegionName, battle.Options.Locations.SpectateBounds.ToArray())
|
||||
{ }
|
||||
|
||||
public PvPSpectateRegion(PvPBattle battle, GenericReader reader)
|
||||
: base(battle, battle.SpectateRegionName, reader)
|
||||
{ }
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public sealed class PvPScenario
|
||||
{
|
||||
private readonly Type _TypeOf;
|
||||
private string _Info;
|
||||
|
||||
private string _Name;
|
||||
|
||||
public PvPScenario(PvPBattle battle)
|
||||
{
|
||||
_TypeOf = battle.GetType();
|
||||
_Name = battle.Name;
|
||||
_Info = battle.ToHtmlString(preview: true).Replace("(Internal)", String.Empty);
|
||||
}
|
||||
|
||||
public Type TypeOf => _TypeOf;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => _Name;
|
||||
set
|
||||
{
|
||||
if (!String.IsNullOrEmpty(value))
|
||||
{
|
||||
_Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Info
|
||||
{
|
||||
get => _Info;
|
||||
set
|
||||
{
|
||||
if (!String.IsNullOrEmpty(value))
|
||||
{
|
||||
_Info = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PvPBattle CreateBattle()
|
||||
{
|
||||
var battle = Activator.CreateInstance(_TypeOf) as PvPBattle;
|
||||
|
||||
foreach (var profile in AutoPvP.Profiles.Values)
|
||||
{
|
||||
if (!profile.IsSubscribed(battle))
|
||||
{
|
||||
profile.Subscribe(battle);
|
||||
}
|
||||
}
|
||||
|
||||
return battle;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0}: {1}", _Name, _TypeOf);
|
||||
}
|
||||
|
||||
public string ToHtmlString(Mobile viewer = null, bool big = true)
|
||||
{
|
||||
return big ? String.Format("<big>{0}</big>", _Info) : _Info;
|
||||
}
|
||||
|
||||
public static implicit operator PvPScenario(PvPBattle battle)
|
||||
{
|
||||
return new PvPScenario(battle);
|
||||
}
|
||||
}
|
||||
}
|
||||
260
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/Objects/Season.cs
Normal file
260
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/Objects/Season.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPSeason : IComparable<PvPSeason>, IEquatable<PvPSeason>
|
||||
{
|
||||
public PvPSeason(int number)
|
||||
{
|
||||
Number = number;
|
||||
|
||||
Winners = new Dictionary<PlayerMobile, List<Item>>();
|
||||
Losers = new Dictionary<PlayerMobile, List<Item>>();
|
||||
}
|
||||
|
||||
public PvPSeason(GenericReader reader)
|
||||
{
|
||||
Deserialize(reader);
|
||||
}
|
||||
|
||||
public int Number { get; private set; }
|
||||
public DateTime? Started { get; private set; }
|
||||
public DateTime? Ended { get; private set; }
|
||||
|
||||
public Dictionary<PlayerMobile, List<Item>> Winners { get; private set; }
|
||||
public Dictionary<PlayerMobile, List<Item>> Losers { get; private set; }
|
||||
|
||||
public bool Active => (this == AutoPvP.CurrentSeason);
|
||||
|
||||
public virtual int CompareTo(PvPSeason a)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Number > a.Number)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Number < a.Number)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual bool Equals(PvPSeason a)
|
||||
{
|
||||
return a != null && (ReferenceEquals(this, a) || Number == a.Number);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Number;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public virtual void Start()
|
||||
{
|
||||
Started = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public virtual void End()
|
||||
{
|
||||
Ended = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public virtual void Sync()
|
||||
{
|
||||
if (Active && Started == null)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else if (!Active && Ended == null)
|
||||
{
|
||||
if (Started == null)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
End();
|
||||
AutoPvP.SeasonChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Serialize(GenericWriter writer)
|
||||
{
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
writer.WriteBlockDictionary(
|
||||
Winners,
|
||||
(w, k, v) =>
|
||||
{
|
||||
w.Write(k);
|
||||
w.WriteItemList(v, true);
|
||||
});
|
||||
|
||||
writer.WriteBlockDictionary(
|
||||
Losers,
|
||||
(w, k, v) =>
|
||||
{
|
||||
w.Write(k);
|
||||
w.WriteItemList(v, true);
|
||||
});
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Number);
|
||||
|
||||
if (version < 1)
|
||||
{
|
||||
writer.WriteMobileList(Winners.Keys.ToList(), true);
|
||||
writer.WriteMobileList(Losers.Keys.ToList(), true);
|
||||
}
|
||||
|
||||
if (Started != null)
|
||||
{
|
||||
writer.Write(true);
|
||||
writer.Write(Started.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(false);
|
||||
}
|
||||
|
||||
if (Ended != null)
|
||||
{
|
||||
writer.Write(true);
|
||||
writer.Write(Ended.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Deserialize(GenericReader reader)
|
||||
{
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
Winners = reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var k = r.ReadMobile<PlayerMobile>();
|
||||
var v = r.ReadStrongItemList();
|
||||
return new KeyValuePair<PlayerMobile, List<Item>>(k, v);
|
||||
});
|
||||
|
||||
Losers = reader.ReadBlockDictionary(
|
||||
r =>
|
||||
{
|
||||
var k = r.ReadMobile<PlayerMobile>();
|
||||
var v = r.ReadStrongItemList();
|
||||
return new KeyValuePair<PlayerMobile, List<Item>>(k, v);
|
||||
});
|
||||
}
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
Number = reader.ReadInt();
|
||||
|
||||
if (version < 1)
|
||||
{
|
||||
var winners = reader.ReadStrongMobileList<PlayerMobile>();
|
||||
Winners = new Dictionary<PlayerMobile, List<Item>>(winners.Count);
|
||||
winners.ForEach(m => Winners.Add(m, new List<Item>()));
|
||||
|
||||
var losers = reader.ReadStrongMobileList<PlayerMobile>();
|
||||
Losers = new Dictionary<PlayerMobile, List<Item>>(losers.Count);
|
||||
losers.ForEach(m => Losers.Add(m, new List<Item>()));
|
||||
}
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
Started = reader.ReadDateTime();
|
||||
}
|
||||
|
||||
if (reader.ReadBool())
|
||||
{
|
||||
Ended = reader.ReadDateTime();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("Season: {0}", Number);
|
||||
}
|
||||
|
||||
public string ToHtmlString(Mobile viewer = null, bool big = true)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(ToString());
|
||||
sb.AppendLine();
|
||||
|
||||
if (Started != null)
|
||||
{
|
||||
sb.AppendLine("Started: {0}", Started);
|
||||
}
|
||||
|
||||
if (Ended != null)
|
||||
{
|
||||
sb.AppendLine("Ended: {0}", Ended);
|
||||
}
|
||||
|
||||
if (Winners.Count > 0)
|
||||
{
|
||||
sb.AppendLine("Winners");
|
||||
sb.AppendLine();
|
||||
|
||||
Winners.Keys.For(
|
||||
(i, p) => sb.AppendLine("{0}: {1}", (Numeral)(i + 1), p.Name.WrapUOHtmlColor(viewer.GetNotorietyColor(p))));
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
|
||||
return big ? String.Format("<big>{0}</big>", sb) : sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
1190
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/Objects/Team.cs
Normal file
1190
Scripts/SubSystem/VitaNex/Core/Modules/AutoPVP/Objects/Team.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleBroadcasts : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleLocalBroadcasts Local { get; protected set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleWorldBroadcasts World { get; protected set; }
|
||||
|
||||
public PvPBattleBroadcasts()
|
||||
{
|
||||
Local = new PvPBattleLocalBroadcasts();
|
||||
World = new PvPBattleWorldBroadcasts();
|
||||
}
|
||||
|
||||
public PvPBattleBroadcasts(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Broadcasts";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Local.Clear();
|
||||
World.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Local.Reset();
|
||||
World.Reset();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlock(w => w.WriteType(Local, t => Local.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(World, t => World.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlock(r => Local = r.ReadTypeCreate<PvPBattleLocalBroadcasts>(r) ?? new PvPBattleLocalBroadcasts());
|
||||
reader.ReadBlock(r => World = r.ReadTypeCreate<PvPBattleWorldBroadcasts>(r) ?? new PvPBattleWorldBroadcasts());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum PvPBattleLocalBroadcastMode
|
||||
{
|
||||
Disabled,
|
||||
Broadcast
|
||||
}
|
||||
|
||||
[PropertyObject]
|
||||
public class PvPBattleLocalBroadcasts : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPBattleLocalBroadcastMode Mode { get; set; }
|
||||
|
||||
[Hue, CommandProperty(AutoPvP.Access)]
|
||||
public int MessageHue { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool OpenNotify { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool StartNotify { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool EndNotify { get; set; }
|
||||
|
||||
public PvPBattleLocalBroadcasts()
|
||||
{
|
||||
OpenNotify = true;
|
||||
StartNotify = true;
|
||||
EndNotify = true;
|
||||
MessageHue = 85;
|
||||
Mode = PvPBattleLocalBroadcastMode.Disabled;
|
||||
}
|
||||
|
||||
public PvPBattleLocalBroadcasts(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Local Broadcasts";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
OpenNotify = false;
|
||||
StartNotify = false;
|
||||
EndNotify = false;
|
||||
MessageHue = 0;
|
||||
Mode = PvPBattleLocalBroadcastMode.Disabled;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
OpenNotify = true;
|
||||
StartNotify = true;
|
||||
EndNotify = true;
|
||||
MessageHue = 85;
|
||||
Mode = PvPBattleLocalBroadcastMode.Disabled;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteFlag(Mode);
|
||||
writer.Write(MessageHue);
|
||||
writer.Write(OpenNotify);
|
||||
writer.Write(StartNotify);
|
||||
writer.Write(EndNotify);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Mode = reader.ReadFlag<PvPBattleLocalBroadcastMode>();
|
||||
MessageHue = reader.ReadInt();
|
||||
OpenNotify = reader.ReadBool();
|
||||
StartNotify = reader.ReadBool();
|
||||
EndNotify = reader.ReadBool();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public enum PvPBattleWorldBroadcastMode
|
||||
{
|
||||
Disabled,
|
||||
Broadcast,
|
||||
TownCrier,
|
||||
Notify
|
||||
}
|
||||
|
||||
[PropertyObject]
|
||||
public class PvPBattleWorldBroadcasts : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public PvPBattleWorldBroadcastMode Mode { get; set; }
|
||||
|
||||
[Hue, CommandProperty(AutoPvP.Access)]
|
||||
public int MessageHue { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool OpenNotify { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool StartNotify { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool EndNotify { get; set; }
|
||||
|
||||
public PvPBattleWorldBroadcasts()
|
||||
{
|
||||
OpenNotify = true;
|
||||
StartNotify = true;
|
||||
EndNotify = true;
|
||||
MessageHue = 85;
|
||||
Mode = PvPBattleWorldBroadcastMode.Broadcast;
|
||||
}
|
||||
|
||||
public PvPBattleWorldBroadcasts(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "World Broadcasts";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
OpenNotify = false;
|
||||
StartNotify = false;
|
||||
EndNotify = false;
|
||||
MessageHue = 0;
|
||||
Mode = PvPBattleWorldBroadcastMode.Disabled;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
OpenNotify = true;
|
||||
StartNotify = true;
|
||||
EndNotify = true;
|
||||
MessageHue = 85;
|
||||
Mode = PvPBattleWorldBroadcastMode.Broadcast;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteFlag(Mode);
|
||||
writer.Write(MessageHue);
|
||||
writer.Write(OpenNotify);
|
||||
writer.Write(StartNotify);
|
||||
writer.Write(EndNotify);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.ReadInt();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Mode = reader.ReadFlag<PvPBattleWorldBroadcastMode>();
|
||||
MessageHue = reader.ReadInt();
|
||||
OpenNotify = reader.ReadBool();
|
||||
StartNotify = reader.ReadBool();
|
||||
EndNotify = reader.ReadBool();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleLocations : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattlePriority { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual Map Map { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual Point3D SpectateJoin { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual MapPoint Eject { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual MapPoint SpectateGate { get; set; }
|
||||
|
||||
public virtual List<Rectangle3D> BattleBounds { get; set; }
|
||||
public virtual List<Rectangle3D> SpectateBounds { get; set; }
|
||||
|
||||
public Point3D BattleFixedPoint => GetBattleFixedPoint();
|
||||
public Point3D SpectateFixedPoint => GetSpectateFixedPoint();
|
||||
|
||||
public PvPBattleLocations()
|
||||
{
|
||||
BattlePriority = Region.DefaultPriority;
|
||||
|
||||
Map = Map.Internal;
|
||||
Eject = MapPoint.Empty;
|
||||
|
||||
SpectateGate = MapPoint.Empty;
|
||||
SpectateJoin = Point3D.Zero;
|
||||
|
||||
BattleBounds = new List<Rectangle3D>();
|
||||
SpectateBounds = new List<Rectangle3D>();
|
||||
}
|
||||
|
||||
public PvPBattleLocations(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Locations";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Map = Map.Internal;
|
||||
BattlePriority = Region.DefaultPriority;
|
||||
BattleBounds.Free(true);
|
||||
SpectateBounds.Free(true);
|
||||
SpectateJoin = Point3D.Zero;
|
||||
Eject = MapPoint.Empty;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Map = Map.Internal;
|
||||
BattlePriority = Region.DefaultPriority;
|
||||
BattleBounds.Free(true);
|
||||
SpectateBounds.Free(true);
|
||||
SpectateJoin = Point3D.Zero;
|
||||
Eject = MapPoint.Empty;
|
||||
}
|
||||
|
||||
public Point3D GetBattleFixedPoint()
|
||||
{
|
||||
if (BattleBounds == null || BattleBounds.Count == 0)
|
||||
{
|
||||
return Point3D.Zero;
|
||||
}
|
||||
|
||||
var p = BattleBounds[0].Start;
|
||||
|
||||
return p.ToPoint3D(Map.GetAverageZ(p.X, p.Y));
|
||||
}
|
||||
|
||||
public Point3D GetSpectateFixedPoint()
|
||||
{
|
||||
if (SpectateBounds == null || SpectateBounds.Count == 0)
|
||||
{
|
||||
return Point3D.Zero;
|
||||
}
|
||||
|
||||
var p = SpectateBounds[0].Start;
|
||||
|
||||
return p.ToPoint3D(Map.GetAverageZ(p.X, p.Y));
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
SpectateGate.Serialize(writer);
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Map);
|
||||
writer.Write(BattlePriority);
|
||||
|
||||
Eject.Serialize(writer);
|
||||
|
||||
writer.Write(SpectateJoin);
|
||||
|
||||
writer.WriteBlockList(BattleBounds, (w, b) => w.Write(b));
|
||||
writer.WriteBlockList(SpectateBounds, (w, b) => w.Write(b));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
SpectateGate = new MapPoint(reader);
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
Map = reader.ReadMap();
|
||||
BattlePriority = reader.ReadInt();
|
||||
|
||||
Eject = new MapPoint(reader);
|
||||
|
||||
SpectateJoin = reader.ReadPoint3D();
|
||||
|
||||
BattleBounds = reader.ReadBlockList(r => r.ReadRect3D());
|
||||
SpectateBounds = reader.ReadBlockList(r => r.ReadRect3D());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleMissions : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleObjectives Team { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleObjectives Player { get; set; }
|
||||
|
||||
public PvPBattleMissions()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
Team = new PvPBattleObjectives();
|
||||
Player = new PvPBattleObjectives();
|
||||
}
|
||||
|
||||
public PvPBattleMissions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public virtual double ComputeScore(PvPTeam team)
|
||||
{
|
||||
if (!Enabled || Team.IsEmpty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Team.ComputeScore(team);
|
||||
}
|
||||
|
||||
public virtual double ComputeScore(PvPBattle battle, PlayerMobile player)
|
||||
{
|
||||
if (!Enabled || Player.IsEmpty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Player.ComputeScore(battle, player);
|
||||
}
|
||||
|
||||
public virtual bool Completed(PvPTeam team)
|
||||
{
|
||||
if (!Enabled || Team.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Team.Completed(team);
|
||||
}
|
||||
|
||||
public virtual bool Completed(PvPBattle battle, PlayerMobile player)
|
||||
{
|
||||
if (!Enabled || Player.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Player.Completed(battle, player);
|
||||
}
|
||||
|
||||
public virtual string GetStatus(PvPTeam team)
|
||||
{
|
||||
if (!Enabled || Team.IsEmpty)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
return Team.GetStatus(team);
|
||||
}
|
||||
|
||||
public virtual string GetStatus(PvPBattle battle, PlayerMobile player)
|
||||
{
|
||||
if (!Enabled || Player.IsEmpty)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
return Player.GetStatus(battle, player);
|
||||
}
|
||||
|
||||
public virtual void GetHtmlString(StringBuilder html)
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = html.Length;
|
||||
var len = html.Length;
|
||||
|
||||
if (!Team.IsEmpty)
|
||||
{
|
||||
Team.GetHtmlString(html);
|
||||
|
||||
if (len < html.Length)
|
||||
{
|
||||
html.Insert(len, "Team Objectives\n".WrapUOHtmlColor(Color.LawnGreen, false));
|
||||
}
|
||||
}
|
||||
|
||||
len = html.Length;
|
||||
|
||||
if (!Player.IsEmpty)
|
||||
{
|
||||
Player.GetHtmlString(html);
|
||||
|
||||
if (len < html.Length)
|
||||
{
|
||||
html.Insert(len, "Player Objectives\n".WrapUOHtmlColor(Color.LawnGreen, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < html.Length)
|
||||
{
|
||||
html.Insert(idx, "Missions\n".WrapUOHtmlBig().WrapUOHtmlColor(Color.PaleGoldenrod, false));
|
||||
html.Append(String.Empty.WrapUOHtmlColor(Color.White, false));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ToHtmlString()
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetHtmlString(html);
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Missions";
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(Enabled);
|
||||
|
||||
writer.WriteBlock(w => w.WriteType(Team, (w1, t) => Team.Serialize(w1)));
|
||||
writer.WriteBlock(w => w.WriteType(Player, (w1, t) => Player.Serialize(w1)));
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
Enabled = reader.ReadBool();
|
||||
|
||||
Team = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleObjectives>(r)) ?? new PvPBattleObjectives();
|
||||
Player = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleObjectives>(r)) ?? new PvPBattleObjectives();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleObjectives : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllRequired { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long PointsTotal { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long PointsGained { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long PointsLost { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long Kills { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long Deaths { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long Resurrections { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long DamageTaken { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long DamageDone { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long HealingTaken { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public long HealingDone { get; set; }
|
||||
|
||||
public virtual bool IsEmpty => PointsTotal + PointsGained + PointsLost
|
||||
+ Kills + Deaths + Resurrections
|
||||
+ DamageTaken + DamageDone
|
||||
+ HealingTaken + HealingDone
|
||||
<= 0;
|
||||
|
||||
public PvPBattleObjectives()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public PvPBattleObjectives(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public virtual void SetDefaults()
|
||||
{
|
||||
AllRequired = false;
|
||||
|
||||
PointsTotal = 0;
|
||||
PointsGained = 0;
|
||||
PointsLost = 0;
|
||||
Kills = 0;
|
||||
Deaths = 0;
|
||||
Resurrections = 0;
|
||||
DamageTaken = 0;
|
||||
DamageDone = 0;
|
||||
HealingTaken = 0;
|
||||
HealingDone = 0;
|
||||
}
|
||||
|
||||
public double ComputeScorePotential(PvPTeam t)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
ComputeScore(t, ref min, ref max, ref total);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public double ComputeScore(PvPTeam t)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
return ComputeScore(t, ref min, ref max, ref total);
|
||||
}
|
||||
|
||||
public virtual double ComputeScore(PvPTeam t, ref double min, ref double max, ref double total)
|
||||
{
|
||||
if (t == null || t.Deleted)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double val, score = 0.0;
|
||||
|
||||
if (PointsTotal > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalPoints() / (double)PointsTotal);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (PointsGained > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalPointsGained() / (double)PointsGained);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (PointsLost > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalPointsLost() / (double)PointsLost);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Kills > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalKills() / (double)Kills);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Deaths > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalDeaths() / (double)Deaths);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Resurrections > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalResurrections() / (double)Resurrections);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (DamageTaken > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalDamageTaken() / (double)DamageTaken);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (DamageDone > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalDamageDone() / (double)DamageDone);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (HealingTaken > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalHealingTaken() / (double)HealingTaken);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (HealingDone > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, t.GetTotalHealingDone() / (double)HealingDone);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public double ComputeScorePotential(PvPBattle b, PlayerMobile p)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
ComputeScore(b, p, ref min, ref max, ref total);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public double ComputeScore(PvPBattle b, PlayerMobile p)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
return ComputeScore(b, p, ref min, ref max, ref total);
|
||||
}
|
||||
|
||||
public virtual double ComputeScore(PvPBattle b, PlayerMobile p, ref double min, ref double max, ref double total)
|
||||
{
|
||||
if (b == null || b.Deleted)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p == null || p.Deleted)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double val, score = 0.0;
|
||||
|
||||
if (PointsTotal > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.Points) / (double)PointsTotal);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (PointsGained > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.PointsGained) / (double)PointsGained);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (PointsLost > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.PointsLost) / (double)PointsLost);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Kills > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.Kills) / (double)Kills);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Deaths > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.Deaths) / (double)Deaths);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (Resurrections > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.Resurrections) / (double)Resurrections);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (DamageTaken > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.DamageTaken) / (double)DamageTaken);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (DamageDone > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.DamageDone) / (double)DamageDone);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (HealingTaken > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.HealingTaken) / (double)HealingTaken);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
if (HealingDone > 0)
|
||||
{
|
||||
score += val = Math.Min(1.0, b.GetStatistic(p, o => o.HealingDone) / (double)HealingDone);
|
||||
|
||||
min = Math.Min(min, val);
|
||||
max = Math.Max(max, val);
|
||||
|
||||
++total;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
public virtual bool Completed(PvPTeam t)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
var score = ComputeScore(t, ref min, ref max, ref total);
|
||||
|
||||
if (score <= 0 || max <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AllRequired)
|
||||
{
|
||||
return score >= total;
|
||||
}
|
||||
|
||||
return max >= 1.0;
|
||||
}
|
||||
|
||||
public virtual bool Completed(PvPBattle b, PlayerMobile p)
|
||||
{
|
||||
double min = 0, max = 0, total = 0;
|
||||
|
||||
var score = ComputeScore(b, p, ref min, ref max, ref total);
|
||||
|
||||
if (score <= 0 || total <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AllRequired)
|
||||
{
|
||||
return score >= total;
|
||||
}
|
||||
|
||||
return max >= 1.0;
|
||||
}
|
||||
|
||||
public virtual string GetStatus(PvPTeam t)
|
||||
{
|
||||
if (t == null || t.Deleted)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var lines = new StringBuilder();
|
||||
|
||||
if (PointsTotal > 0)
|
||||
{
|
||||
lines.AppendLine("Points Total: {0:#,0} / {1:#,0}", t.GetTotalPoints(), PointsTotal);
|
||||
}
|
||||
|
||||
if (PointsGained > 0)
|
||||
{
|
||||
lines.AppendLine("Points Gained: {0:#,0} / {1:#,0}", t.GetTotalPointsGained(), PointsGained);
|
||||
}
|
||||
|
||||
if (PointsLost > 0)
|
||||
{
|
||||
lines.AppendLine("Points Lost: {0:#,0} / {1:#,0}", t.GetTotalPointsLost(), PointsLost);
|
||||
}
|
||||
|
||||
if (Kills > 0)
|
||||
{
|
||||
lines.AppendLine("Kills: {0:#,0} / {1:#,0}", t.GetTotalKills(), Kills);
|
||||
}
|
||||
|
||||
if (Deaths > 0)
|
||||
{
|
||||
lines.AppendLine("Deaths: {0:#,0} / {1:#,0}", t.GetTotalDeaths(), Deaths);
|
||||
}
|
||||
|
||||
if (Resurrections > 0)
|
||||
{
|
||||
lines.AppendLine("Resurrections: {0:#,0} / {1:#,0}", t.GetTotalResurrections(), Resurrections);
|
||||
}
|
||||
|
||||
if (DamageTaken > 0)
|
||||
{
|
||||
lines.AppendLine("Damage Taken: {0:#,0} / {1:#,0}", t.GetTotalDamageTaken(), DamageTaken);
|
||||
}
|
||||
|
||||
if (DamageDone > 0)
|
||||
{
|
||||
lines.AppendLine("Damage Done: {0:#,0} / {1:#,0}", t.GetTotalDamageDone(), DamageDone);
|
||||
}
|
||||
|
||||
if (HealingTaken > 0)
|
||||
{
|
||||
lines.AppendLine("Healing Taken: {0:#,0} / {1:#,0}", t.GetTotalHealingTaken(), HealingTaken);
|
||||
}
|
||||
|
||||
if (HealingDone > 0)
|
||||
{
|
||||
lines.AppendLine("Healing Done: {0:#,0} / {1:#,0}", t.GetTotalHealingDone(), HealingDone);
|
||||
}
|
||||
|
||||
return lines.ToString();
|
||||
}
|
||||
|
||||
public virtual string GetStatus(PvPBattle b, PlayerMobile p)
|
||||
{
|
||||
if (b == null || b.Deleted)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
if (p == null || p.Deleted)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
var lines = new StringBuilder();
|
||||
|
||||
if (PointsTotal > 0)
|
||||
{
|
||||
lines.AppendLine("Points Total: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.Points), PointsTotal);
|
||||
}
|
||||
|
||||
if (PointsGained > 0)
|
||||
{
|
||||
lines.AppendLine("Points Gained: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.PointsGained), PointsGained);
|
||||
}
|
||||
|
||||
if (PointsLost > 0)
|
||||
{
|
||||
lines.AppendLine("Points Lost: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.PointsLost), PointsLost);
|
||||
}
|
||||
|
||||
if (Kills > 0)
|
||||
{
|
||||
lines.AppendLine("Kills: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.Kills), Kills);
|
||||
}
|
||||
|
||||
if (Deaths > 0)
|
||||
{
|
||||
lines.AppendLine("Deaths: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.Deaths), Deaths);
|
||||
}
|
||||
|
||||
if (Resurrections > 0)
|
||||
{
|
||||
lines.AppendLine("Resurrections: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.Resurrections), Resurrections);
|
||||
}
|
||||
|
||||
if (DamageTaken > 0)
|
||||
{
|
||||
lines.AppendLine("Damage Taken: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.DamageTaken), DamageTaken);
|
||||
}
|
||||
|
||||
if (DamageDone > 0)
|
||||
{
|
||||
lines.AppendLine("Damage Done: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.DamageDone), DamageDone);
|
||||
}
|
||||
|
||||
if (HealingTaken > 0)
|
||||
{
|
||||
lines.AppendLine("Healing Taken: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.HealingTaken), HealingTaken);
|
||||
}
|
||||
|
||||
if (HealingDone > 0)
|
||||
{
|
||||
lines.AppendLine("Healing Done: {0:#,0} / {1:#,0}", b.GetStatistic(p, o => o.HealingDone), HealingDone);
|
||||
}
|
||||
|
||||
return lines.ToString();
|
||||
}
|
||||
|
||||
public virtual void GetHtmlString(StringBuilder html)
|
||||
{
|
||||
var len = html.Length;
|
||||
|
||||
if (PointsTotal > 0)
|
||||
{
|
||||
html.AppendLine("Points Total: {0:#,0}", PointsTotal);
|
||||
}
|
||||
|
||||
if (PointsGained > 0)
|
||||
{
|
||||
html.AppendLine("Points Gained: {0:#,0}", PointsGained);
|
||||
}
|
||||
|
||||
if (PointsLost > 0)
|
||||
{
|
||||
html.AppendLine("Points Lost: {0:#,0}", PointsLost);
|
||||
}
|
||||
|
||||
if (Kills > 0)
|
||||
{
|
||||
html.AppendLine("Kills: {0:#,0}", Kills);
|
||||
}
|
||||
|
||||
if (Deaths > 0)
|
||||
{
|
||||
html.AppendLine("Deaths: {0:#,0}", Deaths);
|
||||
}
|
||||
|
||||
if (Resurrections > 0)
|
||||
{
|
||||
html.AppendLine("Resurrections: {0:#,0}", Resurrections);
|
||||
}
|
||||
|
||||
if (DamageTaken > 0)
|
||||
{
|
||||
html.AppendLine("Damage Taken: {0:#,0}", DamageTaken);
|
||||
}
|
||||
|
||||
if (DamageDone > 0)
|
||||
{
|
||||
html.AppendLine("Damage Done: {0:#,0}", DamageDone);
|
||||
}
|
||||
|
||||
if (HealingTaken > 0)
|
||||
{
|
||||
html.AppendLine("Healing Taken: {0:#,0}", HealingTaken);
|
||||
}
|
||||
|
||||
if (HealingDone > 0)
|
||||
{
|
||||
html.AppendLine("Healing Done: {0:#,0}", HealingDone);
|
||||
}
|
||||
|
||||
if (len < html.Length)
|
||||
{
|
||||
html.Insert(len, AllRequired ? "(Complete All)\n" : "(Complete Any)\n");
|
||||
html.Insert(len, String.Empty.WrapUOHtmlColor(Color.PaleGoldenrod, false));
|
||||
html.Append(String.Empty.WrapUOHtmlColor(Color.White, false));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ToHtmlString()
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
GetHtmlString(html);
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Mission Objectives";
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
|
||||
writer.Write(PointsTotal);
|
||||
writer.Write(PointsGained);
|
||||
writer.Write(PointsLost);
|
||||
writer.Write(Kills);
|
||||
writer.Write(Deaths);
|
||||
writer.Write(Resurrections);
|
||||
writer.Write(DamageTaken);
|
||||
writer.Write(DamageDone);
|
||||
writer.Write(HealingTaken);
|
||||
writer.Write(HealingDone);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
|
||||
PointsTotal = reader.ReadLong();
|
||||
PointsGained = reader.ReadLong();
|
||||
PointsLost = reader.ReadLong();
|
||||
Kills = reader.ReadLong();
|
||||
Deaths = reader.ReadLong();
|
||||
Resurrections = reader.ReadLong();
|
||||
DamageTaken = reader.ReadLong();
|
||||
DamageDone = reader.ReadLong();
|
||||
HealingTaken = reader.ReadLong();
|
||||
HealingDone = reader.ReadLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleOptions : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleBroadcasts Broadcasts { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleLocations Locations { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleRestrictions Restrictions { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPRewards Rewards { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleRules Rules { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleSounds Sounds { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleSuddenDeath SuddenDeath { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleTiming Timing { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleWeather Weather { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleMissions Missions { get; set; }
|
||||
|
||||
public PvPBattleOptions()
|
||||
{
|
||||
Broadcasts = new PvPBattleBroadcasts();
|
||||
Locations = new PvPBattleLocations();
|
||||
Restrictions = new PvPBattleRestrictions();
|
||||
Rewards = new PvPRewards();
|
||||
Rules = new PvPBattleRules();
|
||||
Sounds = new PvPBattleSounds();
|
||||
SuddenDeath = new PvPBattleSuddenDeath();
|
||||
Timing = new PvPBattleTiming();
|
||||
Weather = new PvPBattleWeather();
|
||||
Missions = new PvPBattleMissions();
|
||||
}
|
||||
|
||||
public PvPBattleOptions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Broadcasts.Clear();
|
||||
Locations.Clear();
|
||||
Restrictions.Clear();
|
||||
Rewards.Clear();
|
||||
Rules.Clear();
|
||||
Sounds.Clear();
|
||||
SuddenDeath.Clear();
|
||||
Timing.Clear();
|
||||
Weather.Clear();
|
||||
Missions.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Broadcasts.Reset();
|
||||
Locations.Reset();
|
||||
Restrictions.Reset();
|
||||
Rewards.Reset();
|
||||
Rules.Reset();
|
||||
Sounds.Reset();
|
||||
SuddenDeath.Reset();
|
||||
Timing.Reset();
|
||||
Weather.Reset();
|
||||
Missions.Reset();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Advanced Options";
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(1);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
writer.WriteBlock(w => w.WriteType(Missions, t => Missions.Serialize(w)));
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlock(w => w.WriteType(Broadcasts, t => Broadcasts.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Locations, t => Locations.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Restrictions, t => Restrictions.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Rewards, t => Rewards.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Rules, t => Rules.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Sounds, t => Sounds.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(SuddenDeath, t => SuddenDeath.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Timing, t => Timing.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Weather, t => Weather.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1:
|
||||
Missions = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleMissions>(r)) ?? new PvPBattleMissions();
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
Broadcasts = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleBroadcasts>(r)) ?? new PvPBattleBroadcasts();
|
||||
Locations = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleLocations>(r)) ?? new PvPBattleLocations();
|
||||
Restrictions = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleRestrictions>(r)) ?? new PvPBattleRestrictions();
|
||||
Rewards = reader.ReadBlock(r => r.ReadTypeCreate<PvPRewards>(r)) ?? new PvPRewards();
|
||||
Rules = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleRules>(r)) ?? new PvPBattleRules();
|
||||
Sounds = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleSounds>(r)) ?? new PvPBattleSounds();
|
||||
SuddenDeath = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleSuddenDeath>(r)) ?? new PvPBattleSuddenDeath();
|
||||
Timing = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleTiming>(r)) ?? new PvPBattleTiming();
|
||||
Weather = reader.ReadBlock(r => r.ReadTypeCreate<PvPBattleWeather>(r)) ?? new PvPBattleWeather();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (version < 1)
|
||||
{
|
||||
Missions = new PvPBattleMissions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public abstract class PvPBattleRestrictionsBase<TKey> : PropertyObject, IEnumerable<TKey>
|
||||
{
|
||||
public static void SetFlag(ref ulong flags, ulong f, bool v)
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
flags |= f;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags &= ~f;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetFlag(ulong flags, ulong f)
|
||||
{
|
||||
return (flags & f) != 0;
|
||||
}
|
||||
|
||||
private Dictionary<TKey, bool> _List = new Dictionary<TKey, bool>();
|
||||
|
||||
public virtual Dictionary<TKey, bool> List
|
||||
{
|
||||
get => _List;
|
||||
set => _List = value ?? new Dictionary<TKey, bool>();
|
||||
}
|
||||
|
||||
public bool this[TKey key] { get => IsRestricted(key); set => SetRestricted(key, value); }
|
||||
|
||||
public PvPBattleRestrictionsBase()
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public PvPBattleRestrictionsBase(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public abstract void Invalidate();
|
||||
|
||||
public virtual void Invert()
|
||||
{
|
||||
_List.Keys.ForEach(t => _List[t] = !_List[t]);
|
||||
}
|
||||
|
||||
public virtual void Reset(bool val)
|
||||
{
|
||||
_List.Keys.ForEach(t => _List[t] = val);
|
||||
}
|
||||
|
||||
public virtual bool Remove(TKey key)
|
||||
{
|
||||
return _List.Remove(key);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Reset(false);
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
_List.Clear();
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(TKey key, bool val)
|
||||
{
|
||||
if (key != null)
|
||||
{
|
||||
_List[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(TKey key)
|
||||
{
|
||||
return key != null && _List.ContainsKey(key) && _List[key];
|
||||
}
|
||||
|
||||
public IEnumerator<TKey> GetEnumerator()
|
||||
{
|
||||
return _List.Keys.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
writer.WriteDictionary(_List, SerializeEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
_List = reader.ReadDictionary(DeserializeEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void SerializeEntry(GenericWriter writer, TKey key, bool val);
|
||||
public abstract KeyValuePair<TKey, bool> DeserializeEntry(GenericReader reader);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPBattleItemRestrictions : PvPBattleRestrictionsBase<Type>
|
||||
{
|
||||
private static readonly Type _TypeOf = typeof(Item);
|
||||
|
||||
private static Type FindType(string name, bool full = false, bool ignoreCase = true)
|
||||
{
|
||||
return Type.GetType(name, false, ignoreCase) ?? (full
|
||||
? ScriptCompiler.FindTypeByFullName(name, ignoreCase)
|
||||
: ScriptCompiler.FindTypeByName(name, ignoreCase));
|
||||
}
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowNonExceptional { get; set; }
|
||||
|
||||
public PvPBattleItemRestrictions()
|
||||
{
|
||||
AllowNonExceptional = true;
|
||||
}
|
||||
|
||||
public PvPBattleItemRestrictions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Item Restrictions";
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{ }
|
||||
|
||||
public virtual void SetRestricted(Item item, bool restrict)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
SetRestricted(item.GetType(), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(string item, bool restrict)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(item))
|
||||
{
|
||||
SetRestricted(FindType(item), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetRestricted(Type key, bool val)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
base.SetRestricted(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(Item item)
|
||||
{
|
||||
return item != null && IsRestricted(item.GetType());
|
||||
}
|
||||
|
||||
public override bool IsRestricted(Type key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
return base.IsRestricted(_TypeOf) || base.IsRestricted(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var v = writer.SetVersion(1);
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
var flags = 0UL;
|
||||
|
||||
SetFlag(ref flags, 0x1, AllowNonExceptional);
|
||||
|
||||
writer.Write(flags);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var v = reader.GetVersion();
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
var flags = reader.ReadULong();
|
||||
|
||||
AllowNonExceptional = GetFlag(flags, 0x1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowNonExceptional = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void SerializeEntry(GenericWriter writer, Type key, bool val)
|
||||
{
|
||||
writer.WriteType(key);
|
||||
writer.Write(val);
|
||||
}
|
||||
|
||||
public override KeyValuePair<Type, bool> DeserializeEntry(GenericReader reader)
|
||||
{
|
||||
var k = reader.ReadType();
|
||||
var v = reader.ReadBool();
|
||||
return new KeyValuePair<Type, bool>(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Mobiles;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPBattlePetRestrictions : PvPBattleRestrictionsBase<Type>
|
||||
{
|
||||
private static readonly Type _TypeOf = typeof(BaseCreature);
|
||||
|
||||
private static Type FindType(string name, bool full = false, bool ignoreCase = true)
|
||||
{
|
||||
return Type.GetType(name, false, ignoreCase) ?? (full
|
||||
? ScriptCompiler.FindTypeByFullName(name, ignoreCase)
|
||||
: ScriptCompiler.FindTypeByName(name, ignoreCase));
|
||||
}
|
||||
|
||||
public PvPBattlePetRestrictions()
|
||||
{ }
|
||||
|
||||
public PvPBattlePetRestrictions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Pet Restrictions";
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{ }
|
||||
|
||||
public virtual void SetRestricted(BaseCreature pet, bool restrict)
|
||||
{
|
||||
if (pet != null)
|
||||
{
|
||||
SetRestricted(pet.GetType(), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(string pet, bool restrict)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(pet))
|
||||
{
|
||||
SetRestricted(FindType(pet), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetRestricted(Type key, bool val)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
base.SetRestricted(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(BaseCreature pet)
|
||||
{
|
||||
return pet != null && IsRestricted(pet.GetType());
|
||||
}
|
||||
|
||||
public override bool IsRestricted(Type key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
return base.IsRestricted(_TypeOf) || base.IsRestricted(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
|
||||
public override void SerializeEntry(GenericWriter writer, Type key, bool val)
|
||||
{
|
||||
writer.WriteType(key);
|
||||
writer.Write(val);
|
||||
}
|
||||
|
||||
public override KeyValuePair<Type, bool> DeserializeEntry(GenericReader reader)
|
||||
{
|
||||
var k = reader.ReadType();
|
||||
var v = reader.ReadBool();
|
||||
return new KeyValuePair<Type, bool>(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleRestrictions : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleItemRestrictions Items { get; protected set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattlePetRestrictions Pets { get; protected set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleSkillRestrictions Skills { get; protected set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual PvPBattleSpellRestrictions Spells { get; protected set; }
|
||||
|
||||
public PvPBattleRestrictions()
|
||||
{
|
||||
Items = new PvPBattleItemRestrictions();
|
||||
Pets = new PvPBattlePetRestrictions();
|
||||
Skills = new PvPBattleSkillRestrictions();
|
||||
Spells = new PvPBattleSpellRestrictions();
|
||||
}
|
||||
|
||||
public PvPBattleRestrictions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Restrictions";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
Pets.Clear();
|
||||
Skills.Clear();
|
||||
Spells.Clear();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Items.Reset(false);
|
||||
Pets.Reset(false);
|
||||
Skills.Reset(false);
|
||||
Spells.Reset(false);
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.WriteBlock(w => w.WriteType(Items, t => Items.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Pets, t => Pets.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Skills, t => Skills.Serialize(w)));
|
||||
writer.WriteBlock(w => w.WriteType(Spells, t => Spells.Serialize(w)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
reader.ReadBlock(r => Items = r.ReadTypeCreate<PvPBattleItemRestrictions>(r) ?? new PvPBattleItemRestrictions());
|
||||
reader.ReadBlock(r => Pets = r.ReadTypeCreate<PvPBattlePetRestrictions>(r) ?? new PvPBattlePetRestrictions());
|
||||
reader.ReadBlock(
|
||||
r => Skills = r.ReadTypeCreate<PvPBattleSkillRestrictions>(r) ?? new PvPBattleSkillRestrictions());
|
||||
reader.ReadBlock(
|
||||
r => Spells = r.ReadTypeCreate<PvPBattleSpellRestrictions>(r) ?? new PvPBattleSpellRestrictions());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPBattleSkillRestrictions : PvPBattleRestrictionsBase<int>
|
||||
{
|
||||
private static readonly string[] _SkillNames = Enum.GetNames(typeof(SkillName));
|
||||
|
||||
private static int FindSkill(string name, bool ignoreCase = true)
|
||||
{
|
||||
return _SkillNames.IndexOf(
|
||||
_SkillNames.FirstOrDefault(s => ignoreCase ? Insensitive.Equals(s, name) : String.Equals(s, name)));
|
||||
}
|
||||
|
||||
public PvPBattleSkillRestrictions()
|
||||
{ }
|
||||
|
||||
public PvPBattleSkillRestrictions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Skill Restrictions";
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
foreach (var info in SkillInfo.Table)
|
||||
{
|
||||
SetRestricted(info, false);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(Skill skill, bool restrict)
|
||||
{
|
||||
if (skill != null)
|
||||
{
|
||||
SetRestricted(skill.Info, restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(SkillInfo skill, bool restrict)
|
||||
{
|
||||
if (skill != null)
|
||||
{
|
||||
SetRestricted(skill.SkillID, restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(string skill, bool restrict)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(skill))
|
||||
{
|
||||
SetRestricted(FindSkill(skill), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(SkillName skill, bool restrict)
|
||||
{
|
||||
SetRestricted((int)skill, restrict);
|
||||
}
|
||||
|
||||
public override void SetRestricted(int skill, bool restrict)
|
||||
{
|
||||
if (skill >= 0)
|
||||
{
|
||||
base.SetRestricted(skill, restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(Skill skill)
|
||||
{
|
||||
return skill != null && IsRestricted(skill.Info);
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(SkillInfo skill)
|
||||
{
|
||||
return skill != null && IsRestricted(skill.SkillID);
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(string skill)
|
||||
{
|
||||
return !String.IsNullOrEmpty(skill) && IsRestricted(FindSkill(skill));
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(SkillName skill)
|
||||
{
|
||||
return IsRestricted((int)skill);
|
||||
}
|
||||
|
||||
public override bool IsRestricted(int key)
|
||||
{
|
||||
return key >= 0 && base.IsRestricted(key);
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
|
||||
public override void SerializeEntry(GenericWriter writer, int key, bool val)
|
||||
{
|
||||
writer.Write(key);
|
||||
writer.Write(val);
|
||||
}
|
||||
|
||||
public override KeyValuePair<int, bool> DeserializeEntry(GenericReader reader)
|
||||
{
|
||||
var k = reader.ReadInt();
|
||||
var v = reader.ReadBool();
|
||||
return new KeyValuePair<int, bool>(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Server;
|
||||
using Server.Spells;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
[PropertyObject]
|
||||
public class PvPBattleSpellRestrictions : PvPBattleRestrictionsBase<Type>
|
||||
{
|
||||
private static readonly Type _TypeOf = typeof(Spell);
|
||||
|
||||
private static Type FindType(string name, bool full = false, bool ignoreCase = true)
|
||||
{
|
||||
return Type.GetType(name, false, ignoreCase) ?? (full
|
||||
? ScriptCompiler.FindTypeByFullName(name, ignoreCase)
|
||||
: ScriptCompiler.FindTypeByName(name, ignoreCase));
|
||||
}
|
||||
|
||||
public PvPBattleSpellRestrictions()
|
||||
{ }
|
||||
|
||||
public PvPBattleSpellRestrictions(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Spell Restrictions";
|
||||
}
|
||||
|
||||
public override void Invalidate()
|
||||
{
|
||||
foreach (var t in SpellRegistry.Types)
|
||||
{
|
||||
SetRestricted(t, IsRestricted(t));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(Spell spell, bool restrict)
|
||||
{
|
||||
if (spell != null)
|
||||
{
|
||||
SetRestricted(spell.GetType(), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRestricted(string spell, bool restrict)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(spell))
|
||||
{
|
||||
SetRestricted(FindType(spell), restrict);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetRestricted(Type key, bool val)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
base.SetRestricted(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsRestricted(Spell spell)
|
||||
{
|
||||
return spell != null && IsRestricted(spell.GetType());
|
||||
}
|
||||
|
||||
public override bool IsRestricted(Type key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key.IsEqualOrChildOf(_TypeOf))
|
||||
{
|
||||
return base.IsRestricted(_TypeOf) || base.IsRestricted(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
writer.SetVersion(0);
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
reader.GetVersion();
|
||||
}
|
||||
|
||||
public override void SerializeEntry(GenericWriter writer, Type key, bool val)
|
||||
{
|
||||
writer.WriteType(key);
|
||||
writer.Write(val);
|
||||
}
|
||||
|
||||
public override KeyValuePair<Type, bool> DeserializeEntry(GenericReader reader)
|
||||
{
|
||||
var k = reader.ReadType();
|
||||
var v = reader.ReadBool();
|
||||
return new KeyValuePair<Type, bool>(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Server;
|
||||
|
||||
using VitaNex.Reflection;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleRules : PropertyObject
|
||||
{
|
||||
private readonly object _CopyLock = new object();
|
||||
private PropertyList<PvPBattleRules> _CopyStore;
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowSpeech { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowBeneficial { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowHarmful { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowHousing { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowSpawn { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanDie { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanHeal { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanBeDamaged { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanDamageOwnTeam { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanDamageEnemyTeam { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanHealOwnTeam { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanHealEnemyTeam { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool AllowPets { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanMount { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanFly { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanMountEthereal { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanMoveThrough { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanResurrect { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanUseStuckMenu { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public bool CanEquip { get; set; }
|
||||
|
||||
public PvPBattleRules()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public PvPBattleRules(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Rules";
|
||||
}
|
||||
|
||||
public void SetDefaults()
|
||||
{
|
||||
AllowBeneficial = true;
|
||||
AllowHarmful = true;
|
||||
AllowHousing = false;
|
||||
AllowPets = false;
|
||||
AllowSpawn = false;
|
||||
AllowSpeech = true;
|
||||
CanBeDamaged = true;
|
||||
CanDamageEnemyTeam = true;
|
||||
CanDamageOwnTeam = false;
|
||||
CanDie = false;
|
||||
CanEquip = true;
|
||||
CanFly = false;
|
||||
CanHeal = true;
|
||||
CanHealEnemyTeam = false;
|
||||
CanHealOwnTeam = true;
|
||||
CanMount = false;
|
||||
CanMountEthereal = false;
|
||||
CanMoveThrough = false;
|
||||
CanResurrect = false;
|
||||
CanUseStuckMenu = false;
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public void CopyFrom(PvPBattleRules source)
|
||||
{
|
||||
source.CopyTo(this);
|
||||
}
|
||||
|
||||
public void CopyTo(PvPBattleRules target)
|
||||
{
|
||||
lock (_CopyLock)
|
||||
{
|
||||
if (_CopyStore == null)
|
||||
{
|
||||
_CopyStore = new PropertyList<PvPBattleRules>
|
||||
{
|
||||
Filter = p => p.Name != "InvokeReset" && p.Name != "InvokeClear" && p.PropertyType.IsEqual<bool>()
|
||||
};
|
||||
}
|
||||
|
||||
_CopyStore.Deserialize(this);
|
||||
_CopyStore.Serialize(target);
|
||||
_CopyStore.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(3);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
writer.Write(CanEquip);
|
||||
goto case 2;
|
||||
case 2:
|
||||
writer.Write(CanMoveThrough);
|
||||
goto case 1;
|
||||
case 1:
|
||||
writer.Write(CanFly);
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
writer.Write(AllowBeneficial);
|
||||
writer.Write(AllowHarmful);
|
||||
writer.Write(AllowHousing);
|
||||
writer.Write(AllowPets);
|
||||
writer.Write(AllowSpawn);
|
||||
writer.Write(AllowSpeech);
|
||||
writer.Write(CanBeDamaged);
|
||||
writer.Write(CanDamageEnemyTeam);
|
||||
writer.Write(CanDamageOwnTeam);
|
||||
writer.Write(CanDie);
|
||||
writer.Write(CanHeal);
|
||||
writer.Write(CanHealEnemyTeam);
|
||||
writer.Write(CanHealOwnTeam);
|
||||
writer.Write(CanMount);
|
||||
writer.Write(CanMountEthereal);
|
||||
writer.Write(CanResurrect);
|
||||
writer.Write(CanUseStuckMenu);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
SetDefaults();
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 3:
|
||||
CanEquip = reader.ReadBool();
|
||||
goto case 2;
|
||||
case 2:
|
||||
CanMoveThrough = reader.ReadBool();
|
||||
goto case 1;
|
||||
case 1:
|
||||
CanFly = reader.ReadBool();
|
||||
goto case 0;
|
||||
case 0:
|
||||
{
|
||||
AllowBeneficial = reader.ReadBool();
|
||||
AllowHarmful = reader.ReadBool();
|
||||
AllowHousing = reader.ReadBool();
|
||||
AllowPets = reader.ReadBool();
|
||||
AllowSpawn = reader.ReadBool();
|
||||
AllowSpeech = reader.ReadBool();
|
||||
CanBeDamaged = reader.ReadBool();
|
||||
CanDamageEnemyTeam = reader.ReadBool();
|
||||
CanDamageOwnTeam = reader.ReadBool();
|
||||
CanDie = reader.ReadBool();
|
||||
CanHeal = reader.ReadBool();
|
||||
CanHealEnemyTeam = reader.ReadBool();
|
||||
CanHealOwnTeam = reader.ReadBool();
|
||||
CanMount = reader.ReadBool();
|
||||
CanMountEthereal = reader.ReadBool();
|
||||
CanResurrect = reader.ReadBool();
|
||||
CanUseStuckMenu = reader.ReadBool();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#region Header
|
||||
// _,-'/-'/
|
||||
// . __,-; ,'( '/
|
||||
// \. `-.__`-._`:_,-._ _ , . ``
|
||||
// `:-._,------' ` _,`--` -: `_ , ` ,' :
|
||||
// `---..__,,--' (C) 2023 ` -'. -'
|
||||
// # Vita-Nex [http://core.vita-nex.com] #
|
||||
// {o)xxx|===============- # -===============|xxx(o}
|
||||
// # #
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
using Server;
|
||||
#endregion
|
||||
|
||||
namespace VitaNex.Modules.AutoPvP
|
||||
{
|
||||
public class PvPBattleSounds : PropertyObject
|
||||
{
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual bool Enabled { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int InviteSend { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int InviteAccept { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int InviteCancel { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int QueueJoin { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int QueueLeave { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int Teleport { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattleOpened { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattlePreparing { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattleStarted { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattleEnded { get; set; }
|
||||
|
||||
[CommandProperty(AutoPvP.Access)]
|
||||
public virtual int BattleCanceled { get; set; }
|
||||
|
||||
public PvPBattleSounds()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public PvPBattleSounds(GenericReader reader)
|
||||
: base(reader)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Battle Sounds";
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
InviteSend = -1;
|
||||
InviteAccept = -1;
|
||||
InviteCancel = -1;
|
||||
|
||||
QueueJoin = -1;
|
||||
QueueLeave = -1;
|
||||
|
||||
Teleport = -1;
|
||||
|
||||
BattleOpened = -1;
|
||||
BattlePreparing = -1;
|
||||
BattleStarted = -1;
|
||||
BattleEnded = -1;
|
||||
BattleCanceled = -1;
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
SetDefaults();
|
||||
}
|
||||
|
||||
public virtual void SetDefaults()
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
InviteSend = 0xF0;
|
||||
InviteAccept = 0x5B5;
|
||||
InviteCancel = 0x5B4;
|
||||
|
||||
QueueJoin = 0x664;
|
||||
QueueLeave = 0x51C;
|
||||
|
||||
Teleport = 0x029;
|
||||
|
||||
BattleOpened = 0x2E8;
|
||||
BattlePreparing = 0x2E8;
|
||||
BattleStarted = 0x2E9;
|
||||
BattleEnded = 0x2EA;
|
||||
BattleCanceled = 0x2EA;
|
||||
}
|
||||
|
||||
public override void Serialize(GenericWriter writer)
|
||||
{
|
||||
base.Serialize(writer);
|
||||
|
||||
var version = writer.SetVersion(0);
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
writer.Write(Enabled);
|
||||
|
||||
writer.Write(InviteSend);
|
||||
writer.Write(InviteAccept);
|
||||
writer.Write(InviteCancel);
|
||||
|
||||
writer.Write(QueueJoin);
|
||||
writer.Write(QueueLeave);
|
||||
|
||||
writer.Write(Teleport);
|
||||
|
||||
writer.Write(BattleOpened);
|
||||
writer.Write(BattlePreparing);
|
||||
writer.Write(BattleStarted);
|
||||
writer.Write(BattleEnded);
|
||||
writer.Write(BattleCanceled);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Deserialize(GenericReader reader)
|
||||
{
|
||||
base.Deserialize(reader);
|
||||
|
||||
var version = reader.GetVersion();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Enabled = reader.ReadBool();
|
||||
|
||||
InviteSend = reader.ReadInt();
|
||||
InviteAccept = reader.ReadInt();
|
||||
InviteCancel = reader.ReadInt();
|
||||
|
||||
QueueJoin = reader.ReadInt();
|
||||
QueueLeave = reader.ReadInt();
|
||||
|
||||
Teleport = reader.ReadInt();
|
||||
|
||||
BattleOpened = reader.ReadInt();
|
||||
BattlePreparing = reader.ReadInt();
|
||||
BattleStarted = reader.ReadInt();
|
||||
BattleEnded = reader.ReadInt();
|
||||
BattleCanceled = reader.ReadInt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user