* 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

@@ -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>();
}
}
}