Antag preferences and antag prototype (#1264)

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
ike709
2020-07-06 16:24:29 -05:00
committed by GitHub
parent cee8aaa84c
commit c019d428a7
42 changed files with 833 additions and 50 deletions

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.GameObjects.Components; using Content.Client.GameObjects.Components;
@@ -6,6 +6,7 @@ using Content.Client.Interfaces;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared; using Content.Shared;
using Content.Shared.Jobs; using Content.Shared.Jobs;
using Content.Shared.Antags;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Robust.Client.Graphics.Drawing; using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -42,6 +43,7 @@ namespace Content.Client.UserInterface
private readonly FacialHairStylePicker _facialHairPicker; private readonly FacialHairStylePicker _facialHairPicker;
private readonly List<JobPrioritySelector> _jobPriorities; private readonly List<JobPrioritySelector> _jobPriorities;
private readonly OptionButton _preferenceUnavailableButton; private readonly OptionButton _preferenceUnavailableButton;
private readonly List<AntagPreferenceSelector> _antagPreferences;
private bool _isDirty; private bool _isDirty;
public int CharacterSlot; public int CharacterSlot;
@@ -320,6 +322,52 @@ namespace Content.Client.UserInterface
#endregion #endregion
#region Antags
{
var antagList = new VBoxContainer();
var antagVBox = new VBoxContainer
{
Children =
{
new ScrollContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
Children =
{
antagList
}
}
}
};
tabContainer.AddChild(antagVBox);
tabContainer.SetTabTitle(2, Loc.GetString("Antags"));
_antagPreferences = new List<AntagPreferenceSelector>();
foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => a.Name))
{
if(!antag.SetPreference)
{
continue;
}
var selector = new AntagPreferenceSelector(antag);
antagList.AddChild(selector);
_antagPreferences.Add(selector);
selector.PreferenceChanged += preference =>
{
Profile = Profile.WithAntagPreference(antag.ID, preference);
IsDirty = true;
};
}
}
#endregion
var rightColumn = new VBoxContainer(); var rightColumn = new VBoxContainer();
middleContainer.AddChild(rightColumn); middleContainer.AddChild(rightColumn);
@@ -466,6 +514,7 @@ namespace Content.Client.UserInterface
UpdateHairPickers(); UpdateHairPickers();
UpdateSaveButton(); UpdateSaveButton();
UpdateJobPriorities(); UpdateJobPriorities();
UpdateAntagPreferences();
_preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable); _preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
} }
@@ -533,5 +582,51 @@ namespace Content.Client.UserInterface
}); });
} }
} }
private void UpdateAntagPreferences()
{
foreach (var preferenceSelector in _antagPreferences)
{
var antagId = preferenceSelector.Antag.ID;
var preference = Profile.AntagPreferences.Contains(antagId);
preferenceSelector.Preference = preference;
}
}
private class AntagPreferenceSelector : Control
{
public AntagPrototype Antag { get; }
private readonly CheckBox _checkBox;
public bool Preference
{
get => _checkBox.Pressed;
set => _checkBox.Pressed = value;
}
public event Action<bool> PreferenceChanged;
public AntagPreferenceSelector(AntagPrototype antag)
{
Antag = antag;
_checkBox = new CheckBox {Text = $"{antag.Name}"};
_checkBox.OnToggled += OnCheckBoxToggled;
AddChild(new HBoxContainer
{
Children =
{
_checkBox
}
});
}
private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
{
PreferenceChanged?.Invoke(Preference);
}
}
} }
} }

View File

@@ -0,0 +1,185 @@
// <auto-generated />
using Content.Server.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Content.Server.Database.Migrations.Postgres
{
[DbContext(typeof(PostgresPreferencesDbContext))]
[Migration("20200706172726_Antags")]
partial class Antags
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn)
.HasAnnotation("ProductVersion", "3.1.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("AntagId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("HumanoidProfileId")
.HasColumnType("integer");
b.HasKey("AntagId");
b.HasIndex("HumanoidProfileId", "AntagName")
.IsUnique();
b.ToTable("Antag");
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{
b.Property<int>("HumanoidProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<int>("Age")
.HasColumnType("integer");
b.Property<string>("CharacterName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("EyeColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("FacialHairColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("FacialHairName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("HairColor")
.IsRequired()
.HasColumnType("text");
b.Property<string>("HairName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PreferenceUnavailable")
.HasColumnType("integer");
b.Property<int>("PrefsId")
.HasColumnType("integer");
b.Property<string>("Sex")
.IsRequired()
.HasColumnType("text");
b.Property<string>("SkinColor")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Slot")
.HasColumnType("integer");
b.Property<string>("SlotName")
.IsRequired()
.HasColumnType("text");
b.HasKey("HumanoidProfileId");
b.HasIndex("PrefsId");
b.HasIndex("Slot", "PrefsId")
.IsUnique();
b.ToTable("HumanoidProfile");
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.Property<int>("JobId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("Priority")
.HasColumnType("integer");
b.Property<int>("ProfileHumanoidProfileId")
.HasColumnType("integer");
b.HasKey("JobId");
b.HasIndex("ProfileHumanoidProfileId");
b.ToTable("Job");
});
modelBuilder.Entity("Content.Server.Database.Prefs", b =>
{
b.Property<int>("PrefsId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<int>("SelectedCharacterSlot")
.HasColumnType("integer");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("text");
b.HasKey("PrefsId");
b.HasIndex("Username")
.IsUnique();
b.ToTable("Preferences");
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Antags")
.HasForeignKey("HumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{
b.HasOne("Content.Server.Database.Prefs", "Prefs")
.WithMany("HumanoidProfiles")
.HasForeignKey("PrefsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Jobs")
.HasForeignKey("ProfileHumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Content.Server.Database.Migrations.Postgres
{
public partial class Antags : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Antag",
columns: table => new
{
AntagId = table.Column<int>(nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
HumanoidProfileId = table.Column<int>(nullable: false),
AntagName = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Antag", x => x.AntagId);
table.ForeignKey(
name: "FK_Antag_HumanoidProfile_HumanoidProfileId",
column: x => x.HumanoidProfileId,
principalTable: "HumanoidProfile",
principalColumn: "HumanoidProfileId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Antag_HumanoidProfileId_AntagName",
table: "Antag",
columns: new[] { "HumanoidProfileId", "AntagName" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Antag");
}
}
}

View File

@@ -18,6 +18,28 @@ namespace Content.Server.Database.Migrations.Postgres
.HasAnnotation("ProductVersion", "3.1.4") .HasAnnotation("ProductVersion", "3.1.4")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("AntagId")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("HumanoidProfileId")
.HasColumnType("integer");
b.HasKey("AntagId");
b.HasIndex("HumanoidProfileId", "AntagName")
.IsUnique();
b.ToTable("Antag");
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b => modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{ {
b.Property<int>("HumanoidProfileId") b.Property<int>("HumanoidProfileId")
@@ -129,6 +151,15 @@ namespace Content.Server.Database.Migrations.Postgres
b.ToTable("Preferences"); b.ToTable("Preferences");
}); });
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Antags")
.HasForeignKey("HumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b => modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{ {
b.HasOne("Content.Server.Database.Prefs", "Prefs") b.HasOne("Content.Server.Database.Prefs", "Prefs")

View File

@@ -0,0 +1,178 @@
// <auto-generated />
using Content.Server.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Content.Server.Database.Migrations.Sqlite
{
[DbContext(typeof(SqlitePreferencesDbContext))]
[Migration("20200706172741_Antags")]
partial class Antags
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.4");
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("AntagId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("HumanoidProfileId")
.HasColumnType("INTEGER");
b.HasKey("AntagId");
b.HasIndex("HumanoidProfileId", "AntagName")
.IsUnique();
b.ToTable("Antag");
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{
b.Property<int>("HumanoidProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("Age")
.HasColumnType("INTEGER");
b.Property<string>("CharacterName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("EyeColor")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("FacialHairColor")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("FacialHairName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("HairColor")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("HairName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("PreferenceUnavailable")
.HasColumnType("INTEGER");
b.Property<int>("PrefsId")
.HasColumnType("INTEGER");
b.Property<string>("Sex")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("SkinColor")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Slot")
.HasColumnType("INTEGER");
b.Property<string>("SlotName")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("HumanoidProfileId");
b.HasIndex("PrefsId");
b.HasIndex("Slot", "PrefsId")
.IsUnique();
b.ToTable("HumanoidProfile");
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.Property<int>("JobId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Priority")
.HasColumnType("INTEGER");
b.Property<int>("ProfileHumanoidProfileId")
.HasColumnType("INTEGER");
b.HasKey("JobId");
b.HasIndex("ProfileHumanoidProfileId");
b.ToTable("Job");
});
modelBuilder.Entity("Content.Server.Database.Prefs", b =>
{
b.Property<int>("PrefsId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("SelectedCharacterSlot")
.HasColumnType("INTEGER");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("PrefsId");
b.HasIndex("Username")
.IsUnique();
b.ToTable("Preferences");
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Antags")
.HasForeignKey("HumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{
b.HasOne("Content.Server.Database.Prefs", "Prefs")
.WithMany("HumanoidProfiles")
.HasForeignKey("PrefsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Jobs")
.HasForeignKey("ProfileHumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Content.Server.Database.Migrations.Sqlite
{
public partial class Antags : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Antag",
columns: table => new
{
AntagId = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
HumanoidProfileId = table.Column<int>(nullable: false),
AntagName = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Antag", x => x.AntagId);
table.ForeignKey(
name: "FK_Antag_HumanoidProfile_HumanoidProfileId",
column: x => x.HumanoidProfileId,
principalTable: "HumanoidProfile",
principalColumn: "HumanoidProfileId",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Antag_HumanoidProfileId_AntagName",
table: "Antag",
columns: new[] { "HumanoidProfileId", "AntagName" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Antag");
}
}
}

View File

@@ -15,6 +15,27 @@ namespace Content.Server.Database.Migrations
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "3.1.4"); .HasAnnotation("ProductVersion", "3.1.4");
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("AntagId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("HumanoidProfileId")
.HasColumnType("INTEGER");
b.HasKey("AntagId");
b.HasIndex("HumanoidProfileId", "AntagName")
.IsUnique();
b.ToTable("Antag");
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b => modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{ {
b.Property<int>("HumanoidProfileId") b.Property<int>("HumanoidProfileId")
@@ -123,6 +144,15 @@ namespace Content.Server.Database.Migrations
b.ToTable("Preferences"); b.ToTable("Preferences");
}); });
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.HumanoidProfile", "Profile")
.WithMany("Antags")
.HasForeignKey("HumanoidProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b => modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
{ {
b.HasOne("Content.Server.Database.Prefs", "Prefs") b.HasOne("Content.Server.Database.Prefs", "Prefs")

View File

@@ -64,6 +64,10 @@ namespace Content.Server.Database
modelBuilder.Entity<HumanoidProfile>() modelBuilder.Entity<HumanoidProfile>()
.HasIndex(p => new {p.Slot, p.PrefsId}) .HasIndex(p => new {p.Slot, p.PrefsId})
.IsUnique(); .IsUnique();
modelBuilder.Entity<Antag>()
.HasIndex(p => new {p.HumanoidProfileId , p.AntagName})
.IsUnique();
} }
} }
@@ -90,6 +94,7 @@ namespace Content.Server.Database
public string EyeColor { get; set; } = null!; public string EyeColor { get; set; } = null!;
public string SkinColor { get; set; } = null!; public string SkinColor { get; set; } = null!;
public List<Job> Jobs { get; } = new List<Job>(); public List<Job> Jobs { get; } = new List<Job>();
public List<Antag> Antags { get; } = new List<Antag>();
public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; } public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; }
public int PrefsId { get; set; } public int PrefsId { get; set; }
@@ -114,6 +119,15 @@ namespace Content.Server.Database
High = 3 High = 3
} }
public class Antag
{
public int AntagId { get; set; }
public HumanoidProfile Profile { get; set; } = null!;
public int HumanoidProfileId { get; set; }
public string AntagName { get; set; } = null!;
}
public enum DbPreferenceUnavailableMode public enum DbPreferenceUnavailableMode
{ {
// These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared. // These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared.

View File

@@ -26,8 +26,8 @@ namespace Content.Server.Database
{ {
return await _prefsCtx return await _prefsCtx
.Preferences .Preferences
.Include(p => p.HumanoidProfiles) .Include(p => p.HumanoidProfiles).ThenInclude(h => h.Jobs)
.ThenInclude(h => h.Jobs) .Include(p => p.HumanoidProfiles).ThenInclude(h => h.Antags)
.SingleOrDefaultAsync(p => p.Username == username); .SingleOrDefaultAsync(p => p.Username == username);
} }
@@ -72,6 +72,7 @@ namespace Content.Server.Database
{ {
return await _prefsCtx.HumanoidProfile return await _prefsCtx.HumanoidProfile
.Include(p => p.Jobs) .Include(p => p.Jobs)
.Include(a => a.Antags)
.Join(_prefsCtx.Preferences, .Join(_prefsCtx.Preferences,
profile => new {profile.Slot, profile.PrefsId}, profile => new {profile.Slot, profile.PrefsId},
prefs => new {Slot = prefs.SelectedCharacterSlot, prefs.PrefsId}, prefs => new {Slot = prefs.SelectedCharacterSlot, prefs.PrefsId},

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Content.Shared.Preferences;
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
@@ -11,5 +12,6 @@ namespace Content.Server.GameTicking
public abstract bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false); public abstract bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false);
public virtual string ModeTitle => "Sandbox"; public virtual string ModeTitle => "Sandbox";
public virtual string Description => "Secret!"; public virtual string Description => "Secret!";
public Dictionary<string, HumanoidCharacterProfile> readyProfiles;
} }
} }

View File

@@ -1,17 +1,23 @@
using System; using Content.Server.GameTicking.GameRules;
using System.Collections.Generic; using Content.Server.Interfaces;
using Content.Server.GameTicking.GameRules;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Content.Server.Interfaces.GameTicking; using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs.Roles; using Content.Server.Mobs.Roles;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Sandbox; using Content.Shared.Antags;
using NFluidsynth;
using Robust.Server.Interfaces.Player; using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Logger = Robust.Shared.Log.Logger; using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Log;
using System.Threading.Tasks;
using Content.Shared.Preferences;
namespace Content.Server.GameTicking.GamePresets namespace Content.Server.GameTicking.GamePresets
{ {
@@ -21,11 +27,14 @@ namespace Content.Server.GameTicking.GamePresets
[Dependency] private readonly IChatManager _chatManager; [Dependency] private readonly IChatManager _chatManager;
[Dependency] private readonly IGameTicker _gameTicker; [Dependency] private readonly IGameTicker _gameTicker;
[Dependency] private readonly IRobustRandom _random; [Dependency] private readonly IRobustRandom _random;
[Dependency] private IPrototypeManager _prototypeManager;
#pragma warning restore 649 #pragma warning restore 649
public int MinPlayers { get; set; } = 5; public int MinPlayers { get; set; } = 5;
public int MinTraitors { get; set; } = 2; public int MinTraitors { get; set; } = 2;
public int PlayersPerTraitor { get; set; } = 5; public int PlayersPerTraitor { get; set; } = 5;
private static string TraitorID = "SuspicionTraitor";
private static string InnocentID = "SuspicionInnocent";
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false) public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
{ {
@@ -36,20 +45,48 @@ namespace Content.Server.GameTicking.GamePresets
} }
var list = new List<IPlayerSession>(readyPlayers); var list = new List<IPlayerSession>(readyPlayers);
var prefList = new List<IPlayerSession>();
foreach (var player in list)
{
if (!readyProfiles.ContainsKey(player.Name))
{
continue;
}
var profile = readyProfiles[player.Name];
if (profile.AntagPreferences.Contains(_prototypeManager.Index<AntagPrototype>(TraitorID).Name))
{
prefList.Add(player);
}
}
var numTraitors = Math.Clamp(readyPlayers.Count % PlayersPerTraitor, var numTraitors = Math.Clamp(readyPlayers.Count % PlayersPerTraitor,
MinTraitors, readyPlayers.Count); MinTraitors, readyPlayers.Count);
for (var i = 0; i < numTraitors; i++) for (var i = 0; i < numTraitors; i++)
{ {
var traitor = _random.PickAndTake(list); IPlayerSession traitor;
if(prefList.Count() == 0)
{
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 mind = traitor.Data.ContentData().Mind;
mind.AddRole(new SuspicionTraitorRole(mind)); var antagPrototype = _prototypeManager.Index<AntagPrototype>(TraitorID);
mind.AddRole(new SuspicionTraitorRole(mind, antagPrototype));
} }
foreach (var player in list) foreach (var player in list)
{ {
var mind = player.Data.ContentData().Mind; var mind = player.Data.ContentData().Mind;
mind.AddRole(new SuspicionInnocentRole(mind)); var antagPrototype = _prototypeManager.Index<AntagPrototype>(InnocentID);
mind.AddRole(new SuspicionInnocentRole(mind, antagPrototype));
} }
_gameTicker.AddGameRule<RuleSuspicion>(); _gameTicker.AddGameRule<RuleSuspicion>();

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Threading; using System.Threading;
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Mobs;
@@ -38,8 +39,6 @@ namespace Content.Server.GameTicking.GameRules
public override void Added() public override void Added()
{ {
_chatManager.DispatchServerAnnouncement("There are traitors on the station! Find them, and kill them!");
_entityManager.EventBus.SubscribeEvent<MobDamageStateChangedMessage>(EventSource.Local, this, _onMobDamageStateChanged); _entityManager.EventBus.SubscribeEvent<MobDamageStateChangedMessage>(EventSource.Local, this, _onMobDamageStateChanged);
Timer.SpawnRepeating(DeadCheckDelay, _checkWinConditions, _checkTimerCancel.Token); Timer.SpawnRepeating(DeadCheckDelay, _checkWinConditions, _checkTimerCancel.Token);
@@ -86,7 +85,6 @@ namespace Content.Server.GameTicking.GameRules
{ {
continue; continue;
} }
if (playerSession.ContentData().Mind.HasRole<SuspicionTraitorRole>()) if (playerSession.ContentData().Mind.HasRole<SuspicionTraitorRole>())
traitorsAlive++; traitorsAlive++;
else else

View File

@@ -267,12 +267,12 @@ namespace Content.Server.GameTicking
} }
// Time to start the preset. // Time to start the preset.
var preset = MakeGamePreset(); var preset = MakeGamePreset(profiles);
if (!preset.Start(assignedJobs.Keys.ToList(), force)) if (!preset.Start(assignedJobs.Keys.ToList(), force))
{ {
SetStartPreset(_configurationManager.GetCVar<string>("game.fallbackpreset")); SetStartPreset(_configurationManager.GetCVar<string>("game.fallbackpreset"));
var newPreset = MakeGamePreset(); var newPreset = MakeGamePreset(profiles);
_chatManager.DispatchServerAnnouncement( _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)) if (!newPreset.Start(readyPlayers, force))
@@ -306,7 +306,7 @@ namespace Content.Server.GameTicking
//Tell every client the round has ended. //Tell every client the round has ended.
var roundEndMessage = _netManager.CreateNetMessage<MsgRoundEndMessage>(); var roundEndMessage = _netManager.CreateNetMessage<MsgRoundEndMessage>();
roundEndMessage.GamemodeTitle = MakeGamePreset().ModeTitle; roundEndMessage.GamemodeTitle = MakeGamePreset(null).ModeTitle;
//Get the timespan of the round. //Get the timespan of the round.
roundEndMessage.RoundDuration = IoCManager.Resolve<IGameTiming>().RealTime.Subtract(_roundStartTimeSpan); roundEndMessage.RoundDuration = IoCManager.Resolve<IGameTiming>().RealTime.Subtract(_roundStartTimeSpan);
@@ -318,13 +318,13 @@ namespace Content.Server.GameTicking
var mind = ply.ContentData().Mind; var mind = ply.ContentData().Mind;
if (mind != null) if (mind != null)
{ {
var antag = mind.AllRoles.Any(role => role.Antag); var antag = mind.AllRoles.Any(role => role.Antagonist);
var playerEndRoundInfo = new RoundEndPlayerInfo() var playerEndRoundInfo = new RoundEndPlayerInfo()
{ {
PlayerOOCName = ply.Name, PlayerOOCName = ply.Name,
PlayerICName = mind.CurrentEntity.Name, PlayerICName = mind.CurrentEntity.Name,
Role = antag Role = antag
? mind.AllRoles.First(role => role.Antag).Name ? mind.AllRoles.First(role => role.Antagonist).Name
: mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unknown"), : mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("Unknown"),
Antag = antag Antag = antag
}; };
@@ -805,7 +805,7 @@ namespace Content.Server.GameTicking
var mindComponent = mob.GetComponent<MindComponent>(); var mindComponent = mob.GetComponent<MindComponent>();
if (mindComponent.HasMind) //Redundancy checks. if (mindComponent.HasMind) //Redundancy checks.
{ {
if (mindComponent.Mind.AllRoles.Any(role => role.Antag)) //Give antags a new uplinkaccount. if (mindComponent.Mind.AllRoles.Any(role => role.Antagonist)) //Give antags a new uplinkaccount.
{ {
var uplinkAccount = var uplinkAccount =
new UplinkAccount(mob.Uid, new UplinkAccount(mob.Uid,
@@ -883,8 +883,8 @@ namespace Content.Server.GameTicking
private string GetInfoText() private string GetInfoText()
{ {
var gmTitle = MakeGamePreset().ModeTitle; var gmTitle = MakeGamePreset(null).ModeTitle;
var desc = MakeGamePreset().Description; var desc = MakeGamePreset(null).Description;
return _localization.GetString(@"Hi and welcome to [color=white]Space Station 14![/color] return _localization.GetString(@"Hi and welcome to [color=white]Space Station 14![/color]
The current game mode is: [color=white]{0}[/color]. The current game mode is: [color=white]{0}[/color].
@@ -898,9 +898,11 @@ The current game mode is: [color=white]{0}[/color].
_netManager.ServerSendToMany(infoMsg, _playersInLobby.Keys.Select(p => p.ConnectedClient).ToList()); _netManager.ServerSendToMany(infoMsg, _playersInLobby.Keys.Select(p => p.ConnectedClient).ToList());
} }
private GamePreset MakeGamePreset() private GamePreset MakeGamePreset(Dictionary<string, HumanoidCharacterProfile> readyProfiles)
{ {
return _dynamicTypeFactory.CreateInstance<GamePreset>(_presetType ?? typeof(PresetSandbox)); var preset = _dynamicTypeFactory.CreateInstance<GamePreset>(_presetType ?? typeof(PresetSandbox));
preset.readyProfiles = readyProfiles;
return preset;
} }
#pragma warning disable 649 #pragma warning disable 649

View File

@@ -26,7 +26,7 @@ namespace Content.Server.Mobs
/// <summary> /// <summary>
/// Whether this role should be considered antagonistic or not. /// Whether this role should be considered antagonistic or not.
/// </summary> /// </summary>
public abstract bool Antag { get; } public abstract bool Antagonist { get; }
protected Role(Mind mind) protected Role(Mind mind)
{ {

View File

@@ -11,7 +11,7 @@ namespace Content.Server.Mobs.Roles
public JobPrototype Prototype { get; } public JobPrototype Prototype { get; }
public override string Name { get; } public override string Name { get; }
public override bool Antag => false; public override bool Antagonist => false;
public string StartingGear => Prototype.StartingGear; public string StartingGear => Prototype.StartingGear;

View File

@@ -2,24 +2,32 @@ using Content.Server.GameObjects;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Shared.Antags;
namespace Content.Server.Mobs.Roles namespace Content.Server.Mobs.Roles
{ {
public class SuspicionInnocentRole : Role public class SuspicionInnocentRole : Role
{ {
public SuspicionInnocentRole(Mind mind) : base(mind) public AntagPrototype Prototype { get; }
public SuspicionInnocentRole(Mind mind, AntagPrototype antagPrototype) : base(mind)
{ {
Prototype = antagPrototype;
Name = antagPrototype.Name;
Antagonist = antagPrototype.Antagonist;
} }
public override string Name => "Innocent"; public override string Name { get; }
public override bool Antag => false; public string Objective => Prototype.Objective;
public override bool Antagonist { get; }
public override void Greet() public override void Greet()
{ {
base.Greet(); base.Greet();
var chat = IoCManager.Resolve<IChatManager>(); var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(Mind.Session, "You're an innocent!"); chat.DispatchServerMessage(Mind.Session, $"You're a {Name}!");
chat.DispatchServerMessage(Mind.Session, $"Objective: {Objective}");
} }
} }
} }

View File

@@ -1,25 +1,33 @@
using Content.Server.GameObjects; using Content.Server.GameObjects;
using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.Chat;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Shared.Antags;
namespace Content.Server.Mobs.Roles namespace Content.Server.Mobs.Roles
{ {
public sealed class SuspicionTraitorRole : Role public sealed class SuspicionTraitorRole : Role
{ {
public SuspicionTraitorRole(Mind mind) : base(mind) public AntagPrototype Prototype { get; }
public SuspicionTraitorRole(Mind mind, AntagPrototype antagPrototype) : base(mind)
{ {
Prototype = antagPrototype;
Name = antagPrototype.Name;
Antagonist = antagPrototype.Antagonist;
} }
public override string Name => "Traitor"; public override string Name { get; }
public override bool Antag => true; public string Objective => Prototype.Objective;
public override bool Antagonist { get; }
public override void Greet() public override void Greet()
{ {
base.Greet(); base.Greet();
var chat = IoCManager.Resolve<IChatManager>(); var chat = IoCManager.Resolve<IChatManager>();
chat.DispatchServerMessage(Mind.Session, "You're a traitor!"); chat.DispatchServerMessage(Mind.Session, $"You're a {Name}!");
chat.DispatchServerMessage(Mind.Session, $"Objective: {Objective}");
} }
} }
} }

View File

@@ -56,7 +56,7 @@ namespace Content.Server.PDA
var entity = _entityManager.GetEntity(acc.AccountHolder); var entity = _entityManager.GetEntity(acc.AccountHolder);
if (entity.TryGetComponent(out MindComponent mindComponent)) if (entity.TryGetComponent(out MindComponent mindComponent))
{ {
if (mindComponent.Mind.AllRoles.Any(role => !role.Antag)) if (mindComponent.Mind.AllRoles.Any(role => !role.Antagonist))
{ {
return false; return false;
} }

View File

@@ -107,6 +107,10 @@ namespace Content.Server.Preferences
.Where(j => j.Value != JobPriority.Never) .Where(j => j.Value != JobPriority.Never)
.Select(j => new Job {JobName = j.Key, Priority = (DbJobPriority) j.Value}) .Select(j => new Job {JobName = j.Key, Priority = (DbJobPriority) j.Value})
); );
entity.Antags.AddRange(
humanoid.AntagPreferences
.Select(a => new Antag {AntagName = a})
);
await _prefsDb.SaveCharacterSlotAsync(username, entity); await _prefsDb.SaveCharacterSlotAsync(username, entity);
} }
finally finally
@@ -140,6 +144,7 @@ namespace Content.Server.Preferences
private static HumanoidCharacterProfile ConvertProfiles(HumanoidProfile profile) private static HumanoidCharacterProfile ConvertProfiles(HumanoidProfile profile)
{ {
var jobs = profile.Jobs.ToDictionary(j => j.JobName, j => (JobPriority) j.Priority); var jobs = profile.Jobs.ToDictionary(j => j.JobName, j => (JobPriority) j.Priority);
var antags = profile.Antags.Select(a => a.AntagName);
return new HumanoidCharacterProfile( return new HumanoidCharacterProfile(
profile.CharacterName, profile.CharacterName,
profile.Age, profile.Age,
@@ -154,7 +159,8 @@ namespace Content.Server.Preferences
Color.FromHex(profile.SkinColor) Color.FromHex(profile.SkinColor)
), ),
jobs, jobs,
(PreferenceUnavailableMode) profile.PreferenceUnavailable (PreferenceUnavailableMode) profile.PreferenceUnavailable,
antags.ToList()
); );
} }
} }

View File

@@ -1,6 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Shared.Antags;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.Preferences namespace Content.Shared.Preferences
@@ -9,6 +12,7 @@ namespace Content.Shared.Preferences
public class HumanoidCharacterProfile : ICharacterProfile public class HumanoidCharacterProfile : ICharacterProfile
{ {
private readonly Dictionary<string, JobPriority> _jobPriorities; private readonly Dictionary<string, JobPriority> _jobPriorities;
private readonly List<string> _antagPreferences;
public static int MinimumAge = 18; public static int MinimumAge = 18;
public static int MaximumAge = 90; public static int MaximumAge = 90;
@@ -18,7 +22,8 @@ namespace Content.Shared.Preferences
Sex sex, Sex sex,
HumanoidCharacterAppearance appearance, HumanoidCharacterAppearance appearance,
Dictionary<string, JobPriority> jobPriorities, Dictionary<string, JobPriority> jobPriorities,
PreferenceUnavailableMode preferenceUnavailable) PreferenceUnavailableMode preferenceUnavailable,
List<string> antagPreferences)
{ {
Name = name; Name = name;
Age = age; Age = age;
@@ -26,6 +31,7 @@ namespace Content.Shared.Preferences
Appearance = appearance; Appearance = appearance;
_jobPriorities = jobPriorities; _jobPriorities = jobPriorities;
PreferenceUnavailable = preferenceUnavailable; PreferenceUnavailable = preferenceUnavailable;
_antagPreferences = antagPreferences;
} }
public HumanoidCharacterProfile( public HumanoidCharacterProfile(
@@ -34,9 +40,10 @@ namespace Content.Shared.Preferences
Sex sex, Sex sex,
HumanoidCharacterAppearance appearance, HumanoidCharacterAppearance appearance,
IReadOnlyDictionary<string, JobPriority> jobPriorities, IReadOnlyDictionary<string, JobPriority> jobPriorities,
PreferenceUnavailableMode preferenceUnavailable) PreferenceUnavailableMode preferenceUnavailable,
IReadOnlyList<string> antagPreferences)
: this(name, age, sex, appearance, new Dictionary<string, JobPriority>(jobPriorities), : this(name, age, sex, appearance, new Dictionary<string, JobPriority>(jobPriorities),
preferenceUnavailable) preferenceUnavailable, new List<string>(antagPreferences))
{ {
} }
@@ -46,7 +53,7 @@ namespace Content.Shared.Preferences
new Dictionary<string, JobPriority> new Dictionary<string, JobPriority>
{ {
{SharedGameTicker.OverflowJob, JobPriority.High} {SharedGameTicker.OverflowJob, JobPriority.High}
}, PreferenceUnavailableMode.StayInLobby); }, PreferenceUnavailableMode.StayInLobby, new List<string>());
} }
public string Name { get; } public string Name { get; }
@@ -55,26 +62,27 @@ namespace Content.Shared.Preferences
public ICharacterAppearance CharacterAppearance => Appearance; public ICharacterAppearance CharacterAppearance => Appearance;
public HumanoidCharacterAppearance Appearance { get; } public HumanoidCharacterAppearance Appearance { get; }
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities; public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
public PreferenceUnavailableMode PreferenceUnavailable { get; } public PreferenceUnavailableMode PreferenceUnavailable { get; }
public HumanoidCharacterProfile WithName(string name) public HumanoidCharacterProfile WithName(string name)
{ {
return new HumanoidCharacterProfile(name, Age, Sex, Appearance, _jobPriorities, PreferenceUnavailable); return new HumanoidCharacterProfile(name, Age, Sex, Appearance, _jobPriorities, PreferenceUnavailable, _antagPreferences);
} }
public HumanoidCharacterProfile WithAge(int age) public HumanoidCharacterProfile WithAge(int age)
{ {
return new HumanoidCharacterProfile(Name, age, Sex, Appearance, _jobPriorities, PreferenceUnavailable); return new HumanoidCharacterProfile(Name, age, Sex, Appearance, _jobPriorities, PreferenceUnavailable, _antagPreferences);
} }
public HumanoidCharacterProfile WithSex(Sex sex) public HumanoidCharacterProfile WithSex(Sex sex)
{ {
return new HumanoidCharacterProfile(Name, Age, sex, Appearance, _jobPriorities, PreferenceUnavailable); return new HumanoidCharacterProfile(Name, Age, sex, Appearance, _jobPriorities, PreferenceUnavailable, _antagPreferences);
} }
public HumanoidCharacterProfile WithCharacterAppearance(HumanoidCharacterAppearance appearance) public HumanoidCharacterProfile WithCharacterAppearance(HumanoidCharacterAppearance appearance)
{ {
return new HumanoidCharacterProfile(Name, Age, Sex, appearance, _jobPriorities, PreferenceUnavailable); return new HumanoidCharacterProfile(Name, Age, Sex, appearance, _jobPriorities, PreferenceUnavailable, _antagPreferences);
} }
public HumanoidCharacterProfile WithJobPriorities(IReadOnlyDictionary<string, JobPriority> jobPriorities) public HumanoidCharacterProfile WithJobPriorities(IReadOnlyDictionary<string, JobPriority> jobPriorities)
@@ -85,7 +93,8 @@ namespace Content.Shared.Preferences
Sex, Sex,
Appearance, Appearance,
new Dictionary<string, JobPriority>(jobPriorities), new Dictionary<string, JobPriority>(jobPriorities),
PreferenceUnavailable); PreferenceUnavailable,
_antagPreferences);
} }
public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority) public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority)
@@ -100,12 +109,44 @@ namespace Content.Shared.Preferences
dictionary[jobId] = priority; dictionary[jobId] = priority;
} }
return new HumanoidCharacterProfile(Name, Age, Sex, Appearance, dictionary, PreferenceUnavailable); return new HumanoidCharacterProfile(Name, Age, Sex, Appearance, dictionary, PreferenceUnavailable, _antagPreferences);
} }
public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode) public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode)
{ {
return new HumanoidCharacterProfile(Name, Age, Sex, Appearance, _jobPriorities, mode); return new HumanoidCharacterProfile(Name, Age, Sex, Appearance, _jobPriorities, mode, _antagPreferences);
}
public HumanoidCharacterProfile WithAntagPreferences(IReadOnlyList<string> antagPreferences)
{
return new HumanoidCharacterProfile(
Name,
Age,
Sex,
Appearance,
_jobPriorities,
PreferenceUnavailable,
new List<string>(antagPreferences));
}
public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref)
{
var list = new List<string>(_antagPreferences);
if(pref)
{
if(!list.Contains(antagId))
{
list.Add(antagId);
}
}
else
{
if(list.Contains(antagId))
{
list.Remove(antagId);
}
}
return new HumanoidCharacterProfile(Name, Age, Sex, Appearance, _jobPriorities, PreferenceUnavailable, list);
} }
public string Summary => public string Summary =>
@@ -119,6 +160,7 @@ namespace Content.Shared.Preferences
if (Sex != other.Sex) return false; if (Sex != other.Sex) return false;
if (PreferenceUnavailable != other.PreferenceUnavailable) return false; if (PreferenceUnavailable != other.PreferenceUnavailable) return false;
if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false; if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false;
if (!_antagPreferences.SequenceEqual(other._antagPreferences)) return false;
return Appearance.MemberwiseEquals(other.Appearance); return Appearance.MemberwiseEquals(other.Appearance);
} }
} }

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Shared.Antags
{
/// <summary>
/// Describes information for a single antag.
/// </summary>
[Prototype("antag")]
public class AntagPrototype : IPrototype, IIndexedPrototype
{
public string ID { get; private set; }
/// <summary>
/// The name of this antag as displayed to players.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// The antag's objective, displayed at round-start to the player.
/// </summary>
public string Objective { get; private set; }
/// <summary>
/// Whether or not the antag role is one of the bad guys.
/// </summary>
public bool Antagonist { get; private set; }
/// <summary>
/// Whether or not the player can set the antag role in antag preferences.
/// </summary>
public bool SetPreference { get; private set; }
public void LoadFrom(YamlMappingNode mapping)
{
ID = mapping.GetNode("id").AsString();
Name = Loc.GetString(mapping.GetNode("name").ToString());
Objective = mapping.GetNode("objective").ToString();
Antagonist = mapping.GetNode("antagonist").AsBool();
SetPreference = mapping.GetNode("setPreference").AsBool();
}
}
}

View File

@@ -35,7 +35,8 @@ namespace Content.Tests.Server.Preferences
{ {
{SharedGameTicker.OverflowJob, JobPriority.High} {SharedGameTicker.OverflowJob, JobPriority.High}
}, },
PreferenceUnavailableMode.StayInLobby PreferenceUnavailableMode.StayInLobby,
new List<string>{}
); );
} }

View File

@@ -0,0 +1,6 @@
- type: antag
id: SuspicionInnocent
name: "innocent"
antagonist: false
setPreference: false
objective: "Discover and eliminate all traitors."

View File

@@ -0,0 +1,6 @@
- type: antag
id: SuspicionTraitor
name: "traitor"
antagonist: true
setPreference: true
objective: "Kill the innocents."