namespace Supercell.Laser.Logic.Home { using Supercell.Laser.Logic.Avatar; using Supercell.Laser.Logic.Command; using Supercell.Laser.Logic.Data; using Supercell.Laser.Logic.Data.Helper; using Supercell.Laser.Logic.Home.Gatcha; using Supercell.Laser.Logic.Home.Items; using Supercell.Laser.Logic.Home.Structures; using Supercell.Laser.Logic.Listener; using Supercell.Laser.Logic.Message.Home; using Supercell.Laser.Titan.Math; public class HomeMode { public const int UNLOCKABLE_HEROES_COUNT = 38; public readonly LogicGameListener GameListener; public ClientHome Home; public ClientAvatar Avatar; public Action CharacterChanged; public HomeMode(ClientHome home, ClientAvatar avatar, LogicGameListener gameListener) { Home = home; Avatar = avatar; Home.HomeMode = this; Avatar.HomeMode = this; GameListener = gameListener; } public static HomeMode LoadHomeState(LogicGameListener gameListener, ClientHome home, ClientAvatar avatar, EventData[] events) { home.Events = events; HomeMode homeMode = new HomeMode(home, avatar, gameListener); homeMode.Enter(DateTime.UtcNow); return homeMode; } public bool HasHeroUnlocked(int Brawler) { return Avatar.HasHero(Brawler); } public void RemoveDuplicateHeroes() { if (Avatar == null || Avatar.Heroes == null) { return; } var excludedCharacterIds = new HashSet { 39 }; var duplicateGroups = Avatar.Heroes .Where(h => !excludedCharacterIds.Contains(h.CharacterId)) .GroupBy(h => h.CharacterId) .Where(g => g.Count() > 1) .ToList(); if (!duplicateGroups.Any()) { return; } foreach (var group in duplicateGroups) { var heroesToRemove = group.Skip(1).ToList(); foreach (var hero in heroesToRemove) { Avatar.Heroes.Remove(hero); } } CharacterChanged?.Invoke(Avatar.Heroes.Count); } private bool GetRandomBrawlerForGatcha(Random rand, DeliveryUnit unit) { List Brawlers = new() { 4, 5, 6, 10, 11, 12, 13, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 28, 29, 31, 32, 34, 36 }; List UnlockedBrawlers = new List(); foreach (Hero hero in Avatar.Heroes) { UnlockedBrawlers.Add(GlobalId.GetInstanceId(hero.CharacterId)); } List UnlockableBrawlers = Brawlers.Where(x => !UnlockedBrawlers.Contains(x)).ToList(); if (UnlockableBrawlers.Count == 0) return false; int Brawler = GlobalId.CreateGlobalId(16, UnlockableBrawlers[rand.Next(UnlockableBrawlers.Count)]); CharacterData Character = DataTables.Get(DataType.Character).GetDataByGlobalId(Brawler); GatchaDrop drop = new(1) { DataGlobalId = Character.GetGlobalId(), Count = 1 }; unit.AddDrop(drop); return true; } public bool ProcChance(Random r, double chance, string type, double forcedDrops) { double v1 = r.Next(0, 1000) / 10; switch (type) { case "bonus": chance += forcedDrops * 2; break; case "starpower": chance += forcedDrops * 1.25; break; case "character": chance += forcedDrops * 0.5; break; } return v1 < chance; } public double GetMultByRarity(string rarity) { double result = 0; switch (rarity) { case "bonus": result = 0.5; break; case "character": result = 2; break; case "card_notchar": result = 4; break; case "rare": result = 2; break; case "super_rare": result = 3; break; case "epic": result = 5; break; case "super_epic": result = 7; break; case "legendary": result = 10; break; } return result; } public void SimulateGatcha(DeliveryUnit unit) { Avatar.RollsSinceGoodDrop++; Random rand = new Random(); int unlockedBrawlersCount = Avatar.GetUnlockedHeroesCount(); List powerPoints = new List(); if (unit.Type == 10) { int brawlerChance = 10; if (unlockedBrawlersCount < 4) { brawlerChance = 70; } else if (unlockedBrawlersCount < 6) { brawlerChance = 40; } bool isBrawler = rand.Next(0, 100) < brawlerChance; if (isBrawler) { isBrawler = GetRandomBrawlerForGatcha(rand, unit); } if (!isBrawler) { GatchaDrop coins = new GatchaDrop(7); coins.Count = rand.Next(15, 40); unit.AddDrop(coins); for (int i = 0; i < 2; i++) { List unlockedHeroes = Avatar.Heroes; bool heroValid = false; int generateAttempts = 0; int idx = -1; while (!heroValid && generateAttempts < 10) { generateAttempts++; idx = rand.Next(unlockedHeroes.Count); heroValid = unlockedHeroes[idx].PowerPoints < 1410; if (heroValid) { GatchaDrop drop = new GatchaDrop(6); drop.DataGlobalId = unlockedHeroes[idx].CharacterId; if (powerPoints.Contains(drop.DataGlobalId)) { continue; } powerPoints.Add(drop.DataGlobalId); drop.Count = LogicMath.Min(rand.Next(5, 25), (1410 - unlockedHeroes[idx].PowerPoints)); unit.AddDrop(drop); } } } } } else if (unit.Type == 11) { GatchaDrop coins = new GatchaDrop(7); coins.Count = rand.Next(322, 600); unit.AddDrop(coins); for (int i = 0; i < 5; i++) { List unlockedHeroes = Avatar.Heroes; bool heroValid = false; int generateAttempts = 0; int idx = -1; while (!heroValid && generateAttempts < 10) { generateAttempts++; idx = rand.Next(unlockedHeroes.Count); heroValid = unlockedHeroes[idx].PowerPoints < 1410; if (heroValid) { GatchaDrop drop = new GatchaDrop(6); drop.DataGlobalId = unlockedHeroes[idx].CharacterId; if (powerPoints.Contains(drop.DataGlobalId)) { continue; } powerPoints.Add(drop.DataGlobalId); drop.Count = LogicMath.Min(rand.Next(40, 80), (1410 - unlockedHeroes[idx].PowerPoints)); unit.AddDrop(drop); } } } int brawlerChance = 20; if (unlockedBrawlersCount < 4) { brawlerChance = 70; } else if (unlockedBrawlersCount < 6) { brawlerChance = 40; } bool isBrawler = rand.Next(0, 100) < brawlerChance; if (isBrawler) { isBrawler = GetRandomBrawlerForGatcha(rand, unit); } } else if (unit.Type == 12) { GatchaDrop coins = new GatchaDrop(7); coins.Count = rand.Next(60, 250); unit.AddDrop(coins); for (int i = 0; i < 3; i++) { List unlockedHeroes = Avatar.Heroes; bool heroValid = false; int generateAttempts = 0; int idx = -1; while (!heroValid && generateAttempts < 10) { generateAttempts++; idx = rand.Next(unlockedHeroes.Count); heroValid = unlockedHeroes[idx].PowerPoints < 1410; if (heroValid) { GatchaDrop drop = new GatchaDrop(6); drop.DataGlobalId = unlockedHeroes[idx].CharacterId; if (powerPoints.Contains(drop.DataGlobalId)) { continue; } powerPoints.Add(drop.DataGlobalId); drop.Count = LogicMath.Min(rand.Next(10, 50), (1410 - unlockedHeroes[idx].PowerPoints)); unit.AddDrop(drop); } } } int brawlerChance = 20; if (unlockedBrawlersCount < 4) { brawlerChance = 70; } else if (unlockedBrawlersCount < 6) { brawlerChance = 40; } bool isBrawler = rand.Next(0, 100) < brawlerChance; // brawler = 25% chance. if (isBrawler) { isBrawler = GetRandomBrawlerForGatcha(rand, unit); } } bool bonus = rand.Next(0, 100) < 20; if (bonus) { int count = rand.Next(2, 7) + 1; GatchaDrop drop = new GatchaDrop(8); drop.Count = count; unit.AddDrop(drop); } // Token doublers: 15% chance, amount scales with box type if (rand.Next(0, 100) < 15) { int doublerAmount = unit.Type == 11 ? rand.Next(50, 150) : unit.Type == 12 ? rand.Next(20, 60) : rand.Next(5, 20); GatchaDrop doublers = new GatchaDrop(2) { Count = doublerAmount }; unit.AddDrop(doublers); } // Rare skin: 3% for big box, 1% for others int skinChance = unit.Type == 11 ? 3 : 1; if (rand.Next(0, 100) < skinChance) { int[] rareSkins = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; int skinInstanceId = rareSkins[rand.Next(rareSkins.Length)]; int skinGlobalId = GlobalId.CreateGlobalId(29, skinInstanceId); if (!Home.UnlockedSkins.Contains(skinGlobalId)) { GatchaDrop skin = new GatchaDrop(9) { SkinGlobalId = skinGlobalId, Count = 1 }; unit.AddDrop(skin); } } } public void Enter(DateTime dateTime) { Home.HomeVisited(); } public void ClientTurnReceived(int tick, int checksum, List commands) { foreach (Command command in commands) { if (command.Execute(this) != 0) { OutOfSyncMessage outOfSync = new OutOfSyncMessage(); GameListener.SendMessage(outOfSync); } } Home.Tick(); } } }