* basic implementation

* minor fixes

* objectives temp commit

* proper onstart bind

* changes all conditions to be bound to a mind-instance

* oops

* oops v2

* adds possiblity to enable duplicate assignment of objective
equal objectives are unable to be added

* minor fixes, adds greentext

* refactors incompatability to be defined by requirements

* fixes a wrong whitespace

* minor fix

* addressed reviews v1

* address reviews v2

Co-authored-by: Exp <theexp111@gmail.com>

* final sweep

* adds/refactors traitor&sss cvars

* Update Content.Server/Mobs/Mind.cs

* never trust github web

* adds datasets & makes codewords use them

* addresses exp's reviews

* addressed zumos reviews

Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: Exp <theexp111@gmail.com>
This commit is contained in:
Paul Ritter
2020-12-01 17:05:19 +01:00
committed by GitHub
parent 602f37e70c
commit f7c09fbd7e
28 changed files with 1875 additions and 107 deletions

View File

@@ -43,8 +43,6 @@ namespace Content.Client.GameObjects.Components.Actor
{
base.HandleNetworkMessage(message, netChannel, session);
if(session?.AttachedEntity != Owner) return;
switch (message)
{
case CharacterInfoMessage characterInfoMessage:

View File

@@ -16,6 +16,8 @@ namespace Content.Server.GameObjects.Components.Actor
{
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
{
if(session?.AttachedEntity != Owner) return;
switch (message)
{
case RequestCharacterInfoMessage _:
@@ -30,12 +32,12 @@ namespace Content.Server.GameObjects.Components.Actor
// getting conditions
foreach (var objective in mind.AllObjectives)
{
if (!conditions.ContainsKey(objective.Issuer))
conditions[objective.Issuer] = new List<ConditionInfo>();
if (!conditions.ContainsKey(objective.Prototype.Issuer))
conditions[objective.Prototype.Issuer] = new List<ConditionInfo>();
foreach (var condition in objective.Conditions)
{
conditions[objective.Issuer].Add(new ConditionInfo(condition.GetTitle(),
condition.GetDescription(), condition.GetIcon(), condition.GetProgress(mindComponent.Mind)));
conditions[objective.Prototype.Issuer].Add(new ConditionInfo(condition.Title,
condition.Description, condition.Icon, condition.Progress));
}
}

View File

@@ -15,5 +15,9 @@ namespace Content.Server.GameTicking
public virtual string Description => "Secret!";
public virtual bool DisallowLateJoin => false;
public Dictionary<NetUserId, HumanoidCharacterProfile> readyProfiles;
public virtual void OnGameStarted() { }
public virtual string GetRoundEndDescription() => "";
}
}

View File

@@ -47,10 +47,10 @@ namespace Content.Server.GameTicking.GamePresets
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
{
MinPlayers = _cfg.GetCVar(CCVars.GameSuspicionMinPlayers);
MinTraitors = _cfg.GetCVar(CCVars.GameSuspicionMinTraitors);
PlayersPerTraitor = _cfg.GetCVar(CCVars.GameSuspicionPlayersPerTraitor);
TraitorStartingBalance = _cfg.GetCVar(CCVars.GameSuspicionStartingBalance);
MinPlayers = _cfg.GetCVar(CCVars.SuspicionMinPlayers);
MinTraitors = _cfg.GetCVar(CCVars.SuspicionMinTraitors);
PlayersPerTraitor = _cfg.GetCVar(CCVars.SuspicionPlayersPerTraitor);
TraitorStartingBalance = _cfg.GetCVar(CCVars.SuspicionStartingBalance);
if (!force && readyPlayers.Count < MinPlayers)
{

View File

@@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.GameObjects.Components.PDA;
using Content.Server.GameTicking.GameRules;
using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles.Traitor;
using Content.Server.Objectives.Interfaces;
using Content.Server.Players;
using Content.Server.Prototypes;
using Content.Shared;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.PDA;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.GameTicking.GamePresets
{
public class PresetTraitor : GamePreset
{
[Dependency] private readonly IGameTicker _gameticker = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override string ModeTitle => "Traitor";
private int MinPlayers { get; set; }
private int PlayersPerTraitor { get; set; }
private int MaxTraitors { get; set; }
private int CodewordCount { get; set; }
private int StartingBalance { get; set; }
private float MaxDifficulty { get; set; }
private int MaxPicks { get; set; }
private readonly List<TraitorRole> _traitors = new ();
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
{
MinPlayers = _cfg.GetCVar(CCVars.TraitorMinPlayers);
PlayersPerTraitor = _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor);
MaxTraitors = _cfg.GetCVar(CCVars.TraitorMaxTraitors);
CodewordCount = _cfg.GetCVar(CCVars.TraitorCodewordCount);
StartingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance);
MaxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty);
MaxPicks = _cfg.GetCVar(CCVars.TraitorMaxPicks);
if (!force && readyPlayers.Count < MinPlayers)
{
_chatManager.DispatchServerAnnouncement($"Not enough players readied up for the game! There were {readyPlayers.Count} players readied up out of {MinPlayers} needed.");
return false;
}
if (readyPlayers.Count == 0)
{
_chatManager.DispatchServerAnnouncement("No players readied up! Can't start Traitor.");
return false;
}
var list = new List<IPlayerSession>(readyPlayers);
var prefList = new List<IPlayerSession>();
foreach (var player in list)
{
if (!readyProfiles.ContainsKey(player.UserId))
{
continue;
}
var profile = readyProfiles[player.UserId];
if (profile.AntagPreferences.Contains("Traitor"))
{
prefList.Add(player);
}
}
var numTraitors = MathHelper.Clamp(readyPlayers.Count / PlayersPerTraitor,
1, MaxTraitors);
for (var i = 0; i < numTraitors; i++)
{
IPlayerSession traitor;
if(prefList.Count < numTraitors)
{
if (list.Count == 0)
{
Logger.InfoS("preset", "Insufficient ready players to fill up with traitors, stopping the selection.");
break;
}
traitor = _random.PickAndTake(list);
Logger.InfoS("preset", "Insufficient preferred traitors, picking at random.");
}
else
{
traitor = _random.PickAndTake(prefList);
list.Remove(traitor);
Logger.InfoS("preset", "Selected a preferred traitor.");
}
var mind = traitor.Data.ContentData()?.Mind;
var traitorRole = new TraitorRole(mind);
if (mind == null)
{
Logger.ErrorS("preset", "Failed getting mind for picked traitor.");
continue;
}
// creadth: we need to create uplink for the antag.
// PDA should be in place already, so we just need to
// initiate uplink account.
var uplinkAccount = new UplinkAccount(mind.OwnedEntity.Uid, StartingBalance);
var inventory = mind.OwnedEntity.GetComponent<InventoryComponent>();
if (!inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent pdaItem))
{
Logger.ErrorS("preset", "Failed getting pda for picked traitor.");
continue;
}
var pda = pdaItem.Owner;
var pdaComponent = pda.GetComponent<PDAComponent>();
if (pdaComponent.IdSlotEmpty)
{
Logger.ErrorS("preset","PDA had no id for picked traitor");
continue;
}
mind.AddRole(traitorRole);
_traitors.Add(traitorRole);
pdaComponent.InitUplinkAccount(uplinkAccount);
}
var adjectives = _prototypeManager.Index<DatasetPrototype>("adjectives").Values;
var verbs = _prototypeManager.Index<DatasetPrototype>("verbs").Values;
var codewordPool = adjectives.Concat(verbs).ToList();
var finalCodewordCount = Math.Min(CodewordCount, codewordPool.Count);
var codewords = new string[finalCodewordCount];
for (var i = 0; i < finalCodewordCount; i++)
{
codewords[i] = _random.PickAndTake(codewordPool);
}
foreach (var traitor in _traitors)
{
traitor.GreetTraitor(codewords);
}
_gameticker.AddGameRule<RuleTraitor>();
return true;
}
public override void OnGameStarted()
{
var objectivesMgr = IoCManager.Resolve<IObjectivesManager>();
foreach (var traitor in _traitors)
{
//give traitors their objectives
var difficulty = 0f;
for (var pick = 0; pick < MaxPicks && MaxDifficulty > difficulty; pick++)
{
var objective = objectivesMgr.GetRandomObjective(traitor.Mind);
if (objective == null) continue;
if (traitor.Mind.TryAddObjective(objective))
difficulty += objective.Difficulty;
}
}
}
public override string GetRoundEndDescription()
{
var traitorCount = _traitors.Count;
var result = Loc.GetString("There {0} {1} {2}.", Loc.GetPluralString("was", "were", traitorCount),
traitorCount, Loc.GetPluralString("traitor", "traitors", traitorCount));
foreach (var traitor in _traitors)
{
result += Loc.GetString("\n{0} was a traitor",traitor.Mind.Session.Name);
var objectives = traitor.Mind.AllObjectives.ToArray();
if (objectives.Length == 0)
{
result += ".\n";
continue;
}
result += Loc.GetString(" and had the following objectives:");
foreach (var objectiveGroup in objectives.GroupBy(o => o.Prototype.Issuer))
{
result += $"\n[color=#87cefa]{Loc.GetString(objectiveGroup.Key)}[/color]";
foreach (var objective in objectiveGroup)
{
foreach (var condition in objective.Conditions)
{
var progress = condition.Progress;
result +=
Loc.GetString("\n- {0} | {1}", condition.Title, (progress > 0.99f ? $"[color=green]{Loc.GetString("Success!")}[/color]" : $"[color=red]{Loc.GetString("Failed!")}[/color] ({(int) (progress * 100)}%)"));
}
}
}
}
return result;
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Threading;
using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles.Traitor;
using Content.Server.Players;
using Content.Shared;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Server.GameTicking.GameRules
{
public class RuleTraitor : GameRule
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTicker _gameTicker = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
public override void Added()
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("Hello crew! Have a good shift!"));
bool Predicate(IPlayerSession session) => session.ContentData()?.Mind?.HasRole<TraitorRole>() ?? false;
EntitySystem.Get<AudioSystem>().PlayGlobal("/Audio/Misc/tatoralert.ogg", AudioParams.Default, Predicate);
}
}
}

View File

@@ -115,6 +115,15 @@ namespace Content.Server.GameTicking
}
}
[ViewVariables]
public GamePreset Preset
{
get => _preset == null ? MakeGamePreset(null) : _preset;
set => _preset = value;
}
private GamePreset _preset;
public event Action<GameRunLevelChangedEventArgs> OnRunLevelChanged;
public event Action<GameRuleAddedEventArgs> OnRuleAdded;
@@ -278,18 +287,18 @@ namespace Content.Server.GameTicking
}
// Time to start the preset.
var preset = MakeGamePreset(profiles);
Preset = MakeGamePreset(profiles);
DisallowLateJoin |= preset.DisallowLateJoin;
DisallowLateJoin |= Preset.DisallowLateJoin;
if (!preset.Start(assignedJobs.Keys.ToList(), force))
if (!Preset.Start(assignedJobs.Keys.ToList(), force))
{
if (_configurationManager.GetCVar(CCVars.GameLobbyFallbackEnabled))
{
SetStartPreset(_configurationManager.GetCVar(CCVars.GameLobbyFallbackPreset));
var newPreset = MakeGamePreset(profiles);
_chatManager.DispatchServerAnnouncement(
$"Failed to start {preset.ModeTitle} mode! Defaulting to {newPreset.ModeTitle}...");
$"Failed to start {Preset.ModeTitle} mode! Defaulting to {newPreset.ModeTitle}...");
if (!newPreset.Start(readyPlayers, force))
{
throw new ApplicationException("Fallback preset failed to start!");
@@ -297,15 +306,17 @@ namespace Content.Server.GameTicking
DisallowLateJoin = false;
DisallowLateJoin |= newPreset.DisallowLateJoin;
Preset = newPreset;
}
else
{
SendServerMessage($"Failed to start {preset.ModeTitle} mode! Restarting round...");
SendServerMessage($"Failed to start {Preset.ModeTitle} mode! Restarting round...");
RestartRound();
DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
return;
}
}
Preset.OnGameStarted();
_roundStartTimeSpan = IoCManager.Resolve<IGameTiming>().RealTime;
_sendStatusToAll();
@@ -342,8 +353,8 @@ namespace Content.Server.GameTicking
//Tell every client the round has ended.
var roundEndMessage = _netManager.CreateNetMessage<MsgRoundEndMessage>();
roundEndMessage.GamemodeTitle = MakeGamePreset(null).ModeTitle;
roundEndMessage.RoundEndText = roundEndText;
roundEndMessage.GamemodeTitle = Preset.ModeTitle;
roundEndMessage.RoundEndText = roundEndText + $"\n{Preset.GetRoundEndDescription()}";
//Get the timespan of the round.
roundEndMessage.RoundDuration = IoCManager.Resolve<IGameTiming>().RealTime.Subtract(_roundStartTimeSpan);
@@ -472,6 +483,7 @@ namespace Content.Server.GameTicking
"sandbox" => typeof(PresetSandbox),
"deathmatch" => typeof(PresetDeathMatch),
"suspicion" => typeof(PresetSuspicion),
"traitor" => typeof(PresetTraitor),
_ => default
};
@@ -1003,8 +1015,8 @@ namespace Content.Server.GameTicking
private string GetInfoText()
{
var gmTitle = MakeGamePreset(null).ModeTitle;
var desc = MakeGamePreset(null).Description;
var gmTitle = Preset.ModeTitle;
var desc = Preset.Description;
return Loc.GetString(@"Hi and welcome to [color=white]Space Station 14![/color]
The current game mode is: [color=white]{0}[/color].

View File

@@ -28,7 +28,7 @@ namespace Content.Server.Mobs
{
private readonly ISet<Role> _roles = new HashSet<Role>();
private readonly List<ObjectivePrototype> _objectives = new();
private readonly List<Objective> _objectives = new();
/// <summary>
/// Creates the new mind attached to a specific player session.
@@ -81,7 +81,7 @@ namespace Content.Server.Mobs
/// An enumerable over all the objectives this mind has.
/// </summary>
[ViewVariables]
public IEnumerable<ObjectivePrototype> AllObjectives => _objectives;
public IEnumerable<Objective> AllObjectives => _objectives;
/// <summary>
/// The session of the player owning this mind.
@@ -156,9 +156,12 @@ namespace Content.Server.Mobs
/// <summary>
/// Adds an objective to this mind.
/// </summary>
public bool TryAddObjective(ObjectivePrototype objective)
public bool TryAddObjective(ObjectivePrototype objectivePrototype)
{
if (!objective.CanBeAssigned(this))
if (!objectivePrototype.CanBeAssigned(this))
return false;
var objective = objectivePrototype.GetObjective(this);
if (_objectives.Contains(objective))
return false;
_objectives.Add(objective);
return true;

View File

@@ -0,0 +1,23 @@
using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.Mobs.Roles.Traitor
{
public class TraitorRole : Role
{
public TraitorRole(Mind mind) : base(mind)
{
}
public override string Name => "Syndicate Agent";
public override bool Antagonist => true;
public void GreetTraitor(string[] codewords)
{
var chatMgr = IoCManager.Resolve<IChatManager>();
chatMgr.DispatchServerMessage(Mind.Session, Loc.GetString("Hello Agent!"));
chatMgr.DispatchServerMessage(Mind.Session, Loc.GetString("Your codewords are: {0}", string.Join(", ",codewords)));
}
}
}

View File

@@ -0,0 +1,53 @@
#nullable enable
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Content.Shared.GameObjects.Components.Damage;
using JetBrains.Annotations;
using Robust.Shared.Localization;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
{
[UsedImplicitly]
public class DieCondition : IObjectiveCondition
{
private Mind? _mind;
public IObjectiveCondition GetAssigned(Mind mind)
{
return new DieCondition {_mind = mind};
}
public string Title => Loc.GetString("Die a glorius death");
public string Description => Loc.GetString("Die.");
public SpriteSpecifier Icon => new SpriteSpecifier.Rsi(new ResourcePath("Mobs/Ghosts/ghost_human.rsi"), "icon");
public float Progress => _mind?.OwnedEntity != null &&
_mind.OwnedEntity.TryGetComponent<IDamageableComponent>(out var damageableComponent) &&
damageableComponent.CurrentState != DamageState.Dead
? 0f
: 1f;
public float Difficulty => 1f;
public bool Equals(IObjectiveCondition? other)
{
return other is DieCondition condition && Equals(_mind, condition._mind);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((DieCondition) obj);
}
public override int GetHashCode()
{
return (_mind != null ? _mind.GetHashCode() : 0);
}
}
}

View File

@@ -0,0 +1,48 @@
#nullable enable
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Shared.Localization;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
{
public abstract class KillPersonCondition : IObjectiveCondition
{
protected Mind? Target;
public abstract IObjectiveCondition GetAssigned(Mind mind);
public string Title => Loc.GetString("Kill {0}", Target?.OwnedEntity.Name ?? "");
public string Description => Loc.GetString("Do it however you like, just make sure they don't last the shift.");
public SpriteSpecifier Icon => new SpriteSpecifier.Rsi(new ResourcePath("Objects/Weapons/Guns/Pistols/mk58_wood.rsi"), "icon");
public float Progress => Target?.OwnedEntity != null &&
Target.OwnedEntity
.TryGetComponent<IDamageableComponent>(out var damageableComponent) &&
damageableComponent.CurrentState == DamageState.Dead
? 1f
: 0f;
public float Difficulty => 2f;
public bool Equals(IObjectiveCondition? other)
{
return other is KillPersonCondition kpc && Equals(Target, kpc.Target);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((KillPersonCondition) obj);
}
public override int GetHashCode()
{
return Target?.GetHashCode() ?? 0;
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Linq;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Content.Shared.GameObjects.Components.Damage;
using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Random;
namespace Content.Server.Objectives.Conditions
{
[UsedImplicitly]
public class KillRandomPersonCondition : KillPersonCondition
{
public override IObjectiveCondition GetAssigned(Mind mind)
{
var entityMgr = IoCManager.Resolve<IEntityManager>();
var allHumans = entityMgr.ComponentManager.EntityQuery<MindComponent>().Where(mc =>
{
var entity = mc.Mind?.OwnedEntity;
return entity != null &&
entity.TryGetComponent<IDamageableComponent>(out var damageableComponent) &&
damageableComponent.CurrentState == DamageState.Alive
&& mc.Mind != mind;
}).Select(mc => mc.Mind).ToList();
return new KillRandomPersonCondition {Target = IoCManager.Resolve<IRobustRandom>().Pick(allHumans)};
}
}
}

View File

@@ -0,0 +1,53 @@
#nullable enable
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Content.Shared.GameObjects.Components.Damage;
using JetBrains.Annotations;
using Robust.Shared.Localization;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
{
[UsedImplicitly]
public class StayAliveCondition : IObjectiveCondition
{
private Mind? _mind;
public IObjectiveCondition GetAssigned(Mind mind)
{
return new StayAliveCondition {_mind = mind};
}
public string Title => Loc.GetString("Stay alive.");
public string Description => Loc.GetString("Survive this shift, we need you for another assignment.");
public SpriteSpecifier Icon => new SpriteSpecifier.Rsi(new ResourcePath("Objects/Misc/skub.rsi"), "icon"); //didn't know what else would have been a good icon for staying alive
public float Progress => _mind?.OwnedEntity != null &&
_mind.OwnedEntity.TryGetComponent<IDamageableComponent>(out var damageableComponent) &&
damageableComponent.CurrentState == DamageState.Dead
? 0f
: 1f;
public float Difficulty => 1f;
public bool Equals(IObjectiveCondition? other)
{
return other is StayAliveCondition sac && Equals(_mind, sac._mind);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((StayAliveCondition) obj);
}
public override int GetHashCode()
{
return (_mind != null ? _mind.GetHashCode() : 0);
}
}
}

View File

@@ -1,7 +1,9 @@
#nullable enable
using System;
using Content.Server.GameObjects.Components.ContainerExt;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using JetBrains.Annotations;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -13,45 +15,79 @@ using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
{
[UsedImplicitly]
public class StealCondition : IObjectiveCondition
{
public string PrototypeId { get; private set; } = default!;
public int Amount { get; private set; }
private Mind? _mind;
private string _prototypeId = default!;
private int _amount;
public IObjectiveCondition GetAssigned(Mind mind)
{
return new StealCondition
{
_mind = mind,
_prototypeId = _prototypeId,
_amount = _amount
};
}
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.PrototypeId, "prototype", "");
serializer.DataField(this, x => x.Amount, "amount", 1);
serializer.DataField(ref _prototypeId, "prototype", "");
serializer.DataField(ref _amount, "amount", 1);
if (Amount < 1)
if (_amount < 1)
{
Logger.Error("StealCondition has an amount less than 1 ({0})", Amount);
Logger.Error("StealCondition has an amount less than 1 ({0})", _amount);
}
}
private string PrototypeName =>
IoCManager.Resolve<IPrototypeManager>().TryIndex<EntityPrototype>(PrototypeId, out var prototype)
IoCManager.Resolve<IPrototypeManager>().TryIndex<EntityPrototype>(_prototypeId, out var prototype)
? prototype.Name
: "[CANNOT FIND NAME]";
public string GetTitle() => Loc.GetString("Steal {0} {1}", Amount > 1 ? $"{Amount}x" : "", Loc.GetString(PrototypeName));
public string Title => Loc.GetString("Steal {0}{1}", _amount > 1 ? $"{_amount}x " : "", Loc.GetString(PrototypeName));
public string GetDescription() => Loc.GetString("We need you to steal {0}. Don't get caught.", Loc.GetString(PrototypeName));
public string Description => Loc.GetString("We need you to steal {0}. Don't get caught.", Loc.GetString(PrototypeName));
public SpriteSpecifier GetIcon()
public SpriteSpecifier Icon => new SpriteSpecifier.EntityPrototype(_prototypeId);
public float Progress
{
return new SpriteSpecifier.EntityPrototype(PrototypeId);
get
{
if (_mind?.OwnedEntity == null) return 0f;
if (!_mind.OwnedEntity.TryGetComponent<ContainerManagerComponent>(out var containerManagerComponent)) return 0f;
float count = containerManagerComponent.CountPrototypeOccurencesRecursive(_prototypeId);
return count/_amount;
}
}
public float GetProgress(Mind? mind)
{
if (mind?.OwnedEntity == null) return 0f;
if (!mind.OwnedEntity.TryGetComponent<ContainerManagerComponent>(out var containerManagerComponent)) return 0f;
float count = containerManagerComponent.CountPrototypeOccurencesRecursive(PrototypeId);
return count/Amount;
public float Difficulty => 1f;
public bool Equals(IObjectiveCondition? other)
{
return other is StealCondition stealCondition &&
Equals(_mind, stealCondition._mind) &&
_prototypeId == stealCondition._prototypeId && _amount == stealCondition._amount;
}
public float GetDifficulty() => 1f;
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((StealCondition) obj);
}
public override int GetHashCode()
{
return HashCode.Combine(_mind, _prototypeId, _amount);
}
}
}

View File

@@ -1,35 +1,47 @@
using Content.Server.Mobs;
#nullable enable
using System;
using Content.Server.Mobs;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Interfaces
{
public interface IObjectiveCondition : IExposeData
public interface IObjectiveCondition : IExposeData, IEquatable<IObjectiveCondition>
{
/// <summary>
/// Returns a copy of the IObjectiveCondition which is assigned to the mind.
/// </summary>
/// <param name="mind">Mind to assign to.</param>
/// <returns>The new IObjectiveCondition.</returns>
IObjectiveCondition GetAssigned(Mind mind);
/// <summary>
/// Returns the title of the condition.
/// </summary>
string GetTitle();
string Title { get; }
/// <summary>
/// Returns the description of the condition.
/// </summary>
string GetDescription();
string Description { get; }
/// <summary>
/// Returns a SpriteSpecifier to be used as an icon for the condition.
/// </summary>
SpriteSpecifier GetIcon();
SpriteSpecifier Icon { get; }
/// <summary>
/// Returns the current progress of the condition in %.
/// Returns the current progress of the condition in % from 0 to 1.
/// </summary>
/// <returns>Current progress in %.</returns>
float GetProgress(Mind mind);
float Progress { get; }
/// <summary>
/// Returns a difficulty of the condition.
/// </summary>
float GetDifficulty();
float Difficulty { get; }
void IExposeData.ExposeData(ObjectSerializer serializer){}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Mobs;
using System.Collections.Generic;
using Content.Server.Mobs;
namespace Content.Server.Objectives.Interfaces
{
@@ -7,11 +8,11 @@ namespace Content.Server.Objectives.Interfaces
/// <summary>
/// Returns all objectives the provided mind is valid for.
/// </summary>
ObjectivePrototype[] GetAllPossibleObjectives(Mind mind);
IReadOnlyList<ObjectivePrototype> GetAllPossibleObjectives(Mind mind);
/// <summary>
/// Returns a randomly picked (no pop) collection of objectives the provided mind is valid for.
/// Returns a randomly picked objective the provided mind is valid for.
/// </summary>
ObjectivePrototype[] GetRandomObjectives(Mind mind, float maxDifficulty = 3f);
ObjectivePrototype GetRandomObjective(Mind mind);
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Robust.Shared.ViewVariables;
namespace Content.Server.Objectives
{
public class Objective : IEquatable<Objective>
{
[ViewVariables]
public readonly Mind Mind;
[ViewVariables]
public readonly ObjectivePrototype Prototype;
private readonly List<IObjectiveCondition> _conditions = new();
[ViewVariables]
public IReadOnlyList<IObjectiveCondition> Conditions => _conditions;
public Objective(ObjectivePrototype prototype, Mind mind)
{
Prototype = prototype;
Mind = mind;
foreach (var condition in prototype.Conditions)
{
_conditions.Add(condition.GetAssigned(mind));
}
}
public bool Equals(Objective other)
{
if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
if (!Equals(Mind, other.Mind) || !Equals(Prototype, other.Prototype)) return false;
if (_conditions.Count != other._conditions.Count) return false;
for (var i = 0; i < _conditions.Count; i++)
{
if (!_conditions[i].Equals(other._conditions[i])) return false;
}
return true;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((Objective) obj);
}
public override int GetHashCode()
{
return HashCode.Combine(Mind, Prototype, _conditions);
}
}
}

View File

@@ -15,23 +15,24 @@ namespace Content.Server.Objectives
[ViewVariables]
public string ID { get; private set; }
[ViewVariables(VVAccess.ReadWrite)]
[ViewVariables]
public string Issuer { get; private set; }
[ViewVariables]
public float Probability { get; private set; }
[ViewVariables]
public IReadOnlyList<IObjectiveCondition> Conditions => _conditions;
[ViewVariables]
public IReadOnlyList<IObjectiveRequirement> Requirements => _requirements;
[ViewVariables]
public float Difficulty => _difficultyOverride ?? _conditions.Sum(c => c.GetDifficulty());
public float Difficulty => _difficultyOverride ?? _conditions.Sum(c => c.Difficulty);
private List<IObjectiveCondition> _conditions = new();
private List<IObjectiveRequirement> _requirements = new();
[ViewVariables]
public IReadOnlyList<IObjectiveCondition> Conditions => _conditions;
[ViewVariables]
public bool CanBeDuplicateAssignment { get; private set; }
[ViewVariables(VVAccess.ReadWrite)]
private float? _difficultyOverride = null;
@@ -42,6 +43,14 @@ namespace Content.Server.Objectives
if (!requirement.CanBeAssigned(mind)) return false;
}
if (!CanBeDuplicateAssignment)
{
foreach (var objective in mind.AllObjectives)
{
if (objective.Prototype.ID == ID) return false;
}
}
return true;
}
@@ -52,9 +61,15 @@ namespace Content.Server.Objectives
ser.DataField(this, x => x.ID, "id", string.Empty);
ser.DataField(this, x => x.Issuer, "issuer", "Unknown");
ser.DataField(this, x => x.Probability, "prob", 0.3f);
ser.DataField(this, x => x._conditions, "conditions", new List<IObjectiveCondition>());
ser.DataField(this, x => x._requirements, "requirements", new List<IObjectiveRequirement>());
ser.DataField(this, x => x._difficultyOverride, "difficultyOverride", null);
ser.DataField(ref _conditions, "conditions", new List<IObjectiveCondition>());
ser.DataField(ref _requirements, "requirements", new List<IObjectiveRequirement>());
ser.DataField(ref _difficultyOverride, "difficultyOverride", null);
ser.DataField(this, x => x.CanBeDuplicateAssignment, "canBeDuplicate", false);
}
public Objective GetObjective(Mind mind)
{
return new(this, mind);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
@@ -15,39 +16,24 @@ namespace Content.Server.Objectives
[Dependency] private IPrototypeManager _prototypeManager = default!;
[Dependency] private IRobustRandom _random = default!;
public ObjectivePrototype[] GetAllPossibleObjectives(Mind mind)
public List<ObjectivePrototype> GetAllPossibleObjectives(Mind mind)
{
return _prototypeManager.EnumeratePrototypes<ObjectivePrototype>().Where(objectivePrototype => objectivePrototype.CanBeAssigned(mind)).ToArray();
return _prototypeManager.EnumeratePrototypes<ObjectivePrototype>().Where(objectivePrototype => objectivePrototype.CanBeAssigned(mind)).ToList();
}
public ObjectivePrototype[] GetRandomObjectives(Mind mind, float maxDifficulty = 3)
public ObjectivePrototype? GetRandomObjective(Mind mind)
{
var objectives = GetAllPossibleObjectives(mind);
_random.Shuffle(objectives);
//to prevent endless loops
if(objectives.Length == 0 || objectives.Sum(o => o.Difficulty) == 0f) return objectives;
var result = new List<ObjectivePrototype>();
var currentDifficulty = 0f;
_random.Shuffle(objectives);
while (currentDifficulty < maxDifficulty)
foreach (var objective in objectives)
{
foreach (var objective in objectives)
{
if (!_random.Prob(objective.Probability)) continue;
result.Add(objective);
currentDifficulty += objective.Difficulty;
if (currentDifficulty >= maxDifficulty) break;
}
if (!_random.Prob(objective.Probability)) continue;
return objective;
}
if (currentDifficulty > maxDifficulty) //will almost always happen
{
result.Pop();
}
return result.ToArray();
return null;
}
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Robust.Shared.Serialization;
namespace Content.Server.Objectives.Requirements
{
public class IncompatibleConditionsRequirement : IObjectiveRequirement
{
private List<string> _incompatibleConditions = new();
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x=>x._incompatibleConditions, "conditions", new List<string>());
}
public bool CanBeAssigned(Mind mind)
{
foreach (var objective in mind.AllObjectives)
{
foreach (var condition in objective.Conditions)
{
foreach (var incompatibleCondition in _incompatibleConditions)
{
if (incompatibleCondition == condition.GetType().Name) return false;
}
}
}
return true;
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Content.Server.Mobs;
using Content.Server.Objectives.Interfaces;
using Robust.Shared.Serialization;
namespace Content.Server.Objectives.Requirements
{
public class IncompatibleObjectivesRequirement : IObjectiveRequirement
{
private List<string> _incompatibleObjectives = new();
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x=>x._incompatibleObjectives, "objectives", new List<string>());
}
public bool CanBeAssigned(Mind mind)
{
foreach (var objective in mind.AllObjectives)
{
foreach (var incompatibleObjective in _incompatibleObjectives)
{
if (incompatibleObjective == objective.Prototype.ID) return false;
}
}
return true;
}
}
}

View File

@@ -1,17 +1,19 @@
using Content.Server.Mobs;
using Content.Server.Mobs.Roles.Suspicion;
using Content.Server.Mobs.Roles.Traitor;
using Content.Server.Objectives.Interfaces;
using JetBrains.Annotations;
using Robust.Shared.Serialization;
namespace Content.Server.Objectives.Requirements
{
public class SuspicionTraitorRequirement : IObjectiveRequirement
[UsedImplicitly]
public class TraitorRequirement : IObjectiveRequirement
{
public void ExposeData(ObjectSerializer serializer){}
public bool CanBeAssigned(Mind mind)
{
return mind.HasRole<SuspicionTraitorRole>();
return mind.HasRole<TraitorRole>();
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using YamlDotNet.RepresentationModel;
namespace Content.Server.Prototypes
{
[Prototype("dataset")]
public class DatasetPrototype : IPrototype, IIndexedPrototype
{
private string _id;
public string ID => _id;
private List<string> _values;
public IReadOnlyList<string> Values => _values;
public void LoadFrom(YamlMappingNode mapping)
{
var ser = YamlObjectSerializer.NewReader(mapping);
ser.DataField(ref _id, "id", "");
ser.DataField(ref _values, "values", new List<string>());
}
}
}

View File

@@ -55,21 +55,50 @@ namespace Content.Shared
public static readonly CVarDef<bool>
GamePersistGuests = CVarDef.Create("game.persistguests", true, CVar.ARCHIVE | CVar.SERVERONLY);
public static readonly CVarDef<int> GameSuspicionMinPlayers =
CVarDef.Create("game.suspicion_min_players", 5);
public static readonly CVarDef<int> GameSuspicionMinTraitors =
CVarDef.Create("game.suspicion_min_traitors", 2);
public static readonly CVarDef<int> GameSuspicionPlayersPerTraitor =
CVarDef.Create("game.suspicion_players_per_traitor", 5);
public static readonly CVarDef<int> GameSuspicionStartingBalance =
CVarDef.Create("game.suspicion_starting_balance", 20);
public static readonly CVarDef<bool> GameDiagonalMovement =
CVarDef.Create("game.diagonalmovement", true, CVar.ARCHIVE);
/*
* Suspicion
*/
public static readonly CVarDef<int> SuspicionMinPlayers =
CVarDef.Create("suspicion.min_players", 5);
public static readonly CVarDef<int> SuspicionMinTraitors =
CVarDef.Create("suspicion.min_traitors", 2);
public static readonly CVarDef<int> SuspicionPlayersPerTraitor =
CVarDef.Create("suspicion.players_per_traitor", 5);
public static readonly CVarDef<int> SuspicionStartingBalance =
CVarDef.Create("suspicion.starting_balance", 20);
/*
* Traitor
*/
public static readonly CVarDef<int> TraitorMinPlayers =
CVarDef.Create("traitor.min_players", 5);
public static readonly CVarDef<int> TraitorMaxTraitors =
CVarDef.Create("traitor.max_traitors", 4);
public static readonly CVarDef<int> TraitorPlayersPerTraitor =
CVarDef.Create("traitor.players_per_traitor", 5);
public static readonly CVarDef<int> TraitorCodewordCount =
CVarDef.Create("traitor.codeword_count", 4);
public static readonly CVarDef<int> TraitorStartingBalance =
CVarDef.Create("traitor.starting_balance", 20);
public static readonly CVarDef<int> TraitorMaxDifficulty =
CVarDef.Create("traitor.max_difficulty", 4);
public static readonly CVarDef<int> TraitorMaxPicks =
CVarDef.Create("traitor.max_picks", 20);
/*
* Console
*/

View File

@@ -322,7 +322,6 @@ namespace Content.Shared.GameTicking
AllPlayersEndInfo.Add(readPlayerData);
}
}
public override void WriteToBuffer(NetOutgoingMessage buffer)

View File

@@ -0,0 +1,400 @@
- type: dataset
id: adjectives
values:
- adorable
- adventurous
- aggressive
- alert
- attractive
- average
- beautiful
- blue-eyed
- bloody
- blushing
- bright
- clean
- clear
- cloudy
- colorful
- crowded
- cute
- dark
- drab
- distinct
- dull
- elegant
- excited
- fancy
- filthy
- glamorous
- gleaming
- gorgeous
- graceful
- grotesque
- handsome
- homely
- light
- long
- magnificent
- misty
- motionless
- muddy
- old-fashioned
- plain
- poised
- precious
- quaint
- shiny
- smoggy
- sparkling
- spotless
- stormy
- strange
- ugly
- ugliest
- unsightly
- unusual
- wide-eyed
- alive
- annoying
- bad
- better
- beautiful
- brainy
- breakable
- busy
- careful
- cautious
- clever
- clumsy
- concerned
- crazy
- curious
- dead
- different
- difficult
- doubtful
- easy
- expensive
- famous
- fragile
- frail
- gifted
- helpful
- helpless
- horrible
- important
- impossible
- inexpensive
- innocent
- inquisitive
- modern
- mushy
- odd
- open
- outstanding
- poor
- powerful
- prickly
- puzzled
- real
- rich
- shy
- sleepy
- stupid
- super
- talented
- tame
- tender
- tough
- uninterested
- vast
- wandering
- wild
- wrong
- angry
- annoyed
- anxious
- arrogant
- ashamed
- awful
- bad
- bewildered
- black
- blue
- bored
- clumsy
- combative
- condemned
- confused
- crazy,flipped-out
- creepy
- cruel
- dangerous
- defeated
- defiant
- depressed
- disgusted
- disturbed
- dizzy
- dull
- embarrassed
- envious
- evil
- fierce
- foolish
- frantic
- frightened
- grieving
- grumpy
- helpless
- homeless
- hungry
- hurt
- ill
- itchy
- jealous
- jittery
- lazy
- lonely
- mysterious
- nasty
- naughty
- nervous
- nutty
- obnoxious
- outrageous
- panicky
- repulsive
- scary
- selfish
- sore
- tense
- terrible
- testy
- thoughtless
- tired
- troubled
- upset
- uptight
- weary
- wicked
- worried
- agreeable
- amused
- brave
- calm
- charming
- cheerful
- comfortable
- cooperative
- courageous
- delightful
- determined
- eager
- elated
- enchanting
- encouraging
- energetic
- enthusiastic
- excited
- exuberant
- fair
- faithful
- fantastic
- fine
- friendly
- funny
- gentle
- glorious
- good
- happy
- healthy
- helpful
- hilarious
- jolly
- joyous
- kind
- lively
- lovely
- lucky
- nice
- obedient
- perfect
- pleasant
- proud
- relieved
- silly
- smiling
- splendid
- successful
- thankful
- thoughtful
- victorious
- vivacious
- witty
- wonderful
- zealous
- zany
- broad
- chubby
- crooked
- curved
- deep
- flat
- high
- hollow
- low
- narrow
- round
- shallow
- skinny
- square
- steep
- straight
- wide
- big
- colossal
- fat
- gigantic
- great
- huge
- immense
- large
- little
- mammoth
- massive
- miniature
- petite
- puny
- scrawny
- short
- small
- tall
- teeny
- teeny-tiny
- tiny
- cooing
- deafening
- faint
- harsh
- high-pitched
- hissing
- hushed
- husky
- loud
- melodic
- moaning
- mute
- noisy
- purring
- quiet
- raspy
- resonant
- screeching
- shrill
- silent
- soft
- squealing
- thundering
- voiceless
- whispering
- ancient
- brief
- early
- fast
- late
- long
- modern
- old
- old-fashioned
- quick
- rapid
- short
- slow
- swift
- young
- Taste/Touch
- bitter
- delicious
- fresh
- juicy
- ripe
- rotten
- salty
- sour
- spicy
- stale
- sticky
- strong
- sweet
- tart
- tasteless
- tasty
- thirsty
- fluttering
- fuzzy
- greasy
- grubby
- hard
- hot
- icy
- loose
- melted
- nutritious
- plastic
- prickly
- rainy
- rough
- scattered
- shaggy
- shaky
- sharp
- shivering
- silky
- slimy
- slippery
- smooth
- soft
- solid
- steady
- sticky
- tender
- tight
- uneven
- weak
- wet
- wooden
- yummy
- boiling
- breezy
- broken
- bumpy
- chilly
- cold
- cool
- creepy
- crooked
- cuddly
- curly
- damaged
- damp
- dirty
- dry
- dusty
- filthy
- flaky
- fluffy
- freezing
- hot
- warm
- wet
- abundant
- empty
- few
- heavy
- light
- many
- numerous
- substantial
- capitalist

View File

@@ -0,0 +1,637 @@
- type: dataset
id: verbs
values:
- accept
- add
- admire
- admit
- advise
- afford
- agree
- alert
- allow
- amuse
- analyse
- announce
- annoy
- answer
- apologise
- appear
- applaud
- appreciate
- approve
- argue
- arrange
- arrest
- arrive
- ask
- attach
- attack
- attempt
- attend
- attract
- avoid
- back
- bake
- balance
- ban
- bang
- bare
- bat
- bathe
- battle
- beam
- beg
- behave
- belong
- bleach
- bless
- blind
- blink
- blot
- blush
- boast
- boil
- bolt
- bomb
- book
- bore
- borrow
- bounce
- bow
- box
- brake
- brake
- branch
- breathe
- bruise
- brush
- bubble
- bump
- burn
- bury
- buzz
- calculate
- call
- camp
- care
- carry
- carve
- cause
- challenge
- change
- charge
- chase
- cheat
- check
- cheer
- chew
- choke
- chop
- claim
- clap
- clean
- clear
- clip
- close
- coach
- coil
- collect
- colour
- comb
- command
- communicate
- compare
- compete
- complain
- complete
- concentrate
- concern
- confess
- confuse
- connect
- consider
- consist
- contain
- continue
- copy
- correct
- cough
- count
- cover
- crack
- crash
- crawl
- cross
- crush
- cry
- cure
- curl
- curve
- cycle
- dam
- damage
- dance
- dare
- decay
- deceive
- decide
- decorate
- delay
- delight
- deliver
- depend
- describe
- desert
- deserve
- destroy
- detect
- develop
- disagree
- disappear
- disapprove
- disarm
- discover
- dislike
- divide
- double
- doubt
- drag
- drain
- dream
- dress
- drip
- drop
- drown
- drum
- dry
- dust
- earn
- educate
- embarrass
- employ
- empty
- encourage
- end
- enjoy
- enter
- entertain
- escape
- examine
- excite
- excuse
- exercise
- exist
- expand
- expect
- explain
- explode
- extend
- face
- fade
- fail
- fancy
- fasten
- fax
- fear
- fence
- fetch
- file
- fill
- film
- fire
- fit
- fix
- flap
- flash
- float
- flood
- flow
- flower
- fold
- follow
- fool
- force
- form
- found
- frame
- frighten
- fry
- gather
- gaze
- glow
- glue
- grab
- grate
- grease
- greet
- grin
- grip
- groan
- guarantee
- guard
- guess
- guide
- hammer
- hand
- handle
- hang
- happen
- harass
- harm
- hate
- haunt
- head
- heal
- heap
- heat
- help
- hook
- hop
- hope
- hover
- hug
- hum
- hunt
- hurry
- identify
- ignore
- imagine
- impress
- improve
- include
- increase
- influence
- inform
- inject
- injure
- instruct
- intend
- interest
- interfere
- interrupt
- introduce
- invent
- invite
- irritate
- itch
- jail
- jam
- jog
- join
- joke
- judge
- juggle
- jump
- kick
- kill
- kiss
- kneel
- knit
- knock
- knot
- label
- land
- last
- laugh
- launch
- learn
- level
- license
- lick
- lie
- lighten
- like
- list
- listen
- live
- load
- lock
- long
- look
- love
- man
- manage
- march
- mark
- marry
- match
- mate
- matter
- measure
- meddle
- melt
- memorise
- mend
- messup
- milk
- mine
- miss
- mix
- moan
- moor
- mourn
- move
- muddle
- mug
- multiply
- murder
- nail
- name
- need
- nest
- nod
- note
- notice
- number
- obey
- object
- observe
- obtain
- occur
- offend
- offer
- open
- order
- overflow
- owe
- own
- pack
- paddle
- paint
- park
- part
- pass
- paste
- pat
- pause
- peck
- pedal
- peel
- peep
- perform
- permit
- phone
- pick
- pinch
- pine
- place
- plan
- plant
- play
- please
- plug
- point
- poke
- polish
- pop
- possess
- post
- pour
- practise
- pray
- preach
- precede
- prefer
- prepare
- present
- preserve
- press
- pretend
- prevent
- prick
- print
- produce
- program
- promise
- protect
- provide
- pull
- pump
- punch
- puncture
- punish
- push
- question
- queue
- race
- radiate
- rain
- raise
- reach
- realise
- receive
- recognise
- record
- reduce
- reflect
- refuse
- regret
- reign
- reject
- rejoice
- relax
- release
- rely
- remain
- remember
- remind
- remove
- repair
- repeat
- replace
- reply
- report
- reproduce
- request
- rescue
- retire
- return
- rhyme
- rinse
- risk
- rob
- rock
- roll
- rot
- rub
- ruin
- rule
- rush
- sack
- sail
- satisfy
- save
- saw
- scare
- scatter
- scold
- scorch
- scrape
- scratch
- scream
- screw
- scribble
- scrub
- seal
- search
- separate
- serve
- settle
- shade
- share
- shave
- shelter
- shiver
- shock
- shop
- shriek
- shrug
- sigh
- sign
- signal
- sin
- sip
- ski
- skip
- slap
- slip
- slow
- smash
- smell
- smile
- smoke
- snatch
- sneeze
- sniff
- snore
- snow
- soak
- soothe
- sound
- spare
- spark
- sparkle
- spell
- spill
- spoil
- spot
- spray
- sprout
- squash
- squeak
- squeal
- squeeze
- stain
- stamp
- stare
- start
- stay
- steer
- step
- stir
- stitch
- stop
- store
- strap
- strengthen
- stretch
- strip
- stroke
- stuff
- subtract
- succeed
- suck
- suffer
- suggest
- suit
- supply
- support
- suppose
- surprise
- surround
- suspect
- suspend
- switch
- talk
- tame
- tap
- taste
- tease
- telephone
- tempt
- terrify
- test
- thank
- thaw
- tick
- tickle
- tie
- time
- tip
- tire
- touch
- tour
- tow
- trace
- trade
- train
- transport
- trap
- travel
- treat
- tremble
- trick
- trip
- trot
- trouble
- trust
- try
- tug
- tumble
- turn
- twist
- type
- undress
- unfasten
- unite
- unlock
- unpack
- untidy
- use
- vanish
- visit
- wail
- wait
- walk
- wander
- want
- warm
- warn
- wash
- waste
- watch
- water
- wave
- weigh
- welcome
- whine
- whip
- whirl
- whisper
- whistle
- wink
- wipe
- wish
- wobble
- wonder
- work
- worry
- wrap
- wreck
- wrestle
- wriggle
- yawn
- yell
- zip
- zoom

View File

@@ -1,9 +1,42 @@
- type: objective
id: SoapDeluxeStealObjective
prob: 0.3
id: CaptainIDStealObjective
issuer: The Syndicate
requirements:
- !type:SuspicionTraitorRequirement {}
- !type:TraitorRequirement {}
- !type:IncompatibleConditionsRequirement
conditions:
- DieCondition
conditions:
- !type:StealCondition
prototype: SoapDeluxe
prototype: CaptainIDCard
- type: objective
id: KillRandomObjective
issuer: The Syndicate
requirements:
- !type:TraitorRequirement {}
conditions:
- !type:KillRandomPersonCondition {}
canBeDuplicate: true
- type: objective
id: StayAliveObjective
issuer: The Syndicate
requirements:
- !type:TraitorRequirement {}
- !type:IncompatibleConditionsRequirement
conditions:
- DieCondition
conditions:
- !type:StayAliveCondition {}
- type: objective
id: DieObjective
issuer: The Syndicate
requirements:
- !type:TraitorRequirement {}
- !type:IncompatibleConditionsRequirement
conditions:
- StayAliveCondition
conditions:
- !type:DieCondition {}