using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Godot; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; using Oshima.Core.Constant; using Oshima.FunGame.OshimaModules.Effects.OpenEffects; using Oshima.FunGame.OshimaModules.Models; using Oshima.FunGame.OshimaServers.Service; namespace Milimoe.GodotGame; public partial class Fighting : Control { [Export] public TileMapLayer TileMapLayer { get; set; } [Export] public Camera2D CameraFight { get; set; } public Character Character { get; set; } public Node ParentUI { get; set; } public Node NovelUI { get; set; } public RichTextLabel FightLog { get; set; } public Vector2I StartCell { get; set; } public EnemyBody Enemy { get; set; } public GamingQueue GamingQueue => _gamingQueue; public Dictionary CharacterStatistics { get; } = []; public static Dictionary TeamCharacterStatistics { get; } = []; public PluginConfig StatsConfig { get; } = new(nameof(Fighting), nameof(CharacterStatistics)); public PluginConfig TeamStatsConfig { get; } = new(nameof(Fighting), nameof(TeamCharacterStatistics)); public bool IsWeb { get; set; } = false; public bool TeamMode { get; set; } = false; public string Msg { get; set; } = ""; private GamingQueue _gamingQueue = null; private bool _fastMode = false; public override void _Ready() { InitCharacter(); } public async Task> StartGame(bool isWeb = false, bool isTeam = false) { IsWeb = isWeb; TeamMode = isTeam; try { List result = []; Msg = ""; List allCharactersInGame = [.. FunGameConstant.Characters.OrderBy(o => Random.Shared.Next()).Take(3)]; TeamGamingQueue tgq = null; MixGamingQueue mgq = null; int clevel = 60; int slevel = 6; int mlevel = 8; // 升级和赋能 List characters = []; for (int index = 0; index < allCharactersInGame.Count; index++) { Character c = allCharactersInGame[index]; c.Level = clevel; c.NormalAttack.Level = mlevel; FunGameService.AddCharacterSkills(c, 1, slevel, slevel); foreach (Skill skillLoop in FunGameConstant.Skills) { Skill skill = skillLoop.Copy(); skill.Character = c; skill.Level = slevel; c.Skills.Add(skill); } foreach (Skill skillLoop in FunGameConstant.CommonPassiveSkills) { Skill passive = skillLoop.Copy(); passive.Character = c; passive.Level = 1; c.Skills.Add(passive); } c.EP = 100; characters.Add(c); } Character.NormalAttack.Level = mlevel; foreach (Skill skillLoop in FunGameConstant.Skills) { Skill skill = skillLoop.Copy(); skill.Character = Character; skill.Level = slevel; Character.Skills.Add(skill); } foreach (Skill skillLoop in FunGameConstant.CommonPassiveSkills) { Skill passive = skillLoop.Copy(); passive.Character = Character; passive.Level = 1; Character.Skills.Add(passive); } Character.EP = 100; characters.Add(Character); // 询问玩家需要选择哪个角色 (通过UI界面选择) Character player = Character ?? throw new Exception("没有选择角色,游戏结束。"); // 创建顺序表并排序 Team team1 = null, team2 = null; if (isTeam) { tgq = new(WriteLine) { GameplayEquilibriumConstant = OshimaGameModuleConstant.GameplayEquilibriumConstant }; _gamingQueue = tgq; WriteLine("=== 团队模式随机分组 ==="); // 打乱角色列表 List shuffledCharacters = [.. characters.OrderBy(c => Random.Shared.Next())]; // 创建两个团队 List group1 = []; List group2 = []; // 将角色交替分配到两个团队中 for (int cid = 0; cid < shuffledCharacters.Count; cid++) { Character thisCharacter = shuffledCharacters[cid]; if (cid % 2 == 0) { group1.Add(thisCharacter); } else { group2.Add(thisCharacter); } } // 添加到团队字典,第一个为队长 tgq.AddTeam($"{group1.First().NickName}的小队", group1); tgq.AddTeam($"{group2.First().NickName}的小队", group2); foreach (Team team in tgq.Teams.Values) { WriteLine($"团队【{team.Name}】的成员:\r\n{string.Join("\r\n", team.Members)}\r\n"); if (team.IsOnThisTeam(player)) { foreach (Character c in team.Members.Except([player])) { c.Promotion = 300; } team1 = team; } else { team.Members.ForEach(c => c.Promotion = 400); team2 = team; } } // 初始化角色 _gamingQueue.InitCharacters(characters); } else { mgq = new MixGamingQueue(characters, WriteLine) { GameplayEquilibriumConstant = OshimaGameModuleConstant.GameplayEquilibriumConstant }; _gamingQueue = mgq; } // 启用调试模式 //_gamingQueue.IsDebug = true; // 加载地图和绑定事件 _gamingQueue.LoadGameMap(new GodotMap(TileMapLayer)); _gamingQueue.UseQueueProtected = false; if (_gamingQueue.Map != null) { GodotMap map = (GodotMap)_gamingQueue.Map; HashSet allocated = []; List allGrids = [.. map.Grids.Values]; if (isTeam && tgq != null && team1 != null && team2 != null) { // 团队模式放置角色 Grid currentGrid = map.GetGridByVector2I(StartCell); List grids = map.GetGridsByRange(currentGrid, 10); foreach (Character character in characters) { Grid grid = grids.OrderBy(o => Random.Shared.Next()).FirstOrDefault(g => g.Characters.Count == 0); map.SetCharacterCurrentGrid(character, grid); } map.SetCharacterCurrentGrid(Character, currentGrid); } else { // 非团队模式,随机放置角色 foreach (Character character in characters) { character.NormalAttack.GamingQueue = _gamingQueue; Grid grid = Grid.Empty; do { grid = allGrids[Random.Shared.Next(allGrids.Count)]; } while (allocated.Contains(grid)); allocated.Add(grid); map.SetCharacterCurrentGrid(character, grid); } } } // 绑定事件 //await Controller.SetQueue(_gamingQueue.HardnessTime); //await Controller.SetCharacterStatistics(_gamingQueue.CharacterStatistics); //_gamingQueue.TurnStartEvent += GamingQueue_TurnStart; //_gamingQueue.DecideActionEvent += GamingQueue_DecideAction; //_gamingQueue.SelectNormalAttackTargetsEvent += GamingQueue_SelectNormalAttackTargets; //_gamingQueue.SelectSkillEvent += GamingQueue_SelectSkill; //_gamingQueue.SelectSkillTargetsEvent += GamingQueue_SelectSkillTargets; //_gamingQueue.SelectNonDirectionalSkillTargetsEvent += GamingQueue_SelectNonDirectionalSkillTargets; //_gamingQueue.SelectItemEvent += GamingQueue_SelectItem; //_gamingQueue.QueueUpdatedEvent += GamingQueue_QueueUpdated; //_gamingQueue.TurnEndEvent += GamingQueue_TurnEnd; //_gamingQueue.CharacterInquiryEvent += GamingQueue_CharacterInquiryEvent; //_gamingQueue.CharacterActionTakenEvent += GamingQueue_CharacterActionTakenEvent //_gamingQueue.SelectTargetGridEvent += GamingQueue_SelectTargetGrid; //_gamingQueue.CharacterMoveEvent += GamingQueue_CharacterMove; // 总游戏时长 double totalTime = 0; // 开始空投 Msg = ""; int qMagicCardPack = 5; int qWeapon = 5; int qArmor = 5; int qShoes = 5; int qAccessory = 5; DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory); double nextDropItemTime = 40; if (qMagicCardPack < 5) qMagicCardPack++; if (qWeapon < 5) qWeapon++; if (qArmor < 5) qArmor++; if (qShoes < 5) qShoes++; if (qAccessory < 5) qAccessory++; // 初始化队列,准备开始游戏 _gamingQueue.InitActionQueue(); _gamingQueue.SetCharactersToAIControl(false, characters); _gamingQueue.SetCharactersToAIControl(false, player); _gamingQueue.CustomData.Add("player", player); // 显示初始顺序表 _gamingQueue.DisplayQueue(); WriteLine($"你的角色是 [ {player} ],详细信息:{player.GetInfo()}"); // 总回合数 int maxRound = isTeam ? 9999 : 999; // 随机回合奖励 Dictionary effects = []; foreach (EffectID id in FunGameConstant.RoundRewards.Keys) { long effectID = (long)id; bool isActive = false; if (effectID > (long)EffectID.Active_Start) { isActive = true; } effects.Add(effectID, isActive); } _gamingQueue.InitRoundRewards(maxRound, 1, effects, id => FunGameConstant.RoundRewards[(EffectID)id]); int i = 1; while (i < maxRound) { Msg = ""; if (i == maxRound - 1) { if (isTeam) { WriteLine("两队打到天昏地暗,流局了!!"); break; } else { WriteLine($"=== 终局审判 ==="); Dictionary hpPercentage = []; foreach (Character c in characters) { hpPercentage.TryAdd(c, c.HP / c.MaxHP); } double max = hpPercentage.Values.Max(); Character winner = hpPercentage.Keys.Where(c => hpPercentage[c] == max).First(); WriteLine("[ " + winner + " ] 成为了天选之人!!"); foreach (Character c in characters.Where(c => c != winner && c.HP > 0)) { WriteLine("[ " + winner + " ] 对 [ " + c + " ] 造成了 99999999999 点真实伤害。"); _gamingQueue.DeathCalculation(winner, c); } mgq?.EndGameInfo(winner); } result.Add(Msg); break; } // 检查是否有角色可以行动 Character characterToAct = _gamingQueue.NextCharacter(); // 处理回合 if (characterToAct != null) { // 获取该角色当前位置 if (_gamingQueue.Map != null) { Grid currentGrid = _gamingQueue.Map.GetCharacterCurrentGrid(characterToAct); if (currentGrid != null) _gamingQueue.CustomData["currentGrid"] = currentGrid; } bool isGameEnd = false; try { isGameEnd = _gamingQueue.ProcessTurn(characterToAct); } catch (Exception e) { WriteLine(e.ToString()); } if (isGameEnd) { result.Add(Msg); break; } if (isWeb) _gamingQueue.DisplayQueue(); } string roundMsg = ""; if (_gamingQueue.LastRound.HasKill) { roundMsg = Msg; Msg = ""; } // 模拟时间流逝 double timeLapse = _gamingQueue.TimeLapse(); totalTime += timeLapse; nextDropItemTime -= timeLapse; if (roundMsg != "") { if (isWeb) { roundMsg += "\r\n" + Msg; } result.Add(roundMsg); } if (nextDropItemTime <= 0) { // 空投 Msg = ""; DropItems(_gamingQueue, qMagicCardPack, qWeapon, qArmor, qShoes, qAccessory); WriteLine(""); nextDropItemTime = 40; if (qMagicCardPack < 5) qMagicCardPack++; if (qWeapon < 5) qWeapon++; if (qArmor < 5) qArmor++; if (qShoes < 5) qShoes++; if (qAccessory < 5) qAccessory++; } await Task.Delay(1); } WriteLine("--- 游戏结束 ---"); WriteLine($"总游戏时长:{totalTime:0.##} {_gamingQueue.GameplayEquilibriumConstant.InGameTime}"); // 赛后统计 FunGameService.GetCharacterRating(_gamingQueue.CharacterStatistics, isTeam, tgq != null ? tgq.EliminatedTeams : []); if ((isTeam && tgq != null && tgq.EliminatedTeams.Any(t => t.IsOnThisTeam(Character) && t.IsWinner)) || _gamingQueue.Eliminated.Last() == Character) { Enemy.Dead = true; } result.Add(Msg); return result; } catch (Exception ex) { WriteLine(ex.ToString()); return [ex.ToString()]; } } public void WriteLine(string str = "") { Msg += str + "\r\n"; FightLog.AppendText(str.Trim() + "\r\n"); FightLog.ScrollToLine(FightLog.GetLineCount()); } public void InitCharacter() { foreach (Character c in FunGameConstant.Characters) { CharacterStatistics.Add(c, new()); TeamCharacterStatistics.Add(c, new()); } StatsConfig.LoadConfig(); TeamStatsConfig.LoadConfig(); foreach (Character character in CharacterStatistics.Keys) { if (StatsConfig.ContainsKey(character.ToStringWithOutUser())) { CharacterStatistics[character] = StatsConfig.Get(character.ToStringWithOutUser()) ?? CharacterStatistics[character]; } } foreach (Character character in TeamCharacterStatistics.Keys) { if (TeamStatsConfig.ContainsKey(character.ToStringWithOutUser())) { TeamCharacterStatistics[character] = TeamStatsConfig.Get(character.ToStringWithOutUser()) ?? TeamCharacterStatistics[character]; } } } public static void DropItems(GamingQueue queue, int mQuality, int wQuality, int aQuality, int sQuality, int acQuality) { Item[] weapons = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("11") && (int)i.QualityType == wQuality)]; Item[] armors = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("12") && (int)i.QualityType == aQuality)]; Item[] shoes = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("13") && (int)i.QualityType == sQuality)]; Item[] accessorys = [.. FunGameConstant.Equipment.Where(i => i.Id.ToString().StartsWith("14") && (int)i.QualityType == acQuality)]; Item[] consumables = [.. FunGameConstant.AllItems.Where(i => i.ItemType == ItemType.Consumable && i.IsInGameItem)]; foreach (Character character in queue.AllCharacters) { Item a = null, b = null, c = null, d = null; if (weapons.Length > 0) a = weapons[Random.Shared.Next(weapons.Length)]; if (armors.Length > 0) b = armors[Random.Shared.Next(armors.Length)]; if (shoes.Length > 0) c = shoes[Random.Shared.Next(shoes.Length)]; if (accessorys.Length > 0) d = accessorys[Random.Shared.Next(accessorys.Length)]; List drops = []; if (a != null) drops.Add(a); if (b != null) drops.Add(b); if (c != null) drops.Add(c); if (d != null) drops.Add(d); mQuality = 2; Item magicCardPack = FunGameService.GenerateMagicCardPack(8, (QualityType)mQuality); if (magicCardPack != null) { foreach (Skill magic in magicCardPack.Skills.Magics) { magic.Level = 8; } magicCardPack.SetGamingQueue(queue); queue.Equip(character, magicCardPack); } foreach (Item item in drops) { Item realItem = item.Copy(); realItem.SetGamingQueue(queue); queue.Equip(character, realItem); } if (consumables.Length > 0 && character.Items.Count < 5) { for (int i = 0; i < 2; i++) { Item consumable = consumables[Random.Shared.Next(consumables.Length)].Copy(); consumable.Character = character; character.Items.Add(consumable); } } } } }