Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View 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);
}
}
}
}

View File

@@ -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);
});
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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";
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
}
}

View File

@@ -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
}
}