using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace KfChatDotNetBot.Models.DbModels; public class GamblerDbModel { /// /// ID fo the database row /// public int Id { get; set; } /// /// User that this gambler entity is associated with. /// A user can have multiple associated gambler entities, but only one should be active /// public required UserDbModel User { get; set; } /// /// Gambler's balance. It can be negative if an admin has forced them into an overdraft /// Values are fractional, it is NOT stored as cents, therefore 100.00 KKK is stored as "100" in the database /// public required decimal Balance { get; set; } /// /// What state the gambler entity is in /// public required GamblerState State { get; set; } = GamblerState.Active; /// /// The seed value given to any instance of Random that's associated with the gambler /// [MaxLength(256)] public required string RandomSeed { get; set; } /// /// When the gambler entity was created /// public required DateTimeOffset Created { get; set; } /// /// Reference value for total wagered during the entity's lifetime /// This value is recalculated whenever the bot restarts to ensure integrity /// public required decimal TotalWagered { get; set; } /// /// Wager requirement for the next VIP level /// If TotalWagered reaches this value, it'll trigger the calculation /// public required decimal NextVipLevelWagerRequirement { get; set; } } public class TransactionDbModel { /// /// ID fo the database row /// public int Id { get; set; } /// /// Gambler whose balance was affected by this transaction /// public required GamblerDbModel Gambler { get; set; } /// /// Source of the transaction event /// public required TransactionSourceEventType EventSource { get; set; } /// /// Time when the event occurred /// public required DateTimeOffset Time { get; set; } /// /// Time represented as a 64-bit UNIX epoch /// This just exists to make it far more efficient to query a range of txns /// as then we can use native SQLite dialect to select e.g. last 24 hours /// instead of copying thousands of rows into memory and using LINQ /// public required long TimeUnixEpochSeconds { get; set; } /// /// Effect of the transaction, plus or minus /// public required decimal Effect { get; set; } /// /// Optional descriptive comment for the transaction. e.g. "Win from wager [id]", "Balance adjustment by Avenue", "Juicer from Null", etc. /// public string? Comment { get; set; } = null; /// /// Sender of the transaction in the case of a juicer, null otherwise /// public GamblerDbModel? From { get; set; } = null; /// /// Snapshot of the gambler's balance after this transaction's effect was applied /// public required decimal NewBalance { get; set; } } public class WagerDbModel { /// /// ID fo the database row /// public int Id { get; set; } /// /// Gambler who wagered /// public required GamblerDbModel Gambler { get; set; } /// /// Time they wagered /// public required DateTimeOffset Time { get; set; } /// /// Time represented as a 64-bit UNIX epoch /// This just exists to make it far more efficient to query a range of wagers /// as then we can use native SQLite dialect to select e.g. last 24 hours /// instead of copying thousands of rows into memory and using LINQ /// public required long TimeUnixEpochSeconds { get; set; } /// /// Amount the gambler wagered /// public required decimal WagerAmount { get; set; } /// /// Effect of the wager on the gambler's balance /// public required decimal WagerEffect { get; set; } /// /// Game they played to wager the amount (Note: enum must be extended for any new games. /// Don't remove games which are legacy from the enum, just give them the Obsolete attribute) /// public required WagerGame Game { get; set; } /// /// Multiplier, e.g. 10.5x if a $1 wager paid out $10.50. 0 if it was a complete loss /// public required decimal Multiplier { get; set; } /// /// An optional field to store serialized information about the game that was played /// public string? GameMeta { get; set; } = null; /// /// Whether the results of the wager have been realized yet (i.e., is the game 'complete'?) /// This is useful for wagers related to bets on the outcome of events /// For incomplete bets: set the effect to -wager, subtract it from the user's balance, generate a txn for the wager /// Then when the outcome of the bet is fully realized, modify the effect accordingly, generate a new txn for the /// payout and set a multiplier based on the win (if any) /// public required bool IsComplete { get; set; } } public class GamblerExclusionDbModel { /// /// ID fo the database row /// public int Id { get; set; } /// /// Gambler who is excluded /// public required GamblerDbModel Gambler { get; set; } /// /// When the exclusion expires /// public required DateTimeOffset Expires { get; set; } /// /// When the exclusion was created / began /// public required DateTimeOffset Created { get; set; } /// /// What triggered the exclusion /// public required ExclusionSource Source { get; set; } } public class GamblerPerkDbModel { /// /// ID fo the database row /// public int Id { get; set; } /// /// Gambler entity the perk is associated with /// public required GamblerDbModel Gambler { get; set; } /// /// Name of the perk /// [MaxLength(256)] public required string PerkName { get; set; } /// /// Time when the perk was attained /// public required DateTimeOffset Time { get; set; } /// /// Optional metadata associated with the perk /// public string? Metadata { get; set; } = null; /// /// What type of perk is this /// public required GamblerPerkType PerkType { get; set; } /// /// The tier the perk is at. /// If tiers are not applicable, set to null /// public int? PerkTier { get; set; } /// /// The payout from this perk, if any. If none, set to null /// public decimal? Payout { get; set; } } public enum GamblerPerkType { /// /// For literally anything else, though you should probably just extend this enum /// Other = -1, /// /// Used for tracking VIP levels attained /// [Description("VIP Level")] VipLevel } public enum ExclusionSource { /// /// Exclusion as a result of the hostess' action /// Hostess, /// /// Exclusions placed by administrators /// Administrative } /// /// What event triggered this transaction /// public enum TransactionSourceEventType { /// /// Generic catch-all type if nothing else suits /// Other, /// /// Juice from another user. This is only for person to person transactions, use Bonus for kasino rewards /// Juicer, /// /// Transaction generated from the result of a wager /// Gambling, /// /// For recording events related to an administrative action. e.g. balance adjustments /// Administrative, /// /// Some type of bonus, like a VIP level up. Rakeback / reloads have separate enums for this /// Bonus, /// /// Specifically use for rakeback as we use the delta between last rakeback txn to calculate total wagered /// to figure out what the next rakeback should be (if they've wagered enough to be eligible for one) /// Rakeback, /// /// Use specifically for daily reloads as we use the timing of the last reload txn to figure out if the most /// recent reload has been claimed yet or not /// Reload, /// /// Use this only for hostess juicers as the sum of these juicers in a given day can influence the hostess' behavior /// Hostess, /// /// Specifically use for lossback as we use the delta between last lossback txn to calculate total lost /// to figure out what the next lossback should be. (Basically return a small % of the player's losses /// unless the player's actual position is positive during the period, then tell them to fuck off) /// Lossback } public enum WagerGame { Limbo, Dice, Mines, Planes, [Description("Lambchop")] LambChop, Keno, [Description("Coinflip")] CoinFlip, /// /// This is for betting pools based on some sort of event or outcome /// Event } public enum GamblerState { /// /// Gambler entity is active and user can wager using the profile /// Active, /// /// Gambler entity has been disabled by an administrator (e.g. due to cheating) /// The user will get a new gambler entity when they next interact with the kasino /// AdministrativelyDisabled, /// /// Gambler entity that was abandoned by the user (e.g. to escape an exclusion or crippling debt) /// Abandoned, /// /// Entity was permanently banned. This will prevent future gambler entities being created for this user /// and will effectively lock them out of the game entirely /// PermanentlyBanned }