From 030773b44351febeec135ab38f264e0ec0d03799 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Tue, 31 Oct 2023 11:51:15 -0300 Subject: [PATCH 01/11] WIP Blood Family --- .../Rules/BloodFamilyRuleSystem.cs | 260 ++++++++++++++++++ .../Components/BloodFamilyRuleComponent.cs | 63 +++++ .../Components/FamilyAliveComponent.cs | 12 + .../Systems/KeepFamilyAliveConditionSystem.cs | 42 +++ .../Roles/BloodFamilyRoleComponent.cs | 12 + .../EstacaoPirata/GameRules/roundstart.yml | 6 + .../EstacaoPirata/Objectives/bloodfamily.yml | 19 ++ .../Roles/Antags/bloodfamily.yml | 6 + .../Prototypes/EstacaoPirata/game_presets.yml | 11 + 9 files changed, 431 insertions(+) create mode 100644 Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs create mode 100644 Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs create mode 100644 Content.Server/EstacaoPirata/Roles/BloodFamilyRoleComponent.cs create mode 100644 Resources/Prototypes/EstacaoPirata/GameRules/roundstart.yml create mode 100644 Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml create mode 100644 Resources/Prototypes/EstacaoPirata/Roles/Antags/bloodfamily.yml create mode 100644 Resources/Prototypes/EstacaoPirata/game_presets.yml diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs new file mode 100644 index 00000000000000..e15cc71b1d05fe --- /dev/null +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -0,0 +1,260 @@ +using System.Linq; +using Content.Server.EstacaoPirata.GameTicking.Rules.Components; +using Content.Server.EstacaoPirata.Roles; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.NPC.Systems; +using Content.Server.Objectives; +using Content.Server.Roles; +using Content.Server.Shuttles.Components; +using Content.Shared.CCVar; +using Content.Shared.Objectives.Components; +using Content.Shared.Preferences; +using Content.Shared.Roles; +using Content.Shared.Roles.Jobs; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.Players; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.EstacaoPirata.GameTicking.Rules; + +public sealed class BloodFamilyRuleSystem : GameRuleSystem +{ + + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ObjectivesSystem _objectives = default!; // Usar em MakeBloodFamiliar() // TODO: criar um prototipo de objetivos pra familia + [Dependency] private readonly SharedJobSystem _jobs = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnPlayerJobAssigned); + // Vai ter late join? + } + + protected override void ActiveTick(EntityUid uid, BloodFamilyRuleComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + if (component.SelectionStatus == BloodFamilyRuleComponent.SelectionState.ReadyToSelect && _gameTiming.CurTime > component.AnnounceAt) + DoBloodFamilyStart(component); + } + + /// + /// Vai achar todos os possiveis familiares e chamar o MakeBloodFamiliar + /// + /// + private void DoBloodFamilyStart(BloodFamilyRuleComponent component) + { + Log.Debug("DoBloodFamilyStart chamado"); + if (component.StartCandidates.Count < component.MinBloodFamily) // Setar os start candidates no OnPlayerJobsAssigned vulgo OnPlayerSpawn + { + Log.Error("Tried to start Blood Family mode without enough candidates."); + return; + } + + var numFamily = MathHelper.Clamp(component.StartCandidates.Count / component.PlayersPerFamilyMember, 1, component.MaxBloodFamily); + var familyPool = FindPotentialFamilyMembers(component.StartCandidates, component); + var selectedFamily = PickFamilyMembers(numFamily, familyPool); + + foreach (var player in selectedFamily) + { + MakeBloodFamiliar(player); + } + + // Adicionar game rule de traitor comum tbm + } + + private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) + { + var query = EntityQueryEnumerator(); // Faz a query da entidade que cuida do gamerule (tem so uma entidade eu acho) + // Para testar usar o forcepreset + while (query.MoveNext(out var uid, out var familiar, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + continue; + foreach (var player in ev.Players) + { + if (!ev.Profiles.ContainsKey(player.UserId)) + continue; + + // Aqui pega-se cada jogador que deu ready e esta na partida + + familiar.StartCandidates[player] = ev.Profiles[player.UserId]; + } + + // Vai usar o delay do traitor por enquanto mesmo + var delay = TimeSpan.FromSeconds(0); + // _cfg.GetCVar(CCVars.TraitorStartDelay) + + // _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); + + familiar.AnnounceAt = _gameTiming.CurTime + delay; + + familiar.SelectionStatus = BloodFamilyRuleComponent.SelectionState.ReadyToSelect; + } + } + + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + Log.Debug("OnStartAttempt chamado"); + //throw new NotImplementedException(); + } + + public List FindPotentialFamilyMembers(in Dictionary candidates, BloodFamilyRuleComponent component) + { + var list = new List(); + var pendingQuery = GetEntityQuery(); + + foreach (var player in candidates.Keys) + { + // Role prevents antag. + if (!_jobs.CanBeAntag(player)) + { + continue; + } + + // Latejoin + if (player.AttachedEntity != null && pendingQuery.HasComponent(player.AttachedEntity.Value)) + continue; + + list.Add(player); + } + + var prefList = new List(); + + foreach (var player in list) + { + var profile = candidates[player]; + if (profile.AntagPreferences.Contains(component.BloodFamilyPrototypeId)) + { + prefList.Add(player); + } + } + if (prefList.Count == 0) + { + Log.Info("Insufficient preferred blood family members, picking at random."); + prefList = list; + } + return prefList; + } + + public List PickFamilyMembers(int familyMembersCount, List prefList) + { + var results = new List(familyMembersCount); + if (prefList.Count == 0) + { + Log.Info("Insufficient ready players to fill up with blood family members, stopping the selection."); + return results; + } + + for (var i = 0; i < familyMembersCount; i++) + { + results.Add(_random.PickAndTake(prefList)); + Log.Info("Selected a preferred blood family member."); + } + return results; + } + + public bool MakeBloodFamiliar(ICommonSession player) + { + var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); + if (bloodFamilyRuleEntity == null) + { + //todo fuck me this shit is awful + //no i wont fuck you, erp is against rules + GameTicker.StartGameRule("BloodFamily", out var ruleEntity); + bloodFamilyRuleEntity = Comp(ruleEntity); + } + + if (!_mindSystem.TryGetMind(player, out var mindId, out var mind)) + { + Log.Info("Failed getting mind for picked blood family member."); + return false; + } + + if (HasComp(mindId)) + { + Log.Error($"Player {player.Name} is already a traitor."); + return false; + } + + if (HasComp(mindId)) + { + Log.Error($"Player {player.Name} is already a blood family member."); + return false; + } + + if (mind.OwnedEntity is not { } entity) + { + Log.Error("Mind picked for blood family member did not have an attached entity."); + return false; + } + + // Ainda a decidir se vao vir com um uplink sem TC, mas aqui ficaria o codigo de dar o uplink e dar o codigo para abrir o uplink + + // Prepare blood family role + // var bloodFamilyRole = new BloodFamilyRoleComponent + // { + // PrototypeId = bloodFamilyRuleEntity.BloodFamilyPrototypeId, + // }; + + // Assign blood family roles + _roleSystem.MindAddRole(mindId, new BloodFamilyRoleComponent + { + PrototypeId = bloodFamilyRuleEntity.BloodFamilyPrototypeId + }); + + bloodFamilyRuleEntity.BloodFamilyMinds.Add(mindId); + + if (_mindSystem.TryGetSession(mindId, out var session)) + { + // Notificate player about new role assignment + _audioSystem.PlayGlobal(bloodFamilyRuleEntity.GreetSoundNotification, session); + } + + // Change the faction + _npcFaction.RemoveFaction(entity, "NanoTrasen", false); + _npcFaction.AddFaction(entity, "Syndicate"); + + // Give random objectives + var maxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty); + var maxPicks = bloodFamilyRuleEntity.MaxRandomObjectives; + var difficulty = 0f; + for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) + { + var objective = _objectives.GetRandomObjective(mindId, mind, "TraitorObjectiveGroups"); + if (objective == null) + continue; + + _mindSystem.AddObjective(mindId, mind, objective.Value); + difficulty += Comp(objective.Value).Difficulty; + } + + // Give keep family alive objective + + + return true; + } + + /// + /// Envia o texto de greeting e quem sao os outros blood family members + /// + /// + private void SendBloodFamilyBriefing(EntityUid mind) + { + + } +} diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs new file mode 100644 index 00000000000000..e1a9a08dc84748 --- /dev/null +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs @@ -0,0 +1,63 @@ +using Content.Shared.Preferences; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.EstacaoPirata.GameTicking.Rules.Components; + +/// +/// This is used for... +/// +[RegisterComponent, Access(typeof(BloodFamilyRuleSystem))] +public sealed partial class BloodFamilyRuleComponent : Component +{ + public readonly List BloodFamilyMinds = new(); + + [DataField("bloodFamilyPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string BloodFamilyPrototypeId = "BloodFamily"; + + public int TotalBloodFamilyMembers => BloodFamilyMinds.Count; // Alterar isto + //public string[] Codewords = new string[3]; + + /// + /// Max Blood Family members allowed during selection. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxBloodFamily = 3; + + /// + /// awawwa + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int PlayersPerFamilyMember = 8; + + /// + /// Min Blood Family members needed during selection. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MinBloodFamily = 2; + + /// + ///aa + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxRandomObjectives = 2; + + public enum SelectionState + { + WaitingForSpawn = 0, + ReadyToSelect = 1, + SelectionMade = 2, + } + + public SelectionState SelectionStatus = SelectionState.WaitingForSpawn; + public TimeSpan AnnounceAt = TimeSpan.Zero; + public Dictionary StartCandidates = new(); + + /// + /// Path to antagonist alert sound. + /// + [DataField("greetSoundNotification")] + public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg"); +} diff --git a/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs new file mode 100644 index 00000000000000..6d8cd00f14698b --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs @@ -0,0 +1,12 @@ +using Content.Server.EstacaoPirata.Objectives.Systems; + +namespace Content.Server.EstacaoPirata.Objectives.Components; + +/// +/// This is used for... +/// +[RegisterComponent, Access(typeof(KeepFamilyAliveConditionSystem))] +public sealed partial class FamilyAliveComponent : Component +{ + +} diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs new file mode 100644 index 00000000000000..167f7422d56a49 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Content.Server.EstacaoPirata.GameTicking.Rules.Components; +using Content.Server.EstacaoPirata.Objectives.Components; +using Content.Server.GameTicking.Rules; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; + +namespace Content.Server.EstacaoPirata.Objectives.Systems; + +/// +/// Handles keep alive condition logic for blood family +/// +public sealed class KeepFamilyAliveConditionSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly TargetObjectiveSystem _target = default!; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGetProgress); + + SubscribeLocalEvent(OnAssigned); + } + + private void OnAssigned(EntityUid uid, FamilyAliveComponent component, ref ObjectiveAssignedEvent args) + { + var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); + + + + throw new NotImplementedException(); + } + + private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent component, ref ObjectiveGetProgressEvent args) + { + throw new NotImplementedException(); + } +} diff --git a/Content.Server/EstacaoPirata/Roles/BloodFamilyRoleComponent.cs b/Content.Server/EstacaoPirata/Roles/BloodFamilyRoleComponent.cs new file mode 100644 index 00000000000000..29dfbf14f48906 --- /dev/null +++ b/Content.Server/EstacaoPirata/Roles/BloodFamilyRoleComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Roles; + +namespace Content.Server.EstacaoPirata.Roles; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class BloodFamilyRoleComponent : AntagonistRoleComponent +{ + +} diff --git a/Resources/Prototypes/EstacaoPirata/GameRules/roundstart.yml b/Resources/Prototypes/EstacaoPirata/GameRules/roundstart.yml new file mode 100644 index 00000000000000..f5c6ad31e22e14 --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/GameRules/roundstart.yml @@ -0,0 +1,6 @@ +- type: entity + id: BloodFamily + parent: BaseGameRule + noSpawn: true + components: + - type: BloodFamilyRule diff --git a/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml b/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml new file mode 100644 index 00000000000000..7ecdfb3a2cf66b --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml @@ -0,0 +1,19 @@ +# requires that the target survives the round +- type: entity + abstract: true + parent: BaseSocialObjective + id: BaseKeepFamilyAliveObjective + components: + - type: KeepFamilyAliveCondition + +- type: entity + noSpawn: true + parent: [BaseTraitorSocialObjective, BaseKeepFamilyAliveObjective] + id: FamilyAliveObjective + description: Identify yourself at your own risk. We just need them alive. + components: + - type: Objective + difficulty: 1.75 + - type: TargetObjective + title: objective-condition-other-traitor-alive-title + - type: FamilyAlive diff --git a/Resources/Prototypes/EstacaoPirata/Roles/Antags/bloodfamily.yml b/Resources/Prototypes/EstacaoPirata/Roles/Antags/bloodfamily.yml new file mode 100644 index 00000000000000..37b841358995d7 --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/Roles/Antags/bloodfamily.yml @@ -0,0 +1,6 @@ +- type: antag + id: BloodFamily + name: roles-antag-blood-family-name + antagonist: true + setPreference: true + objective: roles-antag-blood-family-objective diff --git a/Resources/Prototypes/EstacaoPirata/game_presets.yml b/Resources/Prototypes/EstacaoPirata/game_presets.yml new file mode 100644 index 00000000000000..9f3b62d16ecddd --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/game_presets.yml @@ -0,0 +1,11 @@ +- type: gamePreset + id: BloodFamily + alias: + - bloodfamily + - bloodbrothers + name: blood-family-title + description: blood-family-description + showInVote: false + rules: + - BloodFamily + - BasicStationEventScheduler From 08268b9687c6e78aeadbb2312d1ec8fa84f6065b Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Thu, 2 Nov 2023 21:16:42 -0300 Subject: [PATCH 02/11] Adiciona Blood Family funcionando --- .../Rules/BloodFamilyRuleSystem.cs | 191 ++++++++++++++++-- .../Components/BloodFamilyRuleComponent.cs | 13 +- .../Components/FamilyAliveComponent.cs | 2 +- .../GroupTargetObjectiveComponent.cs | 23 +++ .../KeepGroupAliveConditionComponent.cs | 12 ++ .../PickRandomHeadFamilyComponent.cs | 12 ++ .../PickRandomPersonFamilyComponent.cs | 12 ++ .../Systems/GroupTargetObjectiveSystem.cs | 80 ++++++++ .../Systems/KeepFamilyAliveConditionSystem.cs | 42 ---- .../Systems/KeepGroupAliveConditionSystem.cs | 79 ++++++++ .../GameTicking/Rules/TraitorRuleSystem.cs | 7 + .../Systems/KillPersonConditionSystem.cs | 115 ++++++++++- Resources/Locale/en-US/emp/emp.ftl | 2 +- .../game-presets/preset-blood-family.ftl | 15 ++ .../conditions/other-traitor-alive.ftl | 1 + .../prototypes/roles/antags.ftl | 2 + .../Objectives/base_objectives.yml | 19 ++ .../EstacaoPirata/Objectives/bloodfamily.yml | 47 ++++- .../Objectives/objectiveGroups.yml | 14 ++ .../Prototypes/EstacaoPirata/game_presets.yml | 1 + Resources/Prototypes/Objectives/traitor.yml | 1 + 21 files changed, 612 insertions(+), 78 deletions(-) create mode 100644 Content.Server/EstacaoPirata/Objectives/Components/GroupTargetObjectiveComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Components/KeepGroupAliveConditionComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Components/PickRandomHeadFamilyComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Components/PickRandomPersonFamilyComponent.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Systems/GroupTargetObjectiveSystem.cs delete mode 100644 Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs create mode 100644 Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs create mode 100644 Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl create mode 100644 Resources/Locale/pt-BR/estacao-pirata/objectives/conditions/other-traitor-alive.ftl create mode 100644 Resources/Locale/pt-BR/estacao-pirata/prototypes/roles/antags.ftl create mode 100644 Resources/Prototypes/EstacaoPirata/Objectives/base_objectives.yml create mode 100644 Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs index e15cc71b1d05fe..ac1e9016ddf0d5 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -1,4 +1,7 @@ using System.Linq; +using System.Text; +using Content.Server.Antag; +using Content.Server.Chat.Managers; using Content.Server.EstacaoPirata.GameTicking.Rules.Components; using Content.Server.EstacaoPirata.Roles; using Content.Server.GameTicking; @@ -7,10 +10,14 @@ using Content.Server.Mind; using Content.Server.NPC.Systems; using Content.Server.Objectives; +using Content.Server.Radio.Components; using Content.Server.Roles; using Content.Server.Shuttles.Components; using Content.Shared.CCVar; +using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; +using Content.Shared.Objectives.Systems; using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; @@ -28,12 +35,17 @@ public sealed class BloodFamilyRuleSystem : GameRuleSystem(OnStartAttempt); SubscribeLocalEvent(OnPlayerJobAssigned); + SubscribeLocalEvent(OnObjectivesTextGetInfo); // Vai ter late join? } - protected override void ActiveTick(EntityUid uid, BloodFamilyRuleComponent component, GameRuleComponent gameRule, float frameTime) { base.ActiveTick(uid, component, gameRule, frameTime); @@ -68,6 +80,12 @@ private void DoBloodFamilyStart(BloodFamilyRuleComponent component) var numFamily = MathHelper.Clamp(component.StartCandidates.Count / component.PlayersPerFamilyMember, 1, component.MaxBloodFamily); var familyPool = FindPotentialFamilyMembers(component.StartCandidates, component); + + // Se a quantidade de pessoas que colocaram como preferencia Blood Family na criacao de personagem for menor que a quantidade esperada de membros da familia + // seta a quantidade de membro esperados para a mesma de jogadores que escolheram ser membros da familia. + if (numFamily > familyPool.Count) + numFamily = familyPool.Count; + var selectedFamily = PickFamilyMembers(numFamily, familyPool); foreach (var player in selectedFamily) @@ -75,7 +93,24 @@ private void DoBloodFamilyStart(BloodFamilyRuleComponent component) MakeBloodFamiliar(player); } - // Adicionar game rule de traitor comum tbm + component.SelectionStatus = BloodFamilyRuleComponent.SelectionState.SelectionMade; + + // Adiciona objetivos dos membros da familia, isto é feito aqui porque é garantido que todos os membros já foram selecionados e garante + // sincronismo com os objetivos de cada jogador + var allFamily = GetAllBloodFamilyMembersAliveAndConnected(component); + + foreach (var member in allFamily) + { + GiveObjectives(member.Id, member.Mind, component); + + // TODO: implementar implante de radio DE VERDADE + if (member.Mind.OwnedEntity != null) + { + GiveImplants(member.Mind.OwnedEntity.Value); + } + + SendBloodFamilyBriefing(member.Id, member.Mind); + } } private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) @@ -97,7 +132,8 @@ private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) } // Vai usar o delay do traitor por enquanto mesmo - var delay = TimeSpan.FromSeconds(0); + var delay = TimeSpan.FromSeconds(10); + // var delay = TimeSpan.FromSeconds( // TimeSpan.FromSeconds(20); // _cfg.GetCVar(CCVars.TraitorStartDelay) + // _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); @@ -109,8 +145,27 @@ private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) private void OnStartAttempt(RoundStartAttemptEvent ev) { - Log.Debug("OnStartAttempt chamado"); - //throw new NotImplementedException(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var famComp, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + continue; + + var minPlayers = famComp.MinPlayers; + if (!ev.Forced && ev.Players.Length < minPlayers) + { + _chatManager.SendAdminAnnouncement(Loc.GetString("blood-family-not-enough-ready-players", + ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers))); + ev.Cancel(); + continue; + } + + if (ev.Players.Length == 0) + { + _chatManager.DispatchServerAnnouncement(Loc.GetString("blood-family-no-one-ready")); + ev.Cancel(); + } + } } public List FindPotentialFamilyMembers(in Dictionary candidates, BloodFamilyRuleComponent component) @@ -205,12 +260,6 @@ public bool MakeBloodFamiliar(ICommonSession player) // Ainda a decidir se vao vir com um uplink sem TC, mas aqui ficaria o codigo de dar o uplink e dar o codigo para abrir o uplink - // Prepare blood family role - // var bloodFamilyRole = new BloodFamilyRoleComponent - // { - // PrototypeId = bloodFamilyRuleEntity.BloodFamilyPrototypeId, - // }; - // Assign blood family roles _roleSystem.MindAddRole(mindId, new BloodFamilyRoleComponent { @@ -222,6 +271,7 @@ public bool MakeBloodFamiliar(ICommonSession player) if (_mindSystem.TryGetSession(mindId, out var session)) { // Notificate player about new role assignment + Log.Error("AUDIO TOCANDOOO"); _audioSystem.PlayGlobal(bloodFamilyRuleEntity.GreetSoundNotification, session); } @@ -229,13 +279,17 @@ public bool MakeBloodFamiliar(ICommonSession player) _npcFaction.RemoveFaction(entity, "NanoTrasen", false); _npcFaction.AddFaction(entity, "Syndicate"); - // Give random objectives + return true; + } + + private void GiveObjectives(EntityUid mindId, MindComponent mind, BloodFamilyRuleComponent ruleComponent) + { var maxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty); - var maxPicks = bloodFamilyRuleEntity.MaxRandomObjectives; + var maxPicks = ruleComponent.MaxRandomObjectives; var difficulty = 0f; for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) { - var objective = _objectives.GetRandomObjective(mindId, mind, "TraitorObjectiveGroups"); + var objective = _objectives.GetRandomObjective(mindId, mind, "BloodFamilyObjectiveGroups"); if (objective == null) continue; @@ -243,18 +297,117 @@ public bool MakeBloodFamiliar(ICommonSession player) difficulty += Comp(objective.Value).Difficulty; } - // Give keep family alive objective + var familyObjective = _objectivesSystem.TryCreateObjective(mindId, mind, "FamilyAliveObjective"); + + if (familyObjective != null) + { + _mindSystem.AddObjective(mindId, mind, familyObjective.Value); + } + } + private void GiveImplants(EntityUid uid) + { + // Hack ferrado + // var intrinsicRadioTransmitter = AddComp(uid); + // var activeRadio = AddComp(uid); + // var intrinsicRadioRec = AddComp(uid); + // activeRadio.Channels.Add("Syndicate"); + // intrinsicRadioTransmitter.Channels.Add("Syndicate"); + + // var proto = Spawn("SyndieRadioImplant"); + // if (!TryComp(proto, out var comp)) + // { + // return; + // } + // _subdermalImplantSystem.ForceImplant(uid, proto, comp); - return true; + _antagSelection.GiveAntagBagGear(uid, "EncryptionKeySyndie"); + } + + public List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnected(MindComponent ourMind) + { + List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); + foreach (var traitor in EntityQuery()) + { + foreach (var role in GetOtherBloodFamilyMindsAliveAndConnected(ourMind, traitor)) + { + if (!allTraitors.Contains(role)) + allTraitors.Add(role); + } + } + + return allTraitors; + } + + private List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnected(MindComponent ourMind, BloodFamilyRuleComponent component) + { + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + foreach (var traitor in component.BloodFamilyMinds) + { + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + mind != ourMind && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } + } + + return traitors; + } + + private List<(EntityUid Id, MindComponent Mind)> GetAllBloodFamilyMembersAliveAndConnected(BloodFamilyRuleComponent component) + { + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + foreach (var traitor in component.BloodFamilyMinds) + { + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } + } + + return traitors; + } + + private void OnObjectivesTextGetInfo(EntityUid uid, BloodFamilyRuleComponent component, ref ObjectivesTextGetInfoEvent args) + { + args.Minds = component.BloodFamilyMinds; + args.AgentName = Loc.GetString("blood-family-round-end-agent-name"); } /// - /// Envia o texto de greeting e quem sao os outros blood family members + /// Envia o texto de greeting e quem são os outros blood family members /// /// - private void SendBloodFamilyBriefing(EntityUid mind) + /// + private void SendBloodFamilyBriefing(EntityUid mind, MindComponent ourMind) { + if (!_mindSystem.TryGetSession(mind, out var session)) + return; + + var family = GetOtherBloodFamilyMindsAliveAndConnected(ourMind); + + var targetsNames = new StringBuilder(""); + int index = 0; + foreach (var player in family) + { + var targetName = player.Mind.CharacterName; + + if (index > 0 && index < family.Count) + targetsNames.Append("e "); + targetsNames.Append(targetName); + targetsNames.Append(' '); + + index++; + } + _chatManager.DispatchServerMessage(session, Loc.GetString("blood-family-role-greeting",("familyNames",targetsNames))); } } diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs index e1a9a08dc84748..25e825e214a9b0 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs @@ -18,7 +18,6 @@ public sealed partial class BloodFamilyRuleComponent : Component public string BloodFamilyPrototypeId = "BloodFamily"; public int TotalBloodFamilyMembers => BloodFamilyMinds.Count; // Alterar isto - //public string[] Codewords = new string[3]; /// /// Max Blood Family members allowed during selection. @@ -27,10 +26,10 @@ public sealed partial class BloodFamilyRuleComponent : Component public int MaxBloodFamily = 3; /// - /// awawwa + /// For every X players, 1 will be a family member /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public int PlayersPerFamilyMember = 8; + public int PlayersPerFamilyMember = 1; /// /// Min Blood Family members needed during selection. @@ -39,11 +38,17 @@ public sealed partial class BloodFamilyRuleComponent : Component public int MinBloodFamily = 2; /// - ///aa + /// Maximum amount of random objectives a blood family member will have /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MaxRandomObjectives = 2; + /// + /// Minimum players in game for the game rule to be selected + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MinPlayers = 1; + public enum SelectionState { WaitingForSpawn = 0, diff --git a/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs index 6d8cd00f14698b..e4bec38e18b738 100644 --- a/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs +++ b/Content.Server/EstacaoPirata/Objectives/Components/FamilyAliveComponent.cs @@ -5,7 +5,7 @@ namespace Content.Server.EstacaoPirata.Objectives.Components; /// /// This is used for... /// -[RegisterComponent, Access(typeof(KeepFamilyAliveConditionSystem))] +[RegisterComponent, Access(typeof(KeepGroupAliveConditionSystem))] public sealed partial class FamilyAliveComponent : Component { diff --git a/Content.Server/EstacaoPirata/Objectives/Components/GroupTargetObjectiveComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/GroupTargetObjectiveComponent.cs new file mode 100644 index 00000000000000..6541d279c5ceb2 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Components/GroupTargetObjectiveComponent.cs @@ -0,0 +1,23 @@ +using Content.Server.EstacaoPirata.Objectives.Systems; + +namespace Content.Server.EstacaoPirata.Objectives.Components; + +/// +/// This is used for... +/// +[RegisterComponent, Access(typeof(GroupTargetObjectiveSystem))] +public sealed partial class GroupTargetObjectiveComponent : Component +{ + /// + /// Locale id for the objective title. + /// It is passed "targetName" and "job" arguments. + /// + [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] + public string Title = string.Empty; + + /// + /// List of targets' uids + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public List Targets = new List(); +} diff --git a/Content.Server/EstacaoPirata/Objectives/Components/KeepGroupAliveConditionComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/KeepGroupAliveConditionComponent.cs new file mode 100644 index 00000000000000..e9f2926400e6dd --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Components/KeepGroupAliveConditionComponent.cs @@ -0,0 +1,12 @@ +using Content.Server.EstacaoPirata.Objectives.Systems; + +namespace Content.Server.EstacaoPirata.Objectives.Components; + +/// +/// This is used for... +/// +[RegisterComponent, Access(typeof(KeepGroupAliveConditionSystem))] +public sealed partial class KeepGroupAliveConditionComponent : Component +{ + +} diff --git a/Content.Server/EstacaoPirata/Objectives/Components/PickRandomHeadFamilyComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/PickRandomHeadFamilyComponent.cs new file mode 100644 index 00000000000000..0b3a3173f34a38 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Components/PickRandomHeadFamilyComponent.cs @@ -0,0 +1,12 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.EstacaoPirata.Objectives.Components; + +/// +/// This is used for picking a random head exluding members of the blood family to be killed for an objective +/// +[RegisterComponent, Access(typeof(KillPersonConditionSystem))] +public sealed partial class PickRandomHeadFamilyComponent : Component +{ + +} diff --git a/Content.Server/EstacaoPirata/Objectives/Components/PickRandomPersonFamilyComponent.cs b/Content.Server/EstacaoPirata/Objectives/Components/PickRandomPersonFamilyComponent.cs new file mode 100644 index 00000000000000..8dc399bc18c325 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Components/PickRandomPersonFamilyComponent.cs @@ -0,0 +1,12 @@ +using Content.Server.Objectives.Systems; + +namespace Content.Server.EstacaoPirata.Objectives.Components; + +/// +/// This is used for picking a random person exluding members of the blood family to be killed for an objective +/// +[RegisterComponent, Access(typeof(KillPersonConditionSystem))] +public sealed partial class PickRandomPersonFamilyComponent : Component +{ + +} diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/GroupTargetObjectiveSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/GroupTargetObjectiveSystem.cs new file mode 100644 index 00000000000000..59f40f01fb13f1 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Systems/GroupTargetObjectiveSystem.cs @@ -0,0 +1,80 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using Content.Server.EstacaoPirata.Objectives.Components; +using Content.Server.Objectives.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Shared.Roles.Jobs; + +namespace Content.Server.EstacaoPirata.Objectives.Systems; + +/// +/// This handles... +/// +public sealed class GroupTargetObjectiveSystem : EntitySystem +{ + [Dependency] private readonly MetaDataSystem _metaData = default!; + + /// + public override void Initialize() + { + + SubscribeLocalEvent(OnAfterAssign); + } + + private void OnAfterAssign(EntityUid uid, GroupTargetObjectiveComponent component, ref ObjectiveAfterAssignEvent args) + { + if (!GetTargets(uid, out var targets, component)) + return; + + _metaData.SetEntityName(uid, GetTitle(targets, component.Title), args.Meta); + } + + public void SetTargets(EntityUid uid, List targets, GroupTargetObjectiveComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + foreach (var target in targets) + { + comp.Targets.Add(target); + } + + } + + /// + /// Gets the targets from the component. + /// + /// + /// If it is null then the prototype is invalid, just return. + /// + public bool GetTargets(EntityUid uid, [NotNullWhen(true)] out List? targets, GroupTargetObjectiveComponent? comp = null) + { + targets = Resolve(uid, ref comp) ? comp.Targets : null; + return targets != null; + } + + private string GetTitle(List targets, string title) + { + var targetsNames = new StringBuilder(""); + int index = 0; + foreach (var target in targets) + { + var targetName = "Unknown"; + if (TryComp(target, out var mind) && mind.CharacterName != null) + { + targetName = mind.CharacterName; + } + + if(index > 0 && index < targets.Count) + targetsNames.Append("e "); + + targetsNames.Append(targetName); + targetsNames.Append(' '); + + index++; + } + return Loc.GetString(title, ("targetsNames", targetsNames)); + } +} diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs deleted file mode 100644 index 167f7422d56a49..00000000000000 --- a/Content.Server/EstacaoPirata/Objectives/Systems/KeepFamilyAliveConditionSystem.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Linq; -using Content.Server.EstacaoPirata.GameTicking.Rules.Components; -using Content.Server.EstacaoPirata.Objectives.Components; -using Content.Server.GameTicking.Rules; -using Content.Server.Objectives.Components; -using Content.Server.Objectives.Systems; -using Content.Shared.Mind; -using Content.Shared.Objectives.Components; - -namespace Content.Server.EstacaoPirata.Objectives.Systems; - -/// -/// Handles keep alive condition logic for blood family -/// -public sealed class KeepFamilyAliveConditionSystem : EntitySystem -{ - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnAssigned); - } - - private void OnAssigned(EntityUid uid, FamilyAliveComponent component, ref ObjectiveAssignedEvent args) - { - var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); - - - - throw new NotImplementedException(); - } - - private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent component, ref ObjectiveGetProgressEvent args) - { - throw new NotImplementedException(); - } -} diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs new file mode 100644 index 00000000000000..88d3ef3f179237 --- /dev/null +++ b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs @@ -0,0 +1,79 @@ +using System.Linq; +using Content.Server.EstacaoPirata.GameTicking.Rules; +using Content.Server.EstacaoPirata.GameTicking.Rules.Components; +using Content.Server.EstacaoPirata.Objectives.Components; +using Content.Server.GameTicking.Rules; +using Content.Server.Objectives.Components; +using Content.Server.Objectives.Systems; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; + +namespace Content.Server.EstacaoPirata.Objectives.Systems; + +/// +/// Handles keep alive condition logic for blood family +/// +public sealed class KeepGroupAliveConditionSystem : EntitySystem +{ + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly GroupTargetObjectiveSystem _groupTarget = default!; + [Dependency] private readonly BloodFamilyRuleSystem _bloodFamilyRule = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnGetProgress); + + SubscribeLocalEvent(OnAssigned); + } + + private void OnAssigned(EntityUid uid, FamilyAliveComponent component, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(uid, out var comp)) + { + args.Cancelled = true; + return; + } + + var family = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_bloodFamilyRule.GetOtherBloodFamilyMindsAliveAndConnected(args.Mind)); + + // You are the first/only family member. + if (family.Count == 0) + { + Log.Warning("VOCE E O PRIMEIRO FAMILY MEMBER"); + args.Cancelled = true; + return; + } + + var targets = new List(); + foreach (var player in family) + { + targets.Add(player.Id); + } + + _groupTarget.SetTargets(uid, targets, comp); + } + + private void OnGetProgress(EntityUid uid, KeepGroupAliveConditionComponent component, ref ObjectiveGetProgressEvent args) + { + if (!_groupTarget.GetTargets(uid, out var targets)) + return; + + args.Progress = GetProgress(targets); + } + + private float GetProgress(List targets) + { + foreach (var target in targets) + { + if (!TryComp(target, out var mind)) + return 0f; + + if (_mind.IsCharacterDeadIc(mind)) + return 0f; + } + + return 1f; + } +} diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 01317dbfc11b24..ed40804af27d46 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Server.Chat.Managers; +using Content.Server.EstacaoPirata.Roles; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; using Content.Server.NPC.Systems; @@ -230,6 +231,12 @@ public bool MakeTraitor(ICommonSession traitor, bool giveUplink = true, bool giv return false; } + if (HasComp(mindId)) + { + Log.Error($"Player {traitor.Name} is already a blood family member."); + return false; + } + if (mind.OwnedEntity is not { } entity) { Log.Error("Mind picked for traitor did not have an attached entity."); diff --git a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs index c1caa819e449ed..74be13e75a0dd6 100644 --- a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs +++ b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs @@ -1,3 +1,6 @@ +using System.Linq; +using Content.Server.EstacaoPirata.GameTicking.Rules.Components; +using Content.Server.EstacaoPirata.Objectives.Components; using Content.Server.Objectives.Components; using Content.Server.Shuttles.Systems; using Content.Shared.CCVar; @@ -30,8 +33,11 @@ public override void Initialize() SubscribeLocalEvent(OnPersonAssigned); SubscribeLocalEvent(OnHeadAssigned); - } + SubscribeLocalEvent(OnPersonFamilyAssigned); + + SubscribeLocalEvent(OnHeadFamilyAssigned); + } private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref ObjectiveGetProgressEvent args) { if (!_target.GetTarget(uid, out var target)) @@ -64,6 +70,53 @@ private void OnPersonAssigned(EntityUid uid, PickRandomPersonComponent comp, ref _target.SetTarget(uid, _random.Pick(allHumans), target); } + private void OnPersonFamilyAssigned(EntityUid uid, PickRandomPersonFamilyComponent component, ref ObjectiveAssignedEvent args) + { + // invalid objective prototype + if (!TryComp(uid, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + + var allHumans = _mind.GetAliveHumansExcept(args.MindId); + + // no other humans to kill + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + var family = EntityQuery().FirstOrDefault(); + + if (family == null) + { + args.Cancelled = true; + return; + } + + Log.Warning($"Quantidade de familiares: {family.BloodFamilyMinds.Count}"); + foreach (var player in family.BloodFamilyMinds) + { + Log.Warning($"Familiar: {player}"); + allHumans.Remove(player); + } + + if (allHumans.Count <= 0) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(uid, _random.Pick(allHumans), target); + } + private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref ObjectiveAssignedEvent args) { // invalid prototype @@ -99,6 +152,66 @@ private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref Obj _target.SetTarget(uid, _random.Pick(allHeads), target); } + private void OnHeadFamilyAssigned(EntityUid uid, PickRandomHeadFamilyComponent component, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(uid, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + + var allHumans = _mind.GetAliveHumansExcept(args.MindId); + + // no other humans to kill + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + var family = EntityQuery().FirstOrDefault(); + + if (family == null) + { + args.Cancelled = true; + return; + } + + Log.Warning($"Quantidade de familiares: {family.BloodFamilyMinds.Count}"); + foreach (var player in family.BloodFamilyMinds) + { + Log.Warning($"Familiar: {player}"); + allHumans.Remove(player); + } + + var allHeads = new List(); + foreach (var mind in allHumans) + { + // RequireAdminNotify used as a cheap way to check for command department + if (_job.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify) + allHeads.Add(mind); + } + + if (allHeads.Count == 0) + { + allHeads = allHumans; // fallback to non-head target + if (allHeads.Count == 0) + { + args.Cancelled = true; + return; + } + } + + + _target.SetTarget(uid, _random.Pick(allHeads), target); + } + private float GetProgress(EntityUid target, bool requireDead) { // deleted or gibbed or something, counts as dead diff --git a/Resources/Locale/en-US/emp/emp.ftl b/Resources/Locale/en-US/emp/emp.ftl index 6d5e790befbf92..35b40432863051 100644 --- a/Resources/Locale/en-US/emp/emp.ftl +++ b/Resources/Locale/en-US/emp/emp.ftl @@ -1 +1 @@ -emp-disabled-comp-on-examine = [color=lightblue]It's disrupted by an electric field... [/color] +emp-disabled-comp-on-examine = cIt's disrupted by an electric field... [/color] diff --git a/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl b/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl new file mode 100644 index 00000000000000..7cc3ac16584a59 --- /dev/null +++ b/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl @@ -0,0 +1,15 @@ +blood-family-role-greeting = + Você faz parte da família de sangue. + Seus familiares são: {$familyNames} + Você recebeu um cartão de linha de rádio do sindicato para poder se comunicar com sua família. + Seus objetivos estão listados no menu do personagem. + Morte a Nanotrasen! + + +blood-family-round-end-agent-name = membro da Família + +blood-family-not-enough-ready-players = Faltou mais jogadores prontos para a partida! Haviam {$readyPlayersCount} jogadores prontos de {$minimumPlayers} necessários. Impossível iniciar modo família de sangue. +blood-family-no-one-ready = Nenhum jogador deu "pronto"! Impossível iniciar modo família de sangue. + +blood-family-title = Família de Sangue +blood-family-description = Uma família de traidores que fará de tudo para cumprir seus objetivos. diff --git a/Resources/Locale/pt-BR/estacao-pirata/objectives/conditions/other-traitor-alive.ftl b/Resources/Locale/pt-BR/estacao-pirata/objectives/conditions/other-traitor-alive.ftl new file mode 100644 index 00000000000000..dc8174c0959d25 --- /dev/null +++ b/Resources/Locale/pt-BR/estacao-pirata/objectives/conditions/other-traitor-alive.ftl @@ -0,0 +1 @@ +objective-condition-family-alive-title = Garanta que {$targetsNames}permaneça(m) vivo(s). diff --git a/Resources/Locale/pt-BR/estacao-pirata/prototypes/roles/antags.ftl b/Resources/Locale/pt-BR/estacao-pirata/prototypes/roles/antags.ftl new file mode 100644 index 00000000000000..8681ce436030cf --- /dev/null +++ b/Resources/Locale/pt-BR/estacao-pirata/prototypes/roles/antags.ftl @@ -0,0 +1,2 @@ +roles-antag-blood-family-name = Membro da Família de Sangue +roles-antag-blood-family-objective = Complete seus objetivos e mantenha sua família viva. diff --git a/Resources/Prototypes/EstacaoPirata/Objectives/base_objectives.yml b/Resources/Prototypes/EstacaoPirata/Objectives/base_objectives.yml new file mode 100644 index 00000000000000..81b9bbc7335c74 --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/Objectives/base_objectives.yml @@ -0,0 +1,19 @@ +# Base Objectives +- type: entity + abstract: true + parent: BaseObjective + id: BaseBloodFamilyObjective + components: + - type: Objective + issuer: syndicate + - type: RoleRequirement + roles: + components: + - BloodFamilyRole + +- type: entity + abstract: true + parent: BaseObjective + id: BaseKeepFamilyAliveObjective + components: + - type: KeepGroupAliveCondition diff --git a/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml b/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml index 7ecdfb3a2cf66b..b2c0e6b043045e 100644 --- a/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml +++ b/Resources/Prototypes/EstacaoPirata/Objectives/bloodfamily.yml @@ -1,19 +1,46 @@ -# requires that the target survives the round - type: entity - abstract: true - parent: BaseSocialObjective - id: BaseKeepFamilyAliveObjective + noSpawn: true + parent: [BaseBloodFamilyObjective, BaseKeepFamilyAliveObjective] + id: FamilyAliveObjective + description: Mantenha sua família viva a qualquer custo. components: - - type: KeepFamilyAliveCondition + - type: Objective + difficulty: 1.75 + icon: + sprite: Objects/Specific/Medical/firstaidkits.rsi + state: advkit + - type: GroupTargetObjective + title: objective-condition-family-alive-title + - type: FamilyAlive - type: entity noSpawn: true - parent: [BaseTraitorSocialObjective, BaseKeepFamilyAliveObjective] - id: FamilyAliveObjective - description: Identify yourself at your own risk. We just need them alive. + parent: [BaseTraitorObjective, BaseKillObjective] + id: KillRandomPersonFamilyObjective + description: Do it however you like, just make sure they don't make it to centcom. components: - type: Objective difficulty: 1.75 + unique: false - type: TargetObjective - title: objective-condition-other-traitor-alive-title - - type: FamilyAlive + title: objective-condition-kill-person-title + - type: PickRandomPersonFamily + +- type: entity + noSpawn: true + parent: [BaseTraitorObjective, BaseKillObjective] + id: KillRandomHeadFamilyObjective + description: We need this head gone and you probably know why. Good luck, agent. + components: + - type: Objective + # technically its still possible for KillRandomPersonObjective to roll a head but this is guaranteed, so higher difficulty + difficulty: 3.0 + # killing 1 head is enough + unique: true + - type: TargetObjective + title: objective-condition-kill-head-title + - type: PickRandomHeadFamily + - type: KillPersonCondition + # don't count missing evac as killing as heads are higher profile, so you really need to do the dirty work + # if ce flies a shittle to centcom you better find a way onto it + requireDead: true diff --git a/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml b/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml new file mode 100644 index 00000000000000..7ed24ec2bf783f --- /dev/null +++ b/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml @@ -0,0 +1,14 @@ +# Objective Groups +- type: weightedRandom + id: BloodFamilyObjectiveGroups + weights: + TraitorObjectiveGroupSteal: 1 + BloodFamilyObjectiveGroupKill: 1 + TraitorObjectiveGroupState: 1 #As in, something about your character. Alive, dead, arrested, gained an ability... + TraitorObjectiveGroupSocial: 1 #Involves helping/harming others without killing them or stealing their stuff + +- type: weightedRandom + id: BloodFamilyObjectiveGroupKill + weights: + KillRandomPersonFamilyObjective: 1 + KillRandomHeadFamilyObjective: 0.25 diff --git a/Resources/Prototypes/EstacaoPirata/game_presets.yml b/Resources/Prototypes/EstacaoPirata/game_presets.yml index 9f3b62d16ecddd..21441cd3d75124 100644 --- a/Resources/Prototypes/EstacaoPirata/game_presets.yml +++ b/Resources/Prototypes/EstacaoPirata/game_presets.yml @@ -8,4 +8,5 @@ showInVote: false rules: - BloodFamily + - Traitor - BasicStationEventScheduler diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index cc4324e729ca23..1cce6c57b5ef17 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -9,6 +9,7 @@ roles: components: - TraitorRole + - BloodFamilyRole - type: entity abstract: true From 247168b6073435d9af443cb8916abc7b34745c8c Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Thu, 2 Nov 2023 21:18:45 -0300 Subject: [PATCH 03/11] Remove log warning --- .../Objectives/Systems/KeepGroupAliveConditionSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs index 88d3ef3f179237..ccfd3cd41542e2 100644 --- a/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs +++ b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs @@ -41,7 +41,6 @@ private void OnAssigned(EntityUid uid, FamilyAliveComponent component, ref Objec // You are the first/only family member. if (family.Count == 0) { - Log.Warning("VOCE E O PRIMEIRO FAMILY MEMBER"); args.Cancelled = true; return; } From 2036b260b8db03aa0fce3ec72f6e2f5c71bc628b Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Fri, 3 Nov 2023 01:07:04 -0300 Subject: [PATCH 04/11] WIP Times de familias --- .../Rules/BloodFamilyRuleSystem.cs | 112 +++++++++++++----- .../Components/BloodFamilyRuleComponent.cs | 6 +- 2 files changed, 85 insertions(+), 33 deletions(-) diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs index ac1e9016ddf0d5..10870f3b4ab9b9 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -77,16 +77,27 @@ private void DoBloodFamilyStart(BloodFamilyRuleComponent component) Log.Error("Tried to start Blood Family mode without enough candidates."); return; } - - var numFamily = MathHelper.Clamp(component.StartCandidates.Count / component.PlayersPerFamilyMember, 1, component.MaxBloodFamily); + //var numFamily = MathHelper.Clamp(component.StartCandidates.Count / component.PlayersPerFamilyMember, 1, component.MaxBloodFamily); var familyPool = FindPotentialFamilyMembers(component.StartCandidates, component); // Se a quantidade de pessoas que colocaram como preferencia Blood Family na criacao de personagem for menor que a quantidade esperada de membros da familia // seta a quantidade de membro esperados para a mesma de jogadores que escolheram ser membros da familia. - if (numFamily > familyPool.Count) - numFamily = familyPool.Count; + // if (numFamily > familyPool.Count) + // numFamily = familyPool.Count; + + var numTeams = (int)Math.Ceiling((double)familyPool.Count / (double)component.MaxBloodFamily); - var selectedFamily = PickFamilyMembers(numFamily, familyPool); + var selectedFamily = PickFamilyMembers(familyPool, numTeams, component); + + //var sessionAndMind = new Dictionary(); + + // foreach (var player in selectedFamily) + // { + // if (_mindSystem.TryGetMind(player, out var mindId, out var mindComponent)) + // { + // sessionAndMind.Add(player, (mindId,mindComponent)); + // } + // } foreach (var player in selectedFamily) { @@ -206,24 +217,49 @@ public List FindPotentialFamilyMembers(in Dictionary PickFamilyMembers(int familyMembersCount, List prefList) + public Dictionary PickFamilyMembers(List prefList, int numberOfFamilies, BloodFamilyRuleComponent component) { - var results = new List(familyMembersCount); + var results = new Dictionary(); if (prefList.Count == 0) { Log.Info("Insufficient ready players to fill up with blood family members, stopping the selection."); return results; } - for (var i = 0; i < familyMembersCount; i++) + foreach (var player in prefList) { - results.Add(_random.PickAndTake(prefList)); - Log.Info("Selected a preferred blood family member."); + if (!FilterPossiblePlayers(player)) + { + prefList.Remove(player); + } + } + + var familyPool = prefList.Count; + + for (var i = 0; i < numberOfFamilies; i++) + { + for (var j = 0; j < component.MaxBloodFamily; j++) + { + var randomPlayer = _random.PickAndTake(prefList); + if (_mindSystem.TryGetMind(randomPlayer, out var mindId, out var mindComponent)) + { + familyPool -= 1; + var newNumberOfFamilies = (int) Math.Ceiling((double) familyPool / (double) component.MaxBloodFamily); + results.Add(randomPlayer,(mindId, mindComponent, numberOfFamilies-newNumberOfFamilies)); + + Log.Info($"Selected a preferred blood family member for team {numberOfFamilies-newNumberOfFamilies}."); + if ((newNumberOfFamilies < numberOfFamilies) && results.Count > 1) + { + Log.Info($"Team {numberOfFamilies-newNumberOfFamilies} closed."); + break; + } + } + } } return results; } - public bool MakeBloodFamiliar(ICommonSession player) + public bool MakeBloodFamiliar(KeyValuePair playerMindAndTeam) { var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); if (bloodFamilyRuleEntity == null) @@ -234,6 +270,39 @@ public bool MakeBloodFamiliar(ICommonSession player) bloodFamilyRuleEntity = Comp(ruleEntity); } + // Ainda a decidir se vao vir com um uplink sem TC, mas aqui ficaria o codigo de dar o uplink e dar o codigo para abrir o uplink + + // Assign blood family roles + _roleSystem.MindAddRole(playerMindAndTeam.Value.Item1, new BloodFamilyRoleComponent + { + PrototypeId = bloodFamilyRuleEntity.BloodFamilyPrototypeId + }); + + bloodFamilyRuleEntity.BloodFamilyMinds.Add(playerMindAndTeam.Value.Item1); + bloodFamilyRuleEntity.BloodFamilyTeams.Add(playerMindAndTeam.Value.Item3 ,playerMindAndTeam.Value.Item1); + + if (_mindSystem.TryGetSession(playerMindAndTeam.Value.Item1, out var session)) + { + // Notificate player about new role assignment + _audioSystem.PlayGlobal(bloodFamilyRuleEntity.GreetSoundNotification, session); + } + + if (playerMindAndTeam.Value.Item2.OwnedEntity != null) + { + // Change the faction + _npcFaction.RemoveFaction(playerMindAndTeam.Value.Item2.OwnedEntity.Value, "NanoTrasen", false); + _npcFaction.AddFaction(playerMindAndTeam.Value.Item2.OwnedEntity.Value, "Syndicate"); + } + else + { + Log.Error($"Couldn't change {playerMindAndTeam.Value.Item1}'s faction to Syndicate"); + } + + return true; + } + + private bool FilterPossiblePlayers(ICommonSession player) + { if (!_mindSystem.TryGetMind(player, out var mindId, out var mind)) { Log.Info("Failed getting mind for picked blood family member."); @@ -258,27 +327,6 @@ public bool MakeBloodFamiliar(ICommonSession player) return false; } - // Ainda a decidir se vao vir com um uplink sem TC, mas aqui ficaria o codigo de dar o uplink e dar o codigo para abrir o uplink - - // Assign blood family roles - _roleSystem.MindAddRole(mindId, new BloodFamilyRoleComponent - { - PrototypeId = bloodFamilyRuleEntity.BloodFamilyPrototypeId - }); - - bloodFamilyRuleEntity.BloodFamilyMinds.Add(mindId); - - if (_mindSystem.TryGetSession(mindId, out var session)) - { - // Notificate player about new role assignment - Log.Error("AUDIO TOCANDOOO"); - _audioSystem.PlayGlobal(bloodFamilyRuleEntity.GreetSoundNotification, session); - } - - // Change the faction - _npcFaction.RemoveFaction(entity, "NanoTrasen", false); - _npcFaction.AddFaction(entity, "Syndicate"); - return true; } diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs index 25e825e214a9b0..18cbf0d639c946 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs @@ -14,11 +14,15 @@ public sealed partial class BloodFamilyRuleComponent : Component { public readonly List BloodFamilyMinds = new(); + public readonly Dictionary BloodFamilyTeams = new(); + [DataField("bloodFamilyPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string BloodFamilyPrototypeId = "BloodFamily"; public int TotalBloodFamilyMembers => BloodFamilyMinds.Count; // Alterar isto + // TODO: colocar os valores certos + /// /// Max Blood Family members allowed during selection. /// @@ -32,7 +36,7 @@ public sealed partial class BloodFamilyRuleComponent : Component public int PlayersPerFamilyMember = 1; /// - /// Min Blood Family members needed during selection. + /// Min number of players who selected Blood Family in character creation. /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MinBloodFamily = 2; From db32d61fa03c15644e919e9f17bfde5cea2962e3 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Fri, 3 Nov 2023 13:56:32 -0300 Subject: [PATCH 05/11] Adiciona times de familias --- .../Rules/BloodFamilyRuleSystem.cs | 165 ++++++++++++++---- .../Components/BloodFamilyRuleComponent.cs | 4 +- .../Systems/KeepGroupAliveConditionSystem.cs | 2 +- .../Systems/KillPersonConditionSystem.cs | 39 ++++- .../game-presets/preset-blood-family.ftl | 4 +- .../Prototypes/EstacaoPirata/game_presets.yml | 1 + 6 files changed, 169 insertions(+), 46 deletions(-) diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs index 10870f3b4ab9b9..7a9306889b2a54 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -46,6 +46,7 @@ public sealed class BloodFamilyRuleSystem : GameRuleSystem private void DoBloodFamilyStart(BloodFamilyRuleComponent component) { - Log.Debug("DoBloodFamilyStart chamado"); if (component.StartCandidates.Count < component.MinBloodFamily) // Setar os start candidates no OnPlayerJobsAssigned vulgo OnPlayerSpawn { + component.SelectionStatus = BloodFamilyRuleComponent.SelectionState.Error; Log.Error("Tried to start Blood Family mode without enough candidates."); return; } - //var numFamily = MathHelper.Clamp(component.StartCandidates.Count / component.PlayersPerFamilyMember, 1, component.MaxBloodFamily); + var familyPool = FindPotentialFamilyMembers(component.StartCandidates, component); - // Se a quantidade de pessoas que colocaram como preferencia Blood Family na criacao de personagem for menor que a quantidade esperada de membros da familia - // seta a quantidade de membro esperados para a mesma de jogadores que escolheram ser membros da familia. - // if (numFamily > familyPool.Count) - // numFamily = familyPool.Count; + if (familyPool.Count < component.MinBloodFamily) + { + component.SelectionStatus = BloodFamilyRuleComponent.SelectionState.Error; + Log.Error("Tried to start Blood Family mode without enough candidates."); + return; + } var numTeams = (int)Math.Ceiling((double)familyPool.Count / (double)component.MaxBloodFamily); var selectedFamily = PickFamilyMembers(familyPool, numTeams, component); - //var sessionAndMind = new Dictionary(); - - // foreach (var player in selectedFamily) - // { - // if (_mindSystem.TryGetMind(player, out var mindId, out var mindComponent)) - // { - // sessionAndMind.Add(player, (mindId,mindComponent)); - // } - // } - foreach (var player in selectedFamily) { MakeBloodFamiliar(player); @@ -107,7 +100,7 @@ private void DoBloodFamilyStart(BloodFamilyRuleComponent component) component.SelectionStatus = BloodFamilyRuleComponent.SelectionState.SelectionMade; // Adiciona objetivos dos membros da familia, isto é feito aqui porque é garantido que todos os membros já foram selecionados e garante - // sincronismo com os objetivos de cada jogador + // sincronia com os objetivos de cada jogador var allFamily = GetAllBloodFamilyMembersAliveAndConnected(component); foreach (var member in allFamily) @@ -220,7 +213,7 @@ public List FindPotentialFamilyMembers(in Dictionary PickFamilyMembers(List prefList, int numberOfFamilies, BloodFamilyRuleComponent component) { var results = new Dictionary(); - if (prefList.Count == 0) + if (prefList.Count <= 1) { Log.Info("Insufficient ready players to fill up with blood family members, stopping the selection."); return results; @@ -234,31 +227,74 @@ public List FindPotentialFamilyMembers(in Dictionary 1) - { - Log.Info($"Team {numberOfFamilies-newNumberOfFamilies} closed."); - break; - } + results.Add(randomPlayer,(mindId, mindComponent, index)); + Log.Info($"Selected a {mindComponent.CharacterName} for team {index}."); } } + Log.Info($"Team {index} closed."); + index++; } + + // for (var i = 0; i < numberOfFamilies; i++) + // { + // for (var j = 0; j < component.MaxBloodFamily; j++) + // { + // // Colocar IF se randomPlayer nao foi usado, colocar index J com valor 0 (ou valor antes do ultimo loop sei la) + // var randomPlayer = _random.PickAndTake(prefList); + // if (_mindSystem.TryGetMind(randomPlayer, out var mindId, out var mindComponent)) + // { + // var newNumberOfFamilies = (int) Math.Ceiling((double) familyPool / (double) component.MaxBloodFamily); + // familyPool -= 1; + // results.Add(randomPlayer,(mindId, mindComponent, numberOfFamilies-newNumberOfFamilies)); + // + // Log.Info($"Selected a preferred blood family member for team {numberOfFamilies-newNumberOfFamilies}."); + // if ((newNumberOfFamilies < numberOfFamilies) && results.Count > 1) + // { + // Log.Info($"Team {numberOfFamilies-newNumberOfFamilies} closed."); + // break; + // } + // } + // } + // } return results; } + // TODO: fazer isso funcionar pra qualquer numero maximo de jogadores por time + private List FindDivisionOfTeams(int numPlayers) + { + var times = new List(); + + while (numPlayers > 0) + { + if (numPlayers >= 3 && numPlayers != 3 + 1) + { + times.Add(3); + numPlayers -= 3; + } + else + { + times.Add(2); + numPlayers -= 2; + } + } + + return times; + } + public bool MakeBloodFamiliar(KeyValuePair playerMindAndTeam) { var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); @@ -279,7 +315,8 @@ public bool MakeBloodFamiliar(KeyValuePair GetOtherBloodFamilyMindsAliveAndConnected(MindComponent ourMind) + public List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(MindComponent ourMind) { List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); foreach (var traitor in EntityQuery()) { - foreach (var role in GetOtherBloodFamilyMindsAliveAndConnected(ourMind, traitor)) + foreach (var role in GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(ourMind, traitor)) { if (!allTraitors.Contains(role)) allTraitors.Add(role); @@ -387,6 +424,65 @@ private void GiveImplants(EntityUid uid) return allTraitors; } + private List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(MindComponent ourMind, BloodFamilyRuleComponent component) + { + if (ourMind.CurrentEntity == null) + return new List<(EntityUid Id, MindComponent Mind)>(); + + var allPlayers = GetAllBloodFamilyMembersAliveAndConnected(component); + + var find = allPlayers.Find(n=>n.Mind.UserId == ourMind.UserId); + + // Log.Debug($"ourMind({ourMind.CharacterName}): currentEntity:{ourMind.CurrentEntity} ownedEntity:{ourMind.OwnedEntity}"); + // + // foreach (var person in component.BloodFamilyTeams) + // { + // Log.Debug($"{person.Key} do time {person.Value}"); + // } + + var ourMindAndTeam = component.BloodFamilyTeams.Where(n=>n.Key == find.Id); + var ourMindAndTeamReal = ourMindAndTeam.FirstOrDefault(); + + var ourTeam = ourMindAndTeamReal.Value; + + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + foreach (var traitor in component.BloodFamilyTeams) + { + if(traitor.Value != ourTeam) + continue; + + if (TryComp(traitor.Key, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + mind != ourMind && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor.Key, mind)); + } + } + + return traitors; + } + + // private List<(EntityUid Id, MindComponent Mind)> GetAllBloodFamilyMindsAliveAndConnectedSameTeam(MindComponent ourMind, BloodFamilyRuleComponent component) + // { + // var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + // foreach (var traitor in component.BloodFamilyMinds) + // { + // if (TryComp(traitor, out MindComponent? mind) && + // mind.OwnedEntity != null && + // mind.Session != null && + // _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + // mind.CurrentEntity == mind.OwnedEntity) + // { + // traitors.Add((traitor, mind)); + // } + // } + // + // return traitors; + // } + private List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnected(MindComponent ourMind, BloodFamilyRuleComponent component) { var traitors = new List<(EntityUid Id, MindComponent Mind)>(); @@ -426,6 +522,7 @@ private void GiveImplants(EntityUid uid) private void OnObjectivesTextGetInfo(EntityUid uid, BloodFamilyRuleComponent component, ref ObjectivesTextGetInfoEvent args) { + // TODO: nomes de familias, deixar melhor este texto "Havia 4 membro da Familias" args.Minds = component.BloodFamilyMinds; args.AgentName = Loc.GetString("blood-family-round-end-agent-name"); } @@ -440,7 +537,7 @@ private void SendBloodFamilyBriefing(EntityUid mind, MindComponent ourMind) if (!_mindSystem.TryGetSession(mind, out var session)) return; - var family = GetOtherBloodFamilyMindsAliveAndConnected(ourMind); + var family = GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(ourMind); var targetsNames = new StringBuilder(""); int index = 0; diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs index 18cbf0d639c946..f3b3fd2a205738 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs @@ -14,7 +14,8 @@ public sealed partial class BloodFamilyRuleComponent : Component { public readonly List BloodFamilyMinds = new(); - public readonly Dictionary BloodFamilyTeams = new(); + // Mind + team + public readonly Dictionary BloodFamilyTeams = new(); [DataField("bloodFamilyPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string BloodFamilyPrototypeId = "BloodFamily"; @@ -58,6 +59,7 @@ public enum SelectionState WaitingForSpawn = 0, ReadyToSelect = 1, SelectionMade = 2, + Error = 3, } public SelectionState SelectionStatus = SelectionState.WaitingForSpawn; diff --git a/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs index ccfd3cd41542e2..4e08964254324b 100644 --- a/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs +++ b/Content.Server/EstacaoPirata/Objectives/Systems/KeepGroupAliveConditionSystem.cs @@ -36,7 +36,7 @@ private void OnAssigned(EntityUid uid, FamilyAliveComponent component, ref Objec return; } - var family = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_bloodFamilyRule.GetOtherBloodFamilyMindsAliveAndConnected(args.Mind)); + var family = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_bloodFamilyRule.GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(args.Mind)); // You are the first/only family member. if (family.Count == 0) diff --git a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs index 74be13e75a0dd6..d75f34f55f60e1 100644 --- a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs +++ b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.EstacaoPirata.GameTicking.Rules; using Content.Server.EstacaoPirata.GameTicking.Rules.Components; using Content.Server.EstacaoPirata.Objectives.Components; using Content.Server.Objectives.Components; @@ -23,6 +24,7 @@ public sealed class KillPersonConditionSystem : EntitySystem [Dependency] private readonly SharedJobSystem _job = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; + [Dependency] private readonly BloodFamilyRuleSystem _bloodFamilyRule = default!; public override void Initialize() { @@ -85,6 +87,7 @@ private void OnPersonFamilyAssigned(EntityUid uid, PickRandomPersonFamilyCompone var allHumans = _mind.GetAliveHumansExcept(args.MindId); + var thisHuman = args.MindId; // no other humans to kill if (allHumans.Count == 0) @@ -101,11 +104,22 @@ private void OnPersonFamilyAssigned(EntityUid uid, PickRandomPersonFamilyCompone return; } - Log.Warning($"Quantidade de familiares: {family.BloodFamilyMinds.Count}"); - foreach (var player in family.BloodFamilyMinds) + var otherTeamMembers = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_bloodFamilyRule.GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(args.Mind)); + + if (otherTeamMembers.Count == 0) + { + args.Cancelled = true; + return; + } + + // var thisHumanMind = family.BloodFamilyTeams.Where(n=>n.Key == thisHuman); + // var thisHumanMindReal = thisHumanMind.FirstOrDefault(); + + foreach (var player in otherTeamMembers) { - Log.Warning($"Familiar: {player}"); - allHumans.Remove(player); + + Log.Warning($"Familiar: {player.Mind.CharacterName} removido do pool de objetivos kill seu time"); + allHumans.Remove(player.Id); } if (allHumans.Count <= 0) @@ -167,6 +181,7 @@ private void OnHeadFamilyAssigned(EntityUid uid, PickRandomHeadFamilyComponent c var allHumans = _mind.GetAliveHumansExcept(args.MindId); + var thisHuman = args.MindId; // no other humans to kill if (allHumans.Count == 0) @@ -183,11 +198,19 @@ private void OnHeadFamilyAssigned(EntityUid uid, PickRandomHeadFamilyComponent c return; } - Log.Warning($"Quantidade de familiares: {family.BloodFamilyMinds.Count}"); - foreach (var player in family.BloodFamilyMinds) + var otherTeamMembers = Enumerable.ToList<(EntityUid Id, MindComponent Mind)>(_bloodFamilyRule.GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(args.Mind)); + + if (otherTeamMembers.Count == 0) { - Log.Warning($"Familiar: {player}"); - allHumans.Remove(player); + args.Cancelled = true; + return; + } + + foreach (var player in otherTeamMembers) + { + + Log.Warning($"Familiar: {player.Mind.CharacterName} removido do pool de objetivos kill seu time"); + allHumans.Remove(player.Id); } var allHeads = new List(); diff --git a/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl b/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl index 7cc3ac16584a59..a8c6acea2d9604 100644 --- a/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl +++ b/Resources/Locale/pt-BR/estacao-pirata/game-ticking/game-presets/preset-blood-family.ftl @@ -6,10 +6,10 @@ blood-family-role-greeting = Morte a Nanotrasen! -blood-family-round-end-agent-name = membro da Família +blood-family-round-end-agent-name = membro de Família blood-family-not-enough-ready-players = Faltou mais jogadores prontos para a partida! Haviam {$readyPlayersCount} jogadores prontos de {$minimumPlayers} necessários. Impossível iniciar modo família de sangue. blood-family-no-one-ready = Nenhum jogador deu "pronto"! Impossível iniciar modo família de sangue. blood-family-title = Família de Sangue -blood-family-description = Uma família de traidores que fará de tudo para cumprir seus objetivos. +blood-family-description = Famílias de traidores que farão de tudo para cumprir seus objetivos. diff --git a/Resources/Prototypes/EstacaoPirata/game_presets.yml b/Resources/Prototypes/EstacaoPirata/game_presets.yml index 21441cd3d75124..f6af369e4141a3 100644 --- a/Resources/Prototypes/EstacaoPirata/game_presets.yml +++ b/Resources/Prototypes/EstacaoPirata/game_presets.yml @@ -6,6 +6,7 @@ name: blood-family-title description: blood-family-description showInVote: false + minPlayers: 2 rules: - BloodFamily - Traitor From 2670526c265c36424abe108891051acd195a4d6a Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Fri, 3 Nov 2023 13:56:50 -0300 Subject: [PATCH 06/11] Conserta ftl do emp que eu quebrei --- Resources/Locale/en-US/emp/emp.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/emp/emp.ftl b/Resources/Locale/en-US/emp/emp.ftl index 35b40432863051..6d5e790befbf92 100644 --- a/Resources/Locale/en-US/emp/emp.ftl +++ b/Resources/Locale/en-US/emp/emp.ftl @@ -1 +1 @@ -emp-disabled-comp-on-examine = cIt's disrupted by an electric field... [/color] +emp-disabled-comp-on-examine = [color=lightblue]It's disrupted by an electric field... [/color] From 8c28e8ca5a52b0c15778e96b270a36bbb879ef52 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Sat, 4 Nov 2023 12:01:55 -0300 Subject: [PATCH 07/11] Adiciona late join --- .../Rules/BloodFamilyRuleSystem.cs | 250 ++++++++++++++---- 1 file changed, 204 insertions(+), 46 deletions(-) diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs index 7a9306889b2a54..a44fcfefc58ba3 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -10,7 +10,6 @@ using Content.Server.Mind; using Content.Server.NPC.Systems; using Content.Server.Objectives; -using Content.Server.Radio.Components; using Content.Server.Roles; using Content.Server.Shuttles.Components; using Content.Shared.CCVar; @@ -24,6 +23,7 @@ using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Players; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -31,7 +31,7 @@ namespace Content.Server.EstacaoPirata.GameTicking.Rules; public sealed class BloodFamilyRuleSystem : GameRuleSystem { - + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -46,7 +46,6 @@ public sealed class BloodFamilyRuleSystem : GameRuleSystem(OnStartAttempt); SubscribeLocalEvent(OnPlayerJobAssigned); SubscribeLocalEvent(OnObjectivesTextGetInfo); - // Vai ter late join? + SubscribeLocalEvent(HandleLatejoin); } + protected override void ActiveTick(EntityUid uid, BloodFamilyRuleComponent component, GameRuleComponent gameRule, float frameTime) { base.ActiveTick(uid, component, gameRule, frameTime); @@ -90,7 +90,7 @@ private void DoBloodFamilyStart(BloodFamilyRuleComponent component) var numTeams = (int)Math.Ceiling((double)familyPool.Count / (double)component.MaxBloodFamily); - var selectedFamily = PickFamilyMembers(familyPool, numTeams, component); + var selectedFamily = PickFamilyMembers(familyPool, component); foreach (var player in selectedFamily) { @@ -172,6 +172,140 @@ private void OnStartAttempt(RoundStartAttemptEvent ev) } } + + private void HandleLatejoin(PlayerSpawnCompleteEvent ev) + { + Log.Warning("HANDLE LATE JOIN"); + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var bloodFamily, out var gameRule)) + { + if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + continue; + + if (bloodFamily.BloodFamilyMinds.Count >= bloodFamily.MaxPlayers) + continue; + if (!ev.LateJoin) + continue; + if (!ev.Profile.AntagPreferences.Contains(bloodFamily.BloodFamilyPrototypeId)) + continue; + + if (ev.JobId == null || !_prototypeManager.TryIndex(ev.JobId, out var job)) + continue; + + if (!job.CanBeAntag) + continue; + + // Before the announcement is made, late-joiners are considered the same as players who readied. + if (bloodFamily.SelectionStatus < BloodFamilyRuleComponent.SelectionState.SelectionMade) + { + bloodFamily.StartCandidates[ev.Player] = ev.Profile; + continue; + } + + // the nth player we adjust our probabilities around + var target = bloodFamily.PlayersPerFamilyMember * bloodFamily.BloodFamilyMinds.Count + 1; + + var chance = 1f / bloodFamily.PlayersPerFamilyMember; + + // If we have too many traitors, divide by how many players below target for next traitor we are. + if (ev.JoinOrder < target) + { + chance /= (target - ev.JoinOrder); + } + else // Tick up towards 100% chance. + { + chance *= ((ev.JoinOrder + 1) - target); + } + + if (chance > 1) + chance = 1; + + // Now that we've calculated our chance, roll and make them a traitor if we roll under. + // You get one shot. + if (_random.Prob(chance)) + { + + + /* + * Chance de 50% para ou criar uma familia nova com 2 pessoas, ou com 3 + */ + var probabilidade = _random.Prob(0.5f); + var minBloodFamily = 0; + + if (probabilidade) + { + minBloodFamily = bloodFamily.MinBloodFamily; + } + else + { + minBloodFamily = bloodFamily.MinBloodFamily - 1; + } + + /* Se a quantidade de pessoas esperando para serem familia forem maior ou igual ao minimo de pessoas necessárias para formar uma familia, + * forma uma familia nova com eles + */ + if (bloodFamily.BloodFamilyQueue.Count >= minBloodFamily) + { + var divisionOfTeams = FindDivisionOfTeams(bloodFamily.BloodFamilyMinds.Count); + + if (_mindSystem.TryGetMind(ev.Player, out var mindId, out var mindComponent)) + { + bloodFamily.BloodFamilyQueue.Add(ev.Player, (mindId,mindComponent, divisionOfTeams.Count)); + } + + int index = 0; + var peopleToDelete = new Dictionary(); + foreach (var playerInQueue in bloodFamily.BloodFamilyQueue) + { + if (index > bloodFamily.MaxBloodFamily) + break; + + MakeBloodFamiliar(playerInQueue); + + peopleToDelete.Add(playerInQueue.Key,playerInQueue.Value); + index++; + } + + + // Da os objetivos para as pessoas que foram adicionadas e remove elas do queue + foreach (var player in peopleToDelete) + { + GiveObjectives(player.Value.Item1, player.Value.Item2, bloodFamily); + + if (player.Value.Item2.OwnedEntity != null) + { + GiveImplants(player.Value.Item2.OwnedEntity.Value); + } + + SendBloodFamilyBriefing(player.Value.Item1, player.Value.Item2); + bloodFamily.BloodFamilyQueue.Remove(player.Key); + } + + } + else + { + Log.Warning("APENAS ADICIONANDO AO QUEUE"); + var divisionOfTeams = FindDivisionOfTeams(bloodFamily.BloodFamilyMinds.Count); + + if (_mindSystem.TryGetMind(ev.Player, out var mindId, out var mindComponent)) + { + bloodFamily.BloodFamilyQueue.Add(ev.Player, (mindId, mindComponent, divisionOfTeams.Count)); + } + } + + // Nao sei se vou deixar entrar num time ja existente, do jeito que funciona os objectives, nao tem como atualizar os objetivos de alg pra incluir + // o familiar novo + } + } + } + + /// + /// Returns all the players that can be an antagonist and has Blood Family enabled in preferences + /// + /// All the candidates that will be checked upon + /// The Blood Family component + /// public List FindPotentialFamilyMembers(in Dictionary candidates, BloodFamilyRuleComponent component) { var list = new List(); @@ -210,7 +344,13 @@ public List FindPotentialFamilyMembers(in Dictionary PickFamilyMembers(List prefList, int numberOfFamilies, BloodFamilyRuleComponent component) + /// + /// Selects which players will be marked to become a blood family member and which team they will be a part of + /// + /// List of players who will possibly be picked to be an antag + /// Blood Family component + /// + public Dictionary PickFamilyMembers(List prefList, BloodFamilyRuleComponent component) { var results = new Dictionary(); if (prefList.Count <= 1) @@ -219,6 +359,9 @@ public List FindPotentialFamilyMembers(in Dictionary FindPotentialFamilyMembers(in Dictionary FindPotentialFamilyMembers(in Dictionary 1) - // { - // Log.Info($"Team {numberOfFamilies-newNumberOfFamilies} closed."); - // break; - // } - // } - // } - // } return results; } - // TODO: fazer isso funcionar pra qualquer numero maximo de jogadores por time + // TODO: fazer isso funcionar pra qualquer numero maximo de jogadores por time (basicamente nao ser hardcoded) + /// + /// Creates a group division based on the number of blood family members + /// + /// Number of players + /// List containing the amount of players in every group + /// Example: If numPlayers is 7, it will return [3,2,2], where every number inside the list is the amount of + /// players in a group, and in this case, there are 3 groups private List FindDivisionOfTeams(int numPlayers) { var times = new List(); @@ -295,6 +422,11 @@ private List FindDivisionOfTeams(int numPlayers) return times; } + /// + /// Makes a player a blood family member, gives their mind the blood family prototype id + /// + /// A player's session, mind, mind component and team they belong to + /// True if it was successful, false if not public bool MakeBloodFamiliar(KeyValuePair playerMindAndTeam) { var bloodFamilyRuleEntity = EntityQuery().FirstOrDefault(); @@ -316,7 +448,6 @@ public bool MakeBloodFamiliar(KeyValuePair + /// Checks if a player has a mind, is already a traitor, is already a blood family member, and if the mind has an attached entity + /// + /// + /// True if all the checks passed private bool FilterPossiblePlayers(ICommonSession player) { if (!_mindSystem.TryGetMind(player, out var mindId, out var mind)) @@ -367,6 +503,12 @@ private bool FilterPossiblePlayers(ICommonSession player) return true; } + /// + /// Gives the blood family member its objetives + /// + /// Mind entity of a player + /// Mind component of a player + /// Blood Family Rule component private void GiveObjectives(EntityUid mindId, MindComponent mind, BloodFamilyRuleComponent ruleComponent) { var maxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty); @@ -382,6 +524,9 @@ private void GiveObjectives(EntityUid mindId, MindComponent mind, BloodFamilyRul difficulty += Comp(objective.Value).Difficulty; } + /* + * Gives the blood family member the "keep your family alive" objective, all blood family members must have one + */ var familyObjective = _objectivesSystem.TryCreateObjective(mindId, mind, "FamilyAliveObjective"); if (familyObjective != null) @@ -390,15 +535,13 @@ private void GiveObjectives(EntityUid mindId, MindComponent mind, BloodFamilyRul } } + // TODO: Dar ao jogador uma linha de radio privada + /// + /// This will give a player its main source of communication between family members + /// + /// Player's mind owned entity private void GiveImplants(EntityUid uid) { - // Hack ferrado - // var intrinsicRadioTransmitter = AddComp(uid); - // var activeRadio = AddComp(uid); - // var intrinsicRadioRec = AddComp(uid); - // activeRadio.Channels.Add("Syndicate"); - // intrinsicRadioTransmitter.Channels.Add("Syndicate"); - // var proto = Spawn("SyndieRadioImplant"); // if (!TryComp(proto, out var comp)) // { @@ -409,6 +552,11 @@ private void GiveImplants(EntityUid uid) _antagSelection.GiveAntagBagGear(uid, "EncryptionKeySyndie"); } + /// + /// Gets all blood family members in a team + /// + /// A player's mind component + /// List of players that are in the passed player's team (ourMind) public List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(MindComponent ourMind) { List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); @@ -424,6 +572,12 @@ private void GiveImplants(EntityUid uid) return allTraitors; } + /// + /// Gets all blood family members in a team + /// + /// A player's mind component + /// Blood Family Rule component + /// List of players that are in the passed player's team (ourMind) private List<(EntityUid Id, MindComponent Mind)> GetOtherBloodFamilyMindsAliveAndConnectedSameTeam(MindComponent ourMind, BloodFamilyRuleComponent component) { if (ourMind.CurrentEntity == null) @@ -433,13 +587,6 @@ private void GiveImplants(EntityUid uid) var find = allPlayers.Find(n=>n.Mind.UserId == ourMind.UserId); - // Log.Debug($"ourMind({ourMind.CharacterName}): currentEntity:{ourMind.CurrentEntity} ownedEntity:{ourMind.OwnedEntity}"); - // - // foreach (var person in component.BloodFamilyTeams) - // { - // Log.Debug($"{person.Key} do time {person.Value}"); - // } - var ourMindAndTeam = component.BloodFamilyTeams.Where(n=>n.Key == find.Id); var ourMindAndTeamReal = ourMindAndTeam.FirstOrDefault(); @@ -502,6 +649,11 @@ private void GiveImplants(EntityUid uid) return traitors; } + /// + /// Gets all players that are blood family members in the match + /// + /// Blood Family Rule component + /// List of players private List<(EntityUid Id, MindComponent Mind)> GetAllBloodFamilyMembersAliveAndConnected(BloodFamilyRuleComponent component) { var traitors = new List<(EntityUid Id, MindComponent Mind)>(); @@ -520,6 +672,12 @@ private void GiveImplants(EntityUid uid) return traitors; } + /// + /// Sets the round end summary text + /// + /// + /// + /// private void OnObjectivesTextGetInfo(EntityUid uid, BloodFamilyRuleComponent component, ref ObjectivesTextGetInfoEvent args) { // TODO: nomes de familias, deixar melhor este texto "Havia 4 membro da Familias" @@ -528,7 +686,7 @@ private void OnObjectivesTextGetInfo(EntityUid uid, BloodFamilyRuleComponent com } /// - /// Envia o texto de greeting e quem são os outros blood family members + /// Sends the greeting text to a player /// /// /// From b9bf3262a48d100550ddde8ffb019734618bd565 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Sat, 4 Nov 2023 12:02:14 -0300 Subject: [PATCH 08/11] Modo aparece em vote para testes --- Resources/Prototypes/EstacaoPirata/game_presets.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Resources/Prototypes/EstacaoPirata/game_presets.yml b/Resources/Prototypes/EstacaoPirata/game_presets.yml index f6af369e4141a3..83c56904052c40 100644 --- a/Resources/Prototypes/EstacaoPirata/game_presets.yml +++ b/Resources/Prototypes/EstacaoPirata/game_presets.yml @@ -5,9 +5,8 @@ - bloodbrothers name: blood-family-title description: blood-family-description - showInVote: false + showInVote: true minPlayers: 2 rules: - BloodFamily - - Traitor - BasicStationEventScheduler From bb7c317224139cd8df67c98438b5d58c25e8f031 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Sat, 4 Nov 2023 12:51:04 -0300 Subject: [PATCH 09/11] Altera regras do jogo --- .../Components/BloodFamilyRuleComponent.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs index f3b3fd2a205738..404d9804f07419 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/Components/BloodFamilyRuleComponent.cs @@ -1,4 +1,5 @@ -using Content.Shared.Preferences; +using Content.Shared.Mind; +using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Audio; @@ -17,6 +18,8 @@ public sealed partial class BloodFamilyRuleComponent : Component // Mind + team public readonly Dictionary BloodFamilyTeams = new(); + public Dictionary BloodFamilyQueue = new(); + [DataField("bloodFamilyPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string BloodFamilyPrototypeId = "BloodFamily"; @@ -25,7 +28,7 @@ public sealed partial class BloodFamilyRuleComponent : Component // TODO: colocar os valores certos /// - /// Max Blood Family members allowed during selection. + /// Max amount members in a family /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MaxBloodFamily = 3; @@ -34,10 +37,10 @@ public sealed partial class BloodFamilyRuleComponent : Component /// For every X players, 1 will be a family member /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public int PlayersPerFamilyMember = 1; + public int PlayersPerFamilyMember = 8; /// - /// Min number of players who selected Blood Family in character creation. + /// Min number of players who selected Blood Family in character creation. (MUDAR PARA Min amount of members in a family?) /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MinBloodFamily = 2; @@ -52,7 +55,13 @@ public sealed partial class BloodFamilyRuleComponent : Component /// Minimum players in game for the game rule to be selected /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public int MinPlayers = 1; + public int MinPlayers = 5; + + /// + /// Maximum players to be selected as a blood family member + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxPlayers = 20; public enum SelectionState { From c1893cefedfa7bc25f0b6733944f83183e3d4d74 Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Sat, 4 Nov 2023 13:01:04 -0300 Subject: [PATCH 10/11] Retira o objetivo de morrer do blood family --- .../EstacaoPirata/Objectives/objectiveGroups.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml b/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml index 7ed24ec2bf783f..01988d609a5ef5 100644 --- a/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/EstacaoPirata/Objectives/objectiveGroups.yml @@ -4,7 +4,7 @@ weights: TraitorObjectiveGroupSteal: 1 BloodFamilyObjectiveGroupKill: 1 - TraitorObjectiveGroupState: 1 #As in, something about your character. Alive, dead, arrested, gained an ability... + BloodFamilyObjectiveGroupState: 1 #As in, something about your character. Alive, dead, arrested, gained an ability... TraitorObjectiveGroupSocial: 1 #Involves helping/harming others without killing them or stealing their stuff - type: weightedRandom @@ -12,3 +12,8 @@ weights: KillRandomPersonFamilyObjective: 1 KillRandomHeadFamilyObjective: 0.25 + +- type: weightedRandom + id: BloodFamilyObjectiveGroupState + weights: + EscapeShuttleObjective: 1 From 19c37879a4aef174c05c0b0b4c23dd91fb5a759e Mon Sep 17 00:00:00 2001 From: Caio Schmidt Date: Sat, 4 Nov 2023 14:48:50 -0300 Subject: [PATCH 11/11] Corrige tempo de start --- .../GameTicking/Rules/BloodFamilyRuleSystem.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs index a44fcfefc58ba3..03fd5936f74989 100644 --- a/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs +++ b/Content.Server/EstacaoPirata/GameTicking/Rules/BloodFamilyRuleSystem.cs @@ -136,10 +136,10 @@ private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) } // Vai usar o delay do traitor por enquanto mesmo - var delay = TimeSpan.FromSeconds(10); - // var delay = TimeSpan.FromSeconds( // TimeSpan.FromSeconds(20); - // _cfg.GetCVar(CCVars.TraitorStartDelay) + - // _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); + // var delay = TimeSpan.FromSeconds(10); + var delay = TimeSpan.FromSeconds( + _cfg.GetCVar(CCVars.TraitorStartDelay) + + _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); familiar.AnnounceAt = _gameTiming.CurTime + delay; @@ -175,7 +175,6 @@ private void OnStartAttempt(RoundStartAttemptEvent ev) private void HandleLatejoin(PlayerSpawnCompleteEvent ev) { - Log.Warning("HANDLE LATE JOIN"); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var bloodFamily, out var gameRule)) @@ -225,8 +224,6 @@ private void HandleLatejoin(PlayerSpawnCompleteEvent ev) // You get one shot. if (_random.Prob(chance)) { - - /* * Chance de 50% para ou criar uma familia nova com 2 pessoas, ou com 3 */ @@ -285,7 +282,6 @@ private void HandleLatejoin(PlayerSpawnCompleteEvent ev) } else { - Log.Warning("APENAS ADICIONANDO AO QUEUE"); var divisionOfTeams = FindDivisionOfTeams(bloodFamily.BloodFamilyMinds.Count); if (_mindSystem.TryGetMind(ev.Player, out var mindId, out var mindComponent))