Upstream species (#6066)

* Step 1 of porting; grabbed most of the files via patches.

* Add species field to the DB

* Appearance patches for slimes.

* Fix the db test.

* Add slime's biocompat.

* slimby

* Fixes, allow specifying if a species is playable or not.

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Address reviews.

* Address reviews.

* make an if-case.

* Fix a goof where species wouldn't get shown in the editor correctly (it'd always default to human)

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
Moony
2022-01-08 19:53:14 -06:00
committed by GitHub
parent a3e73889b2
commit ca984036d6
31 changed files with 2436 additions and 108 deletions

View File

@@ -41,9 +41,10 @@ namespace Content.Client.CharacterAppearance.Systems
HumanoidVisualLayers.LFoot HumanoidVisualLayers.LFoot
}; };
private void UpdateLooks(EntityUid uid, HumanoidAppearanceComponent component, ChangedHumanoidAppearanceEvent args) private void UpdateLooks(EntityUid uid, HumanoidAppearanceComponent component,
ChangedHumanoidAppearanceEvent args)
{ {
if(!EntityManager.TryGetComponent(uid, out SpriteComponent? sprite)) if (!EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
return; return;
if (EntityManager.TryGetComponent(uid, out SharedBodyComponent? body)) if (EntityManager.TryGetComponent(uid, out SharedBodyComponent? body))
@@ -58,13 +59,18 @@ namespace Content.Client.CharacterAppearance.Systems
} }
} }
sprite.LayerSetColor(HumanoidVisualLayers.Hair, var hairColor = component.CanColorHair ? component.Appearance.HairColor : Color.White;
component.CanColorHair ? component.Appearance.HairColor : Color.White); hairColor = component.HairMatchesSkin ? component.Appearance.SkinColor : hairColor;
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, sprite.LayerSetColor(HumanoidVisualLayers.Hair, hairColor.WithAlpha(component.HairAlpha));
component.CanColorFacialHair ? component.Appearance.FacialHairColor : Color.White);
var facialHairColor = component.CanColorHair ? component.Appearance.FacialHairColor : Color.White;
facialHairColor = component.HairMatchesSkin ? component.Appearance.SkinColor : facialHairColor;
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, facialHairColor.WithAlpha(component.HairAlpha));
foreach (var layer in _bodyPartLayers) foreach (var layer in _bodyPartLayers)
{
sprite.LayerSetColor(layer, component.Appearance.SkinColor); sprite.LayerSetColor(layer, component.Appearance.SkinColor);
}
sprite.LayerSetColor(HumanoidVisualLayers.Eyes, component.Appearance.EyeColor); sprite.LayerSetColor(HumanoidVisualLayers.Eyes, component.Appearance.EyeColor);

View File

@@ -5,6 +5,7 @@ using Content.Client.Resources;
using Content.Shared.CharacterAppearance.Systems; using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Species;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -27,6 +28,7 @@ namespace Content.Client.Preferences.UI
{ {
private readonly IClientPreferencesManager _preferencesManager; private readonly IClientPreferencesManager _preferencesManager;
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly IPrototypeManager _prototypeManager;
private readonly Button _createNewCharacterButton; private readonly Button _createNewCharacterButton;
private readonly HumanoidProfileEditor _humanoidProfileEditor; private readonly HumanoidProfileEditor _humanoidProfileEditor;
@@ -38,6 +40,7 @@ namespace Content.Client.Preferences.UI
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_entityManager = entityManager; _entityManager = entityManager;
_prototypeManager = prototypeManager;
_preferencesManager = preferencesManager; _preferencesManager = preferencesManager;
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
@@ -113,6 +116,7 @@ namespace Content.Client.Preferences.UI
numberOfFullSlots++; numberOfFullSlots++;
var characterPickerButton = new CharacterPickerButton(_entityManager, var characterPickerButton = new CharacterPickerButton(_entityManager,
_preferencesManager, _preferencesManager,
_prototypeManager,
characterButtonsGroup, characterButtonsGroup,
character); character);
Characters.AddChild(characterPickerButton); Characters.AddChild(characterPickerButton);
@@ -141,6 +145,7 @@ namespace Content.Client.Preferences.UI
public CharacterPickerButton( public CharacterPickerButton(
IEntityManager entityManager, IEntityManager entityManager,
IClientPreferencesManager preferencesManager, IClientPreferencesManager preferencesManager,
IPrototypeManager prototypeManager,
ButtonGroup group, ButtonGroup group,
ICharacterProfile profile) ICharacterProfile profile)
{ {
@@ -148,9 +153,19 @@ namespace Content.Client.Preferences.UI
ToggleMode = true; ToggleMode = true;
Group = group; Group = group;
_previewDummy = entityManager.SpawnEntity("MobHumanDummy", MapCoordinates.Nullspace);
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy, profile);
var humanoid = profile as HumanoidCharacterProfile; var humanoid = profile as HumanoidCharacterProfile;
if (humanoid is not null)
{
var dummy = prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
_previewDummy = entityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
}
else
{
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SpeciesManager.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
}
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy, profile);
if (humanoid != null) if (humanoid != null)
{ {
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, humanoid); LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, humanoid);

View File

@@ -76,6 +76,15 @@
</BoxContainer> </BoxContainer>
</prefUi:HighlightedContainer> </prefUi:HighlightedContainer>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
<!-- Species -->
<prefUi:HighlightedContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'humanoid-profile-editor-species-label'}" />
<OptionButton Name="CSpeciesButton" />
</BoxContainer>
</prefUi:HighlightedContainer>
</BoxContainer>
<!-- Skin --> <!-- Skin -->
<prefUi:HighlightedContainer> <prefUi:HighlightedContainer>
<BoxContainer HorizontalExpand="True"> <BoxContainer HorizontalExpand="True">

View File

@@ -10,6 +10,7 @@ using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Species;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -28,6 +29,7 @@ using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer; using static Robust.Client.UserInterface.Controls.BoxContainer;
using Range = Robust.Client.UserInterface.Controls.Range;
namespace Content.Client.Preferences.UI namespace Content.Client.Preferences.UI
{ {
@@ -55,6 +57,7 @@ namespace Content.Client.Preferences.UI
private Button _randomizeEverythingButton => CRandomizeEverything; private Button _randomizeEverythingButton => CRandomizeEverything;
private RichTextLabel _warningLabel => CWarningLabel; private RichTextLabel _warningLabel => CWarningLabel;
private readonly IClientPreferencesManager _preferencesManager; private readonly IClientPreferencesManager _preferencesManager;
private readonly IEntityManager _entMan;
private Button _saveButton => CSaveButton; private Button _saveButton => CSaveButton;
private Button _sexFemaleButton => CSexFemale; private Button _sexFemaleButton => CSexFemale;
private Button _sexMaleButton => CSexMale; private Button _sexMaleButton => CSexMale;
@@ -72,14 +75,15 @@ namespace Content.Client.Preferences.UI
private readonly List<JobPrioritySelector> _jobPriorities; private readonly List<JobPrioritySelector> _jobPriorities;
private OptionButton _preferenceUnavailableButton => CPreferenceUnavailableButton; private OptionButton _preferenceUnavailableButton => CPreferenceUnavailableButton;
private readonly Dictionary<string, BoxContainer> _jobCategories; private readonly Dictionary<string, BoxContainer> _jobCategories;
// Mildly hacky, as I don't trust prototype order to stay consistent and don't want the UI to break should a new one get added mid-edit. --moony
private readonly List<SpeciesPrototype> _speciesList;
private readonly List<AntagPreferenceSelector> _antagPreferences; private readonly List<AntagPreferenceSelector> _antagPreferences;
private readonly EntityUid _previewDummy; private EntityUid _previewDummy;
private Control _previewSpriteControl => CSpriteViewFront; private Control _previewSpriteControl => CSpriteViewFront;
private Control _previewSpriteSideControl => CSpriteViewSide; private Control _previewSpriteSideControl => CSpriteViewSide;
private readonly SpriteView _previewSprite; private SpriteView? _previewSprite;
private readonly SpriteView _previewSpriteSide; private SpriteView? _previewSpriteSide;
private bool _isDirty; private bool _isDirty;
private bool _needUpdatePreview; private bool _needUpdatePreview;
@@ -94,7 +98,7 @@ namespace Content.Client.Preferences.UI
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_random = IoCManager.Resolve<IRobustRandom>(); _random = IoCManager.Resolve<IRobustRandom>();
_prototypeManager = prototypeManager; _prototypeManager = prototypeManager;
_entMan = entityManager;
_preferencesManager = preferencesManager; _preferencesManager = preferencesManager;
#region Left #region Left
@@ -171,6 +175,23 @@ namespace Content.Client.Preferences.UI
#endregion Gender #endregion Gender
#region Species
_speciesList = prototypeManager.EnumeratePrototypes<SpeciesPrototype>().ToList();
for (var i = 0; i < _speciesList.Count; i++)
{
CSpeciesButton.AddItem(_speciesList[i].Name, i);
}
CSpeciesButton.OnItemSelected += args =>
{
CSpeciesButton.SelectId(args.Id);
SetSpecies(_speciesList[args.Id].ID);
OnSkinColorOnValueChanged(CSkin);
};
#endregion Species
#region Skin #region Skin
// 0 - 100, 0 being gold/yellowish and 100 being dark // 0 - 100, 0 being gold/yellowish and 100 being dark
@@ -181,33 +202,7 @@ namespace Content.Client.Preferences.UI
// 0 is 45 - 20 - 100 // 0 is 45 - 20 - 100
// 20 is 25 - 20 - 100 // 20 is 25 - 20 - 100
// 100 is 25 - 100 - 20 // 100 is 25 - 100 - 20
_skinColor.OnValueChanged += range => _skinColor.OnValueChanged += OnSkinColorOnValueChanged;
{
if (Profile is null)
return;
int rangeOffset = (int) range.Value - 20;
float hue = 25;
float sat = 20;
float val = 100;
if (rangeOffset < 0)
{
hue += Math.Abs(rangeOffset);
}
else if (rangeOffset > 0)
{
sat += rangeOffset;
val -= rangeOffset;
}
var color = Color.FromHsv(new Vector4(hue / 360, sat / 100, val / 100, 1.0f));
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithSkinColor(color));
IsDirty = true;
};
#endregion #endregion
@@ -434,12 +429,76 @@ namespace Content.Client.Preferences.UI
#endregion Left #endregion Left
#region Right if (preferencesManager.ServerDataLoaded)
{
LoadServerData();
}
#region Preview preferencesManager.OnServerDataLoaded += LoadServerData;
_previewDummy = entityManager.SpawnEntity("MobHumanDummy", MapCoordinates.Nullspace); IsDirty = false;
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(_previewDummy); }
private void OnSkinColorOnValueChanged(Range range)
{
if (Profile is null) return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
switch (skin)
{
case SpeciesSkinColor.HumanToned:
{
var rangeOffset = (int) range.Value - 20;
float hue = 25;
float sat = 20;
float val = 100;
if (rangeOffset <= 0)
{
hue += Math.Abs(rangeOffset);
}
else
{
sat += rangeOffset;
val -= rangeOffset;
}
var color = Color.FromHsv(new Vector4(hue / 360, sat / 100, val / 100, 1.0f));
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
case SpeciesSkinColor.Hues:
{
var color = Color.FromHsv(new Vector4(range.Value / 100.0f, 1.0f, 1.0f, 1.0f));
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
}
IsDirty = true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_entMan.DeleteEntity(_previewDummy);
_preferencesManager.OnServerDataLoaded -= LoadServerData;
}
private void RebuildSpriteView()
{
var dollProto = _prototypeManager.Index<SpeciesPrototype>(Profile?.Species ?? SpeciesManager.DefaultSpecies).DollPrototype;
_previewDummy = _entMan.SpawnEntity(dollProto, MapCoordinates.Nullspace);
var sprite = _entMan.GetComponent<SpriteComponent>(_previewDummy);
_previewSpriteControl.DisposeAllChildren();
// Front // Front
_previewSprite = new SpriteView _previewSprite = new SpriteView
@@ -452,6 +511,8 @@ namespace Content.Client.Preferences.UI
}; };
_previewSpriteControl.AddChild(_previewSprite); _previewSpriteControl.AddChild(_previewSprite);
_previewSpriteSideControl.DisposeAllChildren();
// Side // Side
_previewSpriteSide = new SpriteView _previewSpriteSide = new SpriteView
{ {
@@ -462,29 +523,6 @@ namespace Content.Client.Preferences.UI
SizeFlagsStretchRatio = 1 SizeFlagsStretchRatio = 1
}; };
_previewSpriteSideControl.AddChild(_previewSpriteSide); _previewSpriteSideControl.AddChild(_previewSpriteSide);
#endregion Right
#endregion
if (preferencesManager.ServerDataLoaded)
{
LoadServerData();
}
preferencesManager.OnServerDataLoaded += LoadServerData;
IsDirty = false;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
IoCManager.Resolve<IEntityManager>().DeleteEntity((EntityUid) _previewDummy);
_preferencesManager.OnServerDataLoaded -= LoadServerData;
} }
private void LoadServerData() private void LoadServerData()
@@ -512,6 +550,13 @@ namespace Content.Client.Preferences.UI
IsDirty = true; IsDirty = true;
} }
private void SetSpecies(string newSpecies)
{
Profile = Profile?.WithSpecies(newSpecies);
OnSkinColorOnValueChanged(CSkin); // Species may have special color prefs, make sure to update it.
IsDirty = true;
}
private void SetName(string newName) private void SetName(string newName)
{ {
Profile = Profile?.WithName(newName); Profile = Profile?.WithName(newName);
@@ -576,7 +621,13 @@ namespace Content.Client.Preferences.UI
if (Profile == null) if (Profile == null)
return; return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
var color = Color.ToHsv(Profile.Appearance.SkinColor); var color = Color.ToHsv(Profile.Appearance.SkinColor);
switch (skin)
{
case SpeciesSkinColor.HumanToned:
{
// check for hue/value first, if hue is lower than this percentage // check for hue/value first, if hue is lower than this percentage
// and value is 1.0 // and value is 1.0
// then it'll be hue // then it'll be hue
@@ -590,6 +641,25 @@ namespace Content.Client.Preferences.UI
{ {
_skinColor.Value = color.Y * 100; _skinColor.Value = color.Y * 100;
} }
break;
}
case SpeciesSkinColor.Hues:
{
_skinColor.Value = color.X * 100;
break;
}
}
}
private void UpdateSpecies()
{
if (Profile == null)
{
return;
}
CSpeciesButton.Select(_speciesList.FindIndex(x => x.ID == Profile.Species));
} }
private void UpdateGenderControls() private void UpdateGenderControls()
@@ -660,6 +730,7 @@ namespace Content.Client.Preferences.UI
{ {
if (Profile is null) if (Profile is null)
return; return;
RebuildSpriteView();
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy, Profile); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy, Profile);
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile); LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile);
@@ -672,6 +743,7 @@ namespace Content.Client.Preferences.UI
UpdateSexControls(); UpdateSexControls();
UpdateGenderControls(); UpdateGenderControls();
UpdateSkinColor(); UpdateSkinColor();
UpdateSpecies();
UpdateClothingControls(); UpdateClothingControls();
UpdateBackpackControls(); UpdateBackpackControls();
UpdateAgeEdit(); UpdateAgeEdit();

View File

@@ -0,0 +1,870 @@
// <auto-generated />
using System;
using System.Net;
using System.Text.Json;
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;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
[DbContext(typeof(PostgresServerDbContext))]
[Migration("20220108185749_add-species")]
partial class addspecies
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.Property<Guid>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("user_id");
b.Property<int?>("AdminRankId")
.HasColumnType("integer")
.HasColumnName("admin_rank_id");
b.Property<string>("Title")
.HasColumnType("text")
.HasColumnName("title");
b.HasKey("UserId")
.HasName("PK_admin");
b.HasIndex("AdminRankId")
.HasDatabaseName("IX_admin_admin_rank_id");
b.ToTable("admin", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("admin_flag_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("AdminId")
.HasColumnType("uuid")
.HasColumnName("admin_id");
b.Property<string>("Flag")
.IsRequired()
.HasColumnType("text")
.HasColumnName("flag");
b.Property<bool>("Negative")
.HasColumnType("boolean")
.HasColumnName("negative");
b.HasKey("Id")
.HasName("PK_admin_flag");
b.HasIndex("AdminId")
.HasDatabaseName("IX_admin_flag_admin_id");
b.HasIndex("Flag", "AdminId")
.IsUnique();
b.ToTable("admin_flag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("admin_log_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("RoundId")
.HasColumnType("integer")
.HasColumnName("round_id");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone")
.HasColumnName("date");
b.Property<short>("Impact")
.HasColumnType("smallint")
.HasColumnName("impact");
b.Property<JsonDocument>("Json")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("json");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("text")
.HasColumnName("message");
b.Property<int>("Type")
.HasColumnType("integer")
.HasColumnName("type");
b.HasKey("Id", "RoundId")
.HasName("PK_admin_log");
b.HasIndex("RoundId")
.HasDatabaseName("IX_admin_log_round_id");
b.HasIndex("Type")
.HasDatabaseName("IX_admin_log_type");
b.ToTable("admin_log", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
{
b.Property<int>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("uid");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Uid"));
b.Property<int?>("AdminLogId")
.HasColumnType("integer")
.HasColumnName("admin_log_id");
b.Property<int?>("AdminLogRoundId")
.HasColumnType("integer")
.HasColumnName("admin_log_round_id");
b.Property<string>("Name")
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Uid")
.HasName("PK_admin_log_entity");
b.HasIndex("AdminLogId", "AdminLogRoundId")
.HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id");
b.ToTable("admin_log_entity", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
{
b.Property<Guid>("PlayerUserId")
.HasColumnType("uuid")
.HasColumnName("player_user_id");
b.Property<int>("LogId")
.HasColumnType("integer")
.HasColumnName("log_id");
b.Property<int>("RoundId")
.HasColumnType("integer")
.HasColumnName("round_id");
b.HasKey("PlayerUserId", "LogId", "RoundId")
.HasName("PK_admin_log_player");
b.HasIndex("LogId", "RoundId");
b.ToTable("admin_log_player", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("admin_rank_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("PK_admin_rank");
b.ToTable("admin_rank", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("admin_rank_flag_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("AdminRankId")
.HasColumnType("integer")
.HasColumnName("admin_rank_id");
b.Property<string>("Flag")
.IsRequired()
.HasColumnType("text")
.HasColumnName("flag");
b.HasKey("Id")
.HasName("PK_admin_rank_flag");
b.HasIndex("AdminRankId")
.HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
b.HasIndex("Flag", "AdminRankId")
.IsUnique();
b.ToTable("admin_rank_flag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("antag_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("antag_name");
b.Property<int>("ProfileId")
.HasColumnType("integer")
.HasColumnName("profile_id");
b.HasKey("Id")
.HasName("PK_antag");
b.HasIndex("ProfileId", "AntagName")
.IsUnique();
b.ToTable("antag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AssignedUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("assigned_user_id_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("user_name");
b.HasKey("Id")
.HasName("PK_assigned_user_id");
b.HasIndex("UserId")
.IsUnique();
b.HasIndex("UserName")
.IsUnique();
b.ToTable("assigned_user_id", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("job_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_name");
b.Property<int>("Priority")
.HasColumnType("integer")
.HasColumnName("priority");
b.Property<int>("ProfileId")
.HasColumnType("integer")
.HasColumnName("profile_id");
b.HasKey("Id")
.HasName("PK_job");
b.HasIndex("ProfileId")
.HasDatabaseName("IX_job_profile_id");
b.HasIndex("ProfileId", "JobName")
.IsUnique();
b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority")
.IsUnique()
.HasFilter("priority = 3");
b.ToTable("job", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Player", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("player_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("FirstSeenTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_time");
b.Property<IPAddress>("LastSeenAddress")
.IsRequired()
.HasColumnType("inet")
.HasColumnName("last_seen_address");
b.Property<byte[]>("LastSeenHWId")
.HasColumnType("bytea")
.HasColumnName("last_seen_hwid");
b.Property<DateTime>("LastSeenTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_time");
b.Property<string>("LastSeenUserName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("last_seen_user_name");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_player");
b.HasAlternateKey("UserId")
.HasName("ak_player_user_id");
b.HasIndex("LastSeenUserName");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("player", (string)null);
b.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
});
modelBuilder.Entity("Content.Server.Database.PostgresConnectionLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("connection_log_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<IPAddress>("Address")
.IsRequired()
.HasColumnType("inet")
.HasColumnName("address");
b.Property<byte[]>("HWId")
.HasColumnType("bytea")
.HasColumnName("hwid");
b.Property<DateTime>("Time")
.HasColumnType("timestamp with time zone")
.HasColumnName("time");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("user_name");
b.HasKey("Id")
.HasName("PK_connection_log");
b.HasIndex("UserId");
b.ToTable("connection_log", (string)null);
b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
});
modelBuilder.Entity("Content.Server.Database.PostgresServerBan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("server_ban_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<ValueTuple<IPAddress, int>?>("Address")
.HasColumnType("inet")
.HasColumnName("address");
b.Property<DateTime>("BanTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("ban_time");
b.Property<Guid?>("BanningAdmin")
.HasColumnType("uuid")
.HasColumnName("banning_admin");
b.Property<DateTime?>("ExpirationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("expiration_time");
b.Property<byte[]>("HWId")
.HasColumnType("bytea")
.HasColumnName("hwid");
b.Property<string>("Reason")
.IsRequired()
.HasColumnType("text")
.HasColumnName("reason");
b.Property<Guid?>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_server_ban");
b.HasIndex("Address");
b.HasIndex("UserId");
b.ToTable("server_ban", (string)null);
b.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address");
b.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
});
modelBuilder.Entity("Content.Server.Database.PostgresServerUnban", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("unban_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("BanId")
.HasColumnType("integer")
.HasColumnName("ban_id");
b.Property<DateTime>("UnbanTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("unban_time");
b.Property<Guid?>("UnbanningAdmin")
.HasColumnType("uuid")
.HasColumnName("unbanning_admin");
b.HasKey("Id")
.HasName("PK_server_unban");
b.HasIndex("BanId")
.IsUnique();
b.ToTable("server_unban", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Preference", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("preference_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AdminOOCColor")
.IsRequired()
.HasColumnType("text")
.HasColumnName("admin_ooc_color");
b.Property<int>("SelectedCharacterSlot")
.HasColumnType("integer")
.HasColumnName("selected_character_slot");
b.Property<Guid>("UserId")
.HasColumnType("uuid")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_preference");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("preference", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("profile_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("Age")
.HasColumnType("integer")
.HasColumnName("age");
b.Property<string>("Backpack")
.IsRequired()
.HasColumnType("text")
.HasColumnName("backpack");
b.Property<string>("CharacterName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("char_name");
b.Property<string>("Clothing")
.IsRequired()
.HasColumnType("text")
.HasColumnName("clothing");
b.Property<string>("EyeColor")
.IsRequired()
.HasColumnType("text")
.HasColumnName("eye_color");
b.Property<string>("FacialHairColor")
.IsRequired()
.HasColumnType("text")
.HasColumnName("facial_hair_color");
b.Property<string>("FacialHairName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("facial_hair_name");
b.Property<string>("Gender")
.IsRequired()
.HasColumnType("text")
.HasColumnName("gender");
b.Property<string>("HairColor")
.IsRequired()
.HasColumnType("text")
.HasColumnName("hair_color");
b.Property<string>("HairName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("hair_name");
b.Property<int>("PreferenceId")
.HasColumnType("integer")
.HasColumnName("preference_id");
b.Property<int>("PreferenceUnavailable")
.HasColumnType("integer")
.HasColumnName("pref_unavailable");
b.Property<string>("Sex")
.IsRequired()
.HasColumnType("text")
.HasColumnName("sex");
b.Property<string>("SkinColor")
.IsRequired()
.HasColumnType("text")
.HasColumnName("skin_color");
b.Property<int>("Slot")
.HasColumnType("integer")
.HasColumnName("slot");
b.Property<string>("Species")
.IsRequired()
.HasColumnType("text")
.HasColumnName("species");
b.HasKey("Id")
.HasName("PK_profile");
b.HasIndex("PreferenceId")
.HasDatabaseName("IX_profile_preference_id");
b.HasIndex("Slot", "PreferenceId")
.IsUnique();
b.ToTable("profile", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Round", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("round_id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.HasKey("Id")
.HasName("PK_round");
b.ToTable("round", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Whitelist", b =>
{
b.Property<Guid>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasColumnName("user_id");
b.HasKey("UserId")
.HasName("PK_whitelist");
b.ToTable("whitelist", (string)null);
});
modelBuilder.Entity("PlayerRound", b =>
{
b.Property<int>("PlayersId")
.HasColumnType("integer")
.HasColumnName("players_id");
b.Property<int>("RoundsId")
.HasColumnType("integer")
.HasColumnName("rounds_id");
b.HasKey("PlayersId", "RoundsId")
.HasName("PK_player_round");
b.HasIndex("RoundsId")
.HasDatabaseName("IX_player_round_rounds_id");
b.ToTable("player_round", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.HasOne("Content.Server.Database.AdminRank", "AdminRank")
.WithMany("Admins")
.HasForeignKey("AdminRankId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("FK_admin_admin_rank_admin_rank_id");
b.Navigation("AdminRank");
});
modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
{
b.HasOne("Content.Server.Database.Admin", "Admin")
.WithMany("Flags")
.HasForeignKey("AdminId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_flag_admin_admin_id");
b.Navigation("Admin");
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.HasOne("Content.Server.Database.Round", "Round")
.WithMany("AdminLogs")
.HasForeignKey("RoundId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_round_round_id");
b.Navigation("Round");
});
modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
{
b.HasOne("Content.Server.Database.AdminLog", null)
.WithMany("Entities")
.HasForeignKey("AdminLogId", "AdminLogRoundId")
.HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id");
});
modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
{
b.HasOne("Content.Server.Database.Player", "Player")
.WithMany("AdminLogs")
.HasForeignKey("PlayerUserId")
.HasPrincipalKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_player_player_player_user_id");
b.HasOne("Content.Server.Database.AdminLog", "Log")
.WithMany("Players")
.HasForeignKey("LogId", "RoundId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_player_admin_log_log_id_round_id");
b.Navigation("Log");
b.Navigation("Player");
});
modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
{
b.HasOne("Content.Server.Database.AdminRank", "Rank")
.WithMany("Flags")
.HasForeignKey("AdminRankId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id");
b.Navigation("Rank");
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.Profile", "Profile")
.WithMany("Antags")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_antag_profile_profile_id");
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.HasOne("Content.Server.Database.Profile", "Profile")
.WithMany("Jobs")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_job_profile_profile_id");
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.PostgresServerUnban", b =>
{
b.HasOne("Content.Server.Database.PostgresServerBan", "Ban")
.WithOne("Unban")
.HasForeignKey("Content.Server.Database.PostgresServerUnban", "BanId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_server_unban_server_ban_ban_id");
b.Navigation("Ban");
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.HasOne("Content.Server.Database.Preference", "Preference")
.WithMany("Profiles")
.HasForeignKey("PreferenceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_profile_preference_preference_id");
b.Navigation("Preference");
});
modelBuilder.Entity("PlayerRound", b =>
{
b.HasOne("Content.Server.Database.Player", null)
.WithMany()
.HasForeignKey("PlayersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_player_round_player_players_id");
b.HasOne("Content.Server.Database.Round", null)
.WithMany()
.HasForeignKey("RoundsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_player_round_round_rounds_id");
});
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.Navigation("Flags");
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.Navigation("Entities");
b.Navigation("Players");
});
modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
{
b.Navigation("Admins");
b.Navigation("Flags");
});
modelBuilder.Entity("Content.Server.Database.Player", b =>
{
b.Navigation("AdminLogs");
});
modelBuilder.Entity("Content.Server.Database.PostgresServerBan", b =>
{
b.Navigation("Unban");
});
modelBuilder.Entity("Content.Server.Database.Preference", b =>
{
b.Navigation("Profiles");
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.Navigation("Antags");
b.Navigation("Jobs");
});
modelBuilder.Entity("Content.Server.Database.Round", b =>
{
b.Navigation("AdminLogs");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
public partial class addspecies : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "species",
table: "profile",
type: "text",
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "species",
table: "profile");
}
}
}

View File

@@ -611,6 +611,11 @@ namespace Content.Server.Database.Migrations.Postgres
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("slot"); .HasColumnName("slot");
b.Property<string>("Species")
.IsRequired()
.HasColumnType("text")
.HasColumnName("species");
b.HasKey("Id") b.HasKey("Id")
.HasName("PK_profile"); .HasName("PK_profile");

View File

@@ -0,0 +1,816 @@
// <auto-generated />
using System;
using Content.Server.Database;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
[DbContext(typeof(SqliteServerDbContext))]
[Migration("20220108185734_add-species")]
partial class addspecies
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "6.0.0");
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.Property<Guid>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.Property<int?>("AdminRankId")
.HasColumnType("INTEGER")
.HasColumnName("admin_rank_id");
b.Property<string>("Title")
.HasColumnType("TEXT")
.HasColumnName("title");
b.HasKey("UserId")
.HasName("PK_admin");
b.HasIndex("AdminRankId")
.HasDatabaseName("IX_admin_admin_rank_id");
b.ToTable("admin", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("admin_flag_id");
b.Property<Guid>("AdminId")
.HasColumnType("TEXT")
.HasColumnName("admin_id");
b.Property<string>("Flag")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("flag");
b.Property<bool>("Negative")
.HasColumnType("INTEGER")
.HasColumnName("negative");
b.HasKey("Id")
.HasName("PK_admin_flag");
b.HasIndex("AdminId")
.HasDatabaseName("IX_admin_flag_admin_id");
b.HasIndex("Flag", "AdminId")
.IsUnique();
b.ToTable("admin_flag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("admin_log_id");
b.Property<int>("RoundId")
.HasColumnType("INTEGER")
.HasColumnName("round_id");
b.Property<DateTime>("Date")
.HasColumnType("TEXT")
.HasColumnName("date");
b.Property<sbyte>("Impact")
.HasColumnType("INTEGER")
.HasColumnName("impact");
b.Property<string>("Json")
.IsRequired()
.HasColumnType("jsonb")
.HasColumnName("json");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("message");
b.Property<int>("Type")
.HasColumnType("INTEGER")
.HasColumnName("type");
b.HasKey("Id", "RoundId")
.HasName("PK_admin_log");
b.HasIndex("RoundId")
.HasDatabaseName("IX_admin_log_round_id");
b.HasIndex("Type")
.HasDatabaseName("IX_admin_log_type");
b.ToTable("admin_log", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
{
b.Property<int>("Uid")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("uid");
b.Property<int?>("AdminLogId")
.HasColumnType("INTEGER")
.HasColumnName("admin_log_id");
b.Property<int?>("AdminLogRoundId")
.HasColumnType("INTEGER")
.HasColumnName("admin_log_round_id");
b.Property<string>("Name")
.HasColumnType("TEXT")
.HasColumnName("name");
b.HasKey("Uid")
.HasName("PK_admin_log_entity");
b.HasIndex("AdminLogId", "AdminLogRoundId")
.HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id");
b.ToTable("admin_log_entity", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
{
b.Property<Guid>("PlayerUserId")
.HasColumnType("TEXT")
.HasColumnName("player_user_id");
b.Property<int>("LogId")
.HasColumnType("INTEGER")
.HasColumnName("log_id");
b.Property<int>("RoundId")
.HasColumnType("INTEGER")
.HasColumnName("round_id");
b.HasKey("PlayerUserId", "LogId", "RoundId")
.HasName("PK_admin_log_player");
b.HasIndex("LogId", "RoundId");
b.ToTable("admin_log_player", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("admin_rank_id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("name");
b.HasKey("Id")
.HasName("PK_admin_rank");
b.ToTable("admin_rank", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("admin_rank_flag_id");
b.Property<int>("AdminRankId")
.HasColumnType("INTEGER")
.HasColumnName("admin_rank_id");
b.Property<string>("Flag")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("flag");
b.HasKey("Id")
.HasName("PK_admin_rank_flag");
b.HasIndex("AdminRankId")
.HasDatabaseName("IX_admin_rank_flag_admin_rank_id");
b.HasIndex("Flag", "AdminRankId")
.IsUnique();
b.ToTable("admin_rank_flag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("antag_id");
b.Property<string>("AntagName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("antag_name");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER")
.HasColumnName("profile_id");
b.HasKey("Id")
.HasName("PK_antag");
b.HasIndex("ProfileId", "AntagName")
.IsUnique();
b.ToTable("antag", (string)null);
});
modelBuilder.Entity("Content.Server.Database.AssignedUserId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("assigned_user_id_id");
b.Property<Guid>("UserId")
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("user_name");
b.HasKey("Id")
.HasName("PK_assigned_user_id");
b.HasIndex("UserId")
.IsUnique();
b.HasIndex("UserName")
.IsUnique();
b.ToTable("assigned_user_id", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("job_id");
b.Property<string>("JobName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("job_name");
b.Property<int>("Priority")
.HasColumnType("INTEGER")
.HasColumnName("priority");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER")
.HasColumnName("profile_id");
b.HasKey("Id")
.HasName("PK_job");
b.HasIndex("ProfileId")
.HasDatabaseName("IX_job_profile_id");
b.HasIndex("ProfileId", "JobName")
.IsUnique();
b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority")
.IsUnique()
.HasFilter("priority = 3");
b.ToTable("job", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Player", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("player_id");
b.Property<DateTime>("FirstSeenTime")
.HasColumnType("TEXT")
.HasColumnName("first_seen_time");
b.Property<string>("LastSeenAddress")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("last_seen_address");
b.Property<byte[]>("LastSeenHWId")
.HasColumnType("BLOB")
.HasColumnName("last_seen_hwid");
b.Property<DateTime>("LastSeenTime")
.HasColumnType("TEXT")
.HasColumnName("last_seen_time");
b.Property<string>("LastSeenUserName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("last_seen_user_name");
b.Property<Guid>("UserId")
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_player");
b.HasAlternateKey("UserId")
.HasName("ak_player_user_id");
b.HasIndex("LastSeenUserName");
b.ToTable("player", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Preference", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("preference_id");
b.Property<string>("AdminOOCColor")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("admin_ooc_color");
b.Property<int>("SelectedCharacterSlot")
.HasColumnType("INTEGER")
.HasColumnName("selected_character_slot");
b.Property<Guid>("UserId")
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_preference");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("preference", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("profile_id");
b.Property<int>("Age")
.HasColumnType("INTEGER")
.HasColumnName("age");
b.Property<string>("Backpack")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("backpack");
b.Property<string>("CharacterName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("char_name");
b.Property<string>("Clothing")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("clothing");
b.Property<string>("EyeColor")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("eye_color");
b.Property<string>("FacialHairColor")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("facial_hair_color");
b.Property<string>("FacialHairName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("facial_hair_name");
b.Property<string>("Gender")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("gender");
b.Property<string>("HairColor")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("hair_color");
b.Property<string>("HairName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("hair_name");
b.Property<int>("PreferenceId")
.HasColumnType("INTEGER")
.HasColumnName("preference_id");
b.Property<int>("PreferenceUnavailable")
.HasColumnType("INTEGER")
.HasColumnName("pref_unavailable");
b.Property<string>("Sex")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("sex");
b.Property<string>("SkinColor")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("skin_color");
b.Property<int>("Slot")
.HasColumnType("INTEGER")
.HasColumnName("slot");
b.Property<string>("Species")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("species");
b.HasKey("Id")
.HasName("PK_profile");
b.HasIndex("PreferenceId")
.HasDatabaseName("IX_profile_preference_id");
b.HasIndex("Slot", "PreferenceId")
.IsUnique();
b.ToTable("profile", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Round", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("round_id");
b.HasKey("Id")
.HasName("PK_round");
b.ToTable("round", (string)null);
});
modelBuilder.Entity("Content.Server.Database.SqliteConnectionLog", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("connection_log_id");
b.Property<string>("Address")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("address");
b.Property<byte[]>("HWId")
.HasColumnType("BLOB")
.HasColumnName("hwid");
b.Property<DateTime>("Time")
.HasColumnType("TEXT")
.HasColumnName("time");
b.Property<Guid>("UserId")
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("user_name");
b.HasKey("Id")
.HasName("PK_connection_log");
b.ToTable("connection_log", (string)null);
});
modelBuilder.Entity("Content.Server.Database.SqliteServerBan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("ban_id");
b.Property<string>("Address")
.HasColumnType("TEXT")
.HasColumnName("address");
b.Property<DateTime>("BanTime")
.HasColumnType("TEXT")
.HasColumnName("ban_time");
b.Property<Guid?>("BanningAdmin")
.HasColumnType("TEXT")
.HasColumnName("banning_admin");
b.Property<DateTime?>("ExpirationTime")
.HasColumnType("TEXT")
.HasColumnName("expiration_time");
b.Property<byte[]>("HWId")
.HasColumnType("BLOB")
.HasColumnName("hwid");
b.Property<string>("Reason")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("reason");
b.Property<Guid?>("UserId")
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("PK_ban");
b.ToTable("ban", (string)null);
});
modelBuilder.Entity("Content.Server.Database.SqliteServerUnban", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasColumnName("unban_id");
b.Property<int>("BanId")
.HasColumnType("INTEGER")
.HasColumnName("ban_id");
b.Property<DateTime>("UnbanTime")
.HasColumnType("TEXT")
.HasColumnName("unban_time");
b.Property<Guid?>("UnbanningAdmin")
.HasColumnType("TEXT")
.HasColumnName("unbanning_admin");
b.HasKey("Id")
.HasName("PK_unban");
b.HasIndex("BanId")
.IsUnique();
b.ToTable("unban", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Whitelist", b =>
{
b.Property<Guid>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT")
.HasColumnName("user_id");
b.HasKey("UserId")
.HasName("PK_whitelist");
b.ToTable("whitelist", (string)null);
});
modelBuilder.Entity("PlayerRound", b =>
{
b.Property<int>("PlayersId")
.HasColumnType("INTEGER")
.HasColumnName("players_id");
b.Property<int>("RoundsId")
.HasColumnType("INTEGER")
.HasColumnName("rounds_id");
b.HasKey("PlayersId", "RoundsId")
.HasName("PK_player_round");
b.HasIndex("RoundsId")
.HasDatabaseName("IX_player_round_rounds_id");
b.ToTable("player_round", (string)null);
});
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.HasOne("Content.Server.Database.AdminRank", "AdminRank")
.WithMany("Admins")
.HasForeignKey("AdminRankId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("FK_admin_admin_rank_admin_rank_id");
b.Navigation("AdminRank");
});
modelBuilder.Entity("Content.Server.Database.AdminFlag", b =>
{
b.HasOne("Content.Server.Database.Admin", "Admin")
.WithMany("Flags")
.HasForeignKey("AdminId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_flag_admin_admin_id");
b.Navigation("Admin");
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.HasOne("Content.Server.Database.Round", "Round")
.WithMany("AdminLogs")
.HasForeignKey("RoundId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_round_round_id");
b.Navigation("Round");
});
modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b =>
{
b.HasOne("Content.Server.Database.AdminLog", null)
.WithMany("Entities")
.HasForeignKey("AdminLogId", "AdminLogRoundId")
.HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id");
});
modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b =>
{
b.HasOne("Content.Server.Database.Player", "Player")
.WithMany("AdminLogs")
.HasForeignKey("PlayerUserId")
.HasPrincipalKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_player_player_player_user_id");
b.HasOne("Content.Server.Database.AdminLog", "Log")
.WithMany("Players")
.HasForeignKey("LogId", "RoundId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_log_player_admin_log_log_id_round_id");
b.Navigation("Log");
b.Navigation("Player");
});
modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b =>
{
b.HasOne("Content.Server.Database.AdminRank", "Rank")
.WithMany("Flags")
.HasForeignKey("AdminRankId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id");
b.Navigation("Rank");
});
modelBuilder.Entity("Content.Server.Database.Antag", b =>
{
b.HasOne("Content.Server.Database.Profile", "Profile")
.WithMany("Antags")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_antag_profile_profile_id");
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.Job", b =>
{
b.HasOne("Content.Server.Database.Profile", "Profile")
.WithMany("Jobs")
.HasForeignKey("ProfileId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_job_profile_profile_id");
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.HasOne("Content.Server.Database.Preference", "Preference")
.WithMany("Profiles")
.HasForeignKey("PreferenceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_profile_preference_preference_id");
b.Navigation("Preference");
});
modelBuilder.Entity("Content.Server.Database.SqliteServerUnban", b =>
{
b.HasOne("Content.Server.Database.SqliteServerBan", "Ban")
.WithOne("Unban")
.HasForeignKey("Content.Server.Database.SqliteServerUnban", "BanId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_unban_ban_ban_id");
b.Navigation("Ban");
});
modelBuilder.Entity("PlayerRound", b =>
{
b.HasOne("Content.Server.Database.Player", null)
.WithMany()
.HasForeignKey("PlayersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_player_round_player_players_id");
b.HasOne("Content.Server.Database.Round", null)
.WithMany()
.HasForeignKey("RoundsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_player_round_round_rounds_id");
});
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
b.Navigation("Flags");
});
modelBuilder.Entity("Content.Server.Database.AdminLog", b =>
{
b.Navigation("Entities");
b.Navigation("Players");
});
modelBuilder.Entity("Content.Server.Database.AdminRank", b =>
{
b.Navigation("Admins");
b.Navigation("Flags");
});
modelBuilder.Entity("Content.Server.Database.Player", b =>
{
b.Navigation("AdminLogs");
});
modelBuilder.Entity("Content.Server.Database.Preference", b =>
{
b.Navigation("Profiles");
});
modelBuilder.Entity("Content.Server.Database.Profile", b =>
{
b.Navigation("Antags");
b.Navigation("Jobs");
});
modelBuilder.Entity("Content.Server.Database.Round", b =>
{
b.Navigation("AdminLogs");
});
modelBuilder.Entity("Content.Server.Database.SqliteServerBan", b =>
{
b.Navigation("Unban");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
public partial class addspecies : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "species",
table: "profile",
type: "TEXT",
nullable: false,
defaultValue: "");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "species",
table: "profile");
}
}
}

View File

@@ -454,6 +454,11 @@ namespace Content.Server.Database.Migrations.Sqlite
.HasColumnType("INTEGER") .HasColumnType("INTEGER")
.HasColumnName("slot"); .HasColumnName("slot");
b.Property<string>("Species")
.IsRequired()
.HasColumnType("TEXT")
.HasColumnName("species");
b.HasKey("Id") b.HasKey("Id")
.HasName("PK_profile"); .HasName("PK_profile");

View File

@@ -126,6 +126,7 @@ namespace Content.Server.Database
public int Age { get; set; } public int Age { get; set; }
public string Sex { get; set; } = null!; public string Sex { get; set; } = null!;
public string Gender { get; set; } = null!; public string Gender { get; set; } = null!;
public string Species { get; set; } = null!;
public string HairName { get; set; } = null!; public string HairName { get; set; } = null!;
public string HairColor { get; set; } = null!; public string HairColor { get; set; } = null!;
public string FacialHairName { get; set; } = null!; public string FacialHairName { get; set; } = null!;

View File

@@ -158,6 +158,7 @@ namespace Content.Server.Cloning
struct ClonerDNAEntry { struct ClonerDNAEntry {
public Mind.Mind Mind; public Mind.Mind Mind;
public HumanoidCharacterProfile Profile; public HumanoidCharacterProfile Profile;
public ClonerDNAEntry(Mind.Mind m, HumanoidCharacterProfile hcp) public ClonerDNAEntry(Mind.Mind m, HumanoidCharacterProfile hcp)
{ {
Mind = m; Mind = m;

View File

@@ -8,12 +8,14 @@ using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Cloning; using Content.Shared.Cloning;
using Content.Shared.MobState.Components; using Content.Shared.MobState.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Species;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -24,6 +26,7 @@ namespace Content.Server.Cloning.Components
{ {
[Dependency] private readonly IPlayerManager _playerManager = null!; [Dependency] private readonly IPlayerManager _playerManager = null!;
[Dependency] private readonly IEntityManager _entities = default!; [Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly EuiManager _euiManager = null!; [Dependency] private readonly EuiManager _euiManager = null!;
@@ -138,7 +141,8 @@ namespace Content.Server.Cloning.Components
return; // If we can't track down the client, we can't offer transfer. That'd be quite bad. return; // If we can't track down the client, we can't offer transfer. That'd be quite bad.
} }
var mob = _entities.SpawnEntity("MobHuman", _entities.GetComponent<TransformComponent>(Owner).MapPosition); var speciesProto = _prototype.Index<SpeciesPrototype>(dna.Profile.Species).Prototype;
var mob = _entities.SpawnEntity(speciesProto, _entities.GetComponent<TransformComponent>(Owner).MapPosition);
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(mob, dna.Profile); EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(mob, dna.Profile);

View File

@@ -10,10 +10,13 @@ using Content.Server.Administration.Logs;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.CharacterAppearance; using Content.Shared.CharacterAppearance;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Species;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Server.Database namespace Content.Server.Database
@@ -177,6 +180,7 @@ namespace Content.Server.Database
return new HumanoidCharacterProfile( return new HumanoidCharacterProfile(
profile.CharacterName, profile.CharacterName,
profile.Species,
profile.Age, profile.Age,
sex, sex,
gender, gender,
@@ -204,6 +208,7 @@ namespace Content.Server.Database
var entity = new Profile var entity = new Profile
{ {
CharacterName = humanoid.Name, CharacterName = humanoid.Name,
Species = humanoid.Species,
Age = humanoid.Age, Age = humanoid.Age,
Sex = humanoid.Sex.ToString(), Sex = humanoid.Sex.ToString(),
Gender = humanoid.Gender.ToString(), Gender = humanoid.Gender.ToString(),

View File

@@ -19,6 +19,7 @@ using Content.Shared.Inventory;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Species;
using Content.Shared.Station; using Content.Shared.Station;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -33,7 +34,6 @@ namespace Content.Server.GameTicking
{ {
public partial class GameTicker public partial class GameTicker
{ {
private const string PlayerPrototypeName = "MobHuman";
private const string ObserverPrototypeName = "MobObserver"; private const string ObserverPrototypeName = "MobObserver";
[Dependency] private readonly IdCardSystem _cardSystem = default!; [Dependency] private readonly IdCardSystem _cardSystem = default!;
@@ -212,7 +212,9 @@ namespace Content.Server.GameTicking
private EntityUid SpawnPlayerMob(Job job, HumanoidCharacterProfile? profile, StationId station, bool lateJoin = true) private EntityUid SpawnPlayerMob(Job job, HumanoidCharacterProfile? profile, StationId station, bool lateJoin = true)
{ {
var coordinates = lateJoin ? GetLateJoinSpawnPoint(station) : GetJobSpawnPoint(job.Prototype.ID, station); var coordinates = lateJoin ? GetLateJoinSpawnPoint(station) : GetJobSpawnPoint(job.Prototype.ID, station);
var entity = EntityManager.SpawnEntity(PlayerPrototypeName, coordinates); var entity = EntityManager.SpawnEntity(
_prototypeManager.Index<SpeciesPrototype>(profile?.Species ?? SpeciesManager.DefaultSpecies).Prototype,
coordinates);
if (job.StartingGear != null) if (job.StartingGear != null)
{ {

View File

@@ -7,6 +7,7 @@ using Content.Shared;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Species;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -262,13 +263,22 @@ namespace Content.Server.Preferences.Managers
{ {
case HumanoidCharacterProfile hp: case HumanoidCharacterProfile hp:
{ {
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var selectedSpecies = SpeciesManager.DefaultSpecies;
if (prototypeManager.TryIndex<SpeciesPrototype>(hp.Species, out var species) && species.RoundStart)
{
selectedSpecies = hp.Species;
}
newProf = hp newProf = hp
.WithJobPriorities( .WithJobPriorities(
hp.JobPriorities.Where(job => hp.JobPriorities.Where(job =>
_protos.HasIndex<JobPrototype>(job.Key))) _protos.HasIndex<JobPrototype>(job.Key)))
.WithAntagPreferences( .WithAntagPreferences(
hp.AntagPreferences.Where(antag => hp.AntagPreferences.Where(antag =>
_protos.HasIndex<AntagPrototype>(antag))); _protos.HasIndex<AntagPrototype>(antag)))
.WithSpecies(selectedSpecies);
break; break;
} }
default: default:

View File

@@ -4,6 +4,7 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Body.Part namespace Content.Shared.Body.Part
{ {
//TODO: This should be a prototype. --DrSmugleaf
/// <summary> /// <summary>
/// Determines whether two <see cref="SharedBodyPartComponent"/>s can connect. /// Determines whether two <see cref="SharedBodyPartComponent"/>s can connect.
/// </summary> /// </summary>
@@ -12,6 +13,7 @@ namespace Content.Shared.Body.Part
{ {
Universal = 0, Universal = 0,
Biological, Biological,
Mechanical Mechanical,
Slime
} }
} }

View File

@@ -42,6 +42,14 @@ namespace Content.Shared.CharacterAppearance.Components
[ViewVariables] [ViewVariables]
[DataField("canColorFacialHair")] [DataField("canColorFacialHair")]
public bool CanColorFacialHair { get; set; } = true; public bool CanColorFacialHair { get; set; } = true;
[ViewVariables]
[DataField("hairMatchesSkin")]
public bool HairMatchesSkin { get; set; } = false;
[ViewVariables]
[DataField("hairAlpha")]
public float HairAlpha { get; set; } = 1.0f;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -8,6 +8,7 @@ using Content.Shared.Dataset;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Random.Helpers; using Content.Shared.Random.Helpers;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Species;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.IoC; using Robust.Shared.IoC;
@@ -33,6 +34,7 @@ namespace Content.Shared.Preferences
private HumanoidCharacterProfile( private HumanoidCharacterProfile(
string name, string name,
string species,
int age, int age,
Sex sex, Sex sex,
Gender gender, Gender gender,
@@ -44,6 +46,7 @@ namespace Content.Shared.Preferences
List<string> antagPreferences) List<string> antagPreferences)
{ {
Name = name; Name = name;
Species = species;
Age = age; Age = age;
Sex = sex; Sex = sex;
Gender = gender; Gender = gender;
@@ -60,7 +63,7 @@ namespace Content.Shared.Preferences
HumanoidCharacterProfile other, HumanoidCharacterProfile other,
Dictionary<string, JobPriority> jobPriorities, Dictionary<string, JobPriority> jobPriorities,
List<string> antagPreferences) List<string> antagPreferences)
: this(other.Name, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack, : this(other.Name, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack,
jobPriorities, other.PreferenceUnavailable, antagPreferences) jobPriorities, other.PreferenceUnavailable, antagPreferences)
{ {
} }
@@ -73,6 +76,7 @@ namespace Content.Shared.Preferences
public HumanoidCharacterProfile( public HumanoidCharacterProfile(
string name, string name,
string species,
int age, int age,
Sex sex, Sex sex,
Gender gender, Gender gender,
@@ -82,7 +86,7 @@ namespace Content.Shared.Preferences
IReadOnlyDictionary<string, JobPriority> jobPriorities, IReadOnlyDictionary<string, JobPriority> jobPriorities,
PreferenceUnavailableMode preferenceUnavailable, PreferenceUnavailableMode preferenceUnavailable,
IReadOnlyList<string> antagPreferences) IReadOnlyList<string> antagPreferences)
: this(name, age, sex, gender, appearance, clothing, backpack, new Dictionary<string, JobPriority>(jobPriorities), : this(name, species, age, sex, gender, appearance, clothing, backpack, new Dictionary<string, JobPriority>(jobPriorities),
preferenceUnavailable, new List<string>(antagPreferences)) preferenceUnavailable, new List<string>(antagPreferences))
{ {
} }
@@ -91,6 +95,7 @@ namespace Content.Shared.Preferences
{ {
return new( return new(
"John Doe", "John Doe",
SpeciesManager.DefaultSpecies,
MinimumAge, MinimumAge,
Sex.Male, Sex.Male,
Gender.Male, Gender.Male,
@@ -108,6 +113,9 @@ namespace Content.Shared.Preferences
public static HumanoidCharacterProfile Random() public static HumanoidCharacterProfile Random()
{ {
var random = IoCManager.Resolve<IRobustRandom>(); var random = IoCManager.Resolve<IRobustRandom>();
var species = random.Pick(IoCManager.Resolve<IPrototypeManager>()
.EnumeratePrototypes<SpeciesPrototype>().Where(x => x.RoundStart).ToArray()).ID;
var sex = random.Prob(0.5f) ? Sex.Male : Sex.Female; var sex = random.Prob(0.5f) ? Sex.Male : Sex.Female;
var gender = sex == Sex.Male ? Gender.Male : Gender.Female; var gender = sex == Sex.Male ? Gender.Male : Gender.Female;
@@ -117,7 +125,7 @@ namespace Content.Shared.Preferences
var name = $"{firstName} {lastName}"; var name = $"{firstName} {lastName}";
var age = random.Next(MinimumAge, MaximumAge); var age = random.Next(MinimumAge, MaximumAge);
return new HumanoidCharacterProfile(name, age, sex, gender, HumanoidCharacterAppearance.Random(sex), ClothingPreference.Jumpsuit, BackpackPreference.Backpack, return new HumanoidCharacterProfile(name, species, age, sex, gender, HumanoidCharacterAppearance.Random(sex), ClothingPreference.Jumpsuit, BackpackPreference.Backpack,
new Dictionary<string, JobPriority> new Dictionary<string, JobPriority>
{ {
{SharedGameTicker.FallbackOverflowJob, JobPriority.High} {SharedGameTicker.FallbackOverflowJob, JobPriority.High}
@@ -125,6 +133,7 @@ namespace Content.Shared.Preferences
} }
public string Name { get; private set; } public string Name { get; private set; }
public string Species { get; private set; }
public int Age { get; private set; } public int Age { get; private set; }
public Sex Sex { get; private set; } public Sex Sex { get; private set; }
public Gender Gender { get; private set; } public Gender Gender { get; private set; }
@@ -156,6 +165,12 @@ namespace Content.Shared.Preferences
return new(this) { Gender = gender }; return new(this) { Gender = gender };
} }
public HumanoidCharacterProfile WithSpecies(string species)
{
return new(this) { Species = species };
}
public HumanoidCharacterProfile WithCharacterAppearance(HumanoidCharacterAppearance appearance) public HumanoidCharacterProfile WithCharacterAppearance(HumanoidCharacterAppearance appearance)
{ {
return new(this) { Appearance = appearance }; return new(this) { Appearance = appearance };
@@ -367,6 +382,7 @@ namespace Content.Shared.Preferences
return HashCode.Combine( return HashCode.Combine(
HashCode.Combine( HashCode.Combine(
Name, Name,
Species,
Age, Age,
Sex, Sex,
Gender, Gender,

View File

@@ -0,0 +1,6 @@
namespace Content.Shared.Species;
public class SpeciesManager
{
public const string DefaultSpecies = "Human";
}

View File

@@ -0,0 +1,53 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Shared.Species;
[Prototype("species")]
public class SpeciesPrototype : IPrototype
{
/// <summary>
/// Prototype ID of the species.
/// </summary>
[DataField("id", required: true)]
public string ID { get; } = default!;
/// <summary>
/// User visible name of the species.
/// </summary>
[DataField("name", required: true)]
public string Name { get; } = default!;
/// <summary>
/// Whether the species is available "at round start" (In the character editor)
/// </summary>
[DataField("roundStart", required: true)]
public bool RoundStart { get; } = false;
/// <summary>
/// Prototype used by the species as a body.
/// </summary>
[DataField("prototype", required: true)]
public string Prototype { get; } = default!;
/// <summary>
/// Prototype used by the species for the dress-up doll in various menus.
/// </summary>
[DataField("dollPrototype", required: true)]
public string DollPrototype { get; } = default!;
/// <summary>
/// Method of skin coloration used by the species.
/// </summary>
[DataField("skinColoration", required: true)]
public SpeciesSkinColor SkinColoration { get; }
}
public enum SpeciesSkinColor
{
HumanToned,
Hues,
}

View File

@@ -42,6 +42,7 @@ namespace Content.Tests.Server.Preferences
{ {
return new( return new(
"Charlie Charlieson", "Charlie Charlieson",
"Human",
21, 21,
Sex.Male, Sex.Male,
Gender.Epicene, Gender.Epicene,

View File

@@ -7,6 +7,7 @@ humanoid-profile-editor-sex-male-button = Male
humanoid-profile-editor-sex-female-button = Female humanoid-profile-editor-sex-female-button = Female
humanoid-profile-editor-age-label = Age: humanoid-profile-editor-age-label = Age:
humanoid-profile-editor-skin-color-label = Skin color: humanoid-profile-editor-skin-color-label = Skin color:
humanoid-profile-editor-species-label = Species:
humanoid-profile-editor-pronouns-label = Pronouns: humanoid-profile-editor-pronouns-label = Pronouns:
humanoid-profile-editor-pronouns-male-text = He / Him humanoid-profile-editor-pronouns-male-text = He / Him
humanoid-profile-editor-pronouns-female-text = She / Her humanoid-profile-editor-pronouns-female-text = She / Her

View File

@@ -0,0 +1,28 @@
- type: entity
id: SentientSlimeCore
parent: BaseItem
name: sentient slime core
description: "The source of incredible, unending gooeyness."
components:
- type: Sprite
netsync: false
sprite: Mobs/Species/Human/organs.rsi
state: brain
- type: Mechanism
size: 5
compatibility: Slime
- type: Lung
- type: Brain
- type: Stomach
maxVolume: 250
digestionDelay: 20
- type: Metabolizer
maxReagents: 6
metabolizerTypes: [ Slime ]
removeEmpty: true
groups:
- id: Food
- id: Drink
- id: Medicine
- id: Poison
- id: Narcotic

View File

@@ -23,13 +23,9 @@
- type: BodyPart - type: BodyPart
partType: Torso partType: Torso
size: 14 size: 14
compatibility: Biological compatibility: Slime
mechanisms: mechanisms:
- OrganHumanHeart - SentientSlimeCore
- OrganHumanLungs
- OrganHumanStomach
- OrganHumanLiver
- OrganHumanKidneys
# criticalThreshold: 100 # criticalThreshold: 100
# deadThreshold: 150 # deadThreshold: 150
@@ -48,11 +44,8 @@
- type: BodyPart - type: BodyPart
partType: Head partType: Head
size: 7 size: 7
compatibility: Biological compatibility: Slime
vital: true vital: true
mechanisms:
- OrganHumanBrain
- OrganHumanEyes
# criticalThreshold: 50 # criticalThreshold: 50
# deadThreshold: 120 # deadThreshold: 120
- type: Input - type: Input
@@ -75,7 +68,7 @@
- type: BodyPart - type: BodyPart
partType: Arm partType: Arm
size: 5 size: 5
compatibility: Biological compatibility: Slime
symmetry: Left symmetry: Left
# criticalThreshold: 40 # criticalThreshold: 40
# deadThreshold: 80 # deadThreshold: 80
@@ -95,7 +88,7 @@
- type: BodyPart - type: BodyPart
partType: Arm partType: Arm
size: 5 size: 5
compatibility: Biological compatibility: Slime
symmetry: Right symmetry: Right
# criticalThreshold: 40 # criticalThreshold: 40
# deadThreshold: 80 # deadThreshold: 80
@@ -115,7 +108,7 @@
- type: BodyPart - type: BodyPart
partType: Hand partType: Hand
size: 3 size: 3
compatibility: Biological compatibility: Slime
symmetry: Left symmetry: Left
# criticalThreshold: 30 # criticalThreshold: 30
# deadThreshold: 60 # deadThreshold: 60
@@ -135,7 +128,7 @@
- type: BodyPart - type: BodyPart
partType: Hand partType: Hand
size: 3 size: 3
compatibility: Biological compatibility: Slime
symmetry: Right symmetry: Right
# criticalThreshold: 30 # criticalThreshold: 30
# deadThreshold: 60 # deadThreshold: 60
@@ -155,7 +148,7 @@
- type: BodyPart - type: BodyPart
partType: Leg partType: Leg
size: 6 size: 6
compatibility: Biological compatibility: Slime
symmetry: Left symmetry: Left
- type: entity - type: entity
@@ -173,7 +166,7 @@
- type: BodyPart - type: BodyPart
partType: Leg partType: Leg
size: 6 size: 6
compatibility: Biological compatibility: Slime
symmetry: Right symmetry: Right
# criticalThreshold: 45 # criticalThreshold: 45
# deadThreshold: 90 # deadThreshold: 90
@@ -193,7 +186,7 @@
- type: BodyPart - type: BodyPart
partType: Foot partType: Foot
size: 2 size: 2
compatibility: Biological compatibility: Slime
symmetry: Left symmetry: Left
# criticalThreshold: 30 # criticalThreshold: 30
# deadThreshold: 60 # deadThreshold: 60
@@ -213,7 +206,7 @@
- type: BodyPart - type: BodyPart
partType: Foot partType: Foot
size: 2 size: 2
compatibility: Biological compatibility: Slime
symmetry: Right symmetry: Right
# criticalThreshold: 30 # criticalThreshold: 30
# deadThreshold: 60 # deadThreshold: 60

View File

@@ -6,3 +6,6 @@
- type: metabolizerType - type: metabolizerType
id: Animal id: Animal
- type: metabolizerType
id: Slime

View File

@@ -0,0 +1,26 @@
- type: entity
save: false
name: Urist McHands The Dwarf
parent: MobDwarfBase
id: MobDwarf
description: A miserable pile of secrets.
components:
- type: Mind
showExamineInfo: true
- type: Input
context: "human"
- type: PlayerMobMover
- type: PlayerInputMover
- type: Alerts
- type: Actions
innateActions:
- CombatMode
- Disarm
- HumanScream
- type: Eye
- type: CameraRecoil
- type: Examiner
- type: CanHostGuardian
- type: AiFactionTag
factions:
- NanoTrasen

View File

@@ -1,20 +1,24 @@
- type: entity - type: entity
save: false save: false
parent: BaseSlimePerson parent: MobSlimePersonBase
id: SlimePerson id: MobSlimePerson
components: components:
- type: Mind - type: Mind
showExamineInfo: true showExamineInfo: true
- type: Input - type: Input
context: "human" context: "human"
- type: PlayerMobMover
- type: PlayerInputMover
- type: Alerts - type: Alerts
- type: Actions - type: Actions
innateActions: innateActions:
- HumanScream - CombatMode
- Disarm - Disarm
- HumanScream
- type: Eye - type: Eye
- type: CameraRecoil - type: CameraRecoil
- type: Examiner - type: Examiner
- type: CanHostGuardian
- type: AiFactionTag - type: AiFactionTag
factions: factions:
- NanoTrasen - NanoTrasen

View File

@@ -0,0 +1,192 @@
- type: entity
save: false
name: Urist McHands The Dwarf
parent: MobHumanBase
id: MobDwarfBase
abstract: true
description: A miserable pile of secrets.
components:
- type: Icon
sprite: Mobs/Species/Slime/parts.rsi
state: full
- type: Sprite
netsync: false
noRot: true
drawdepth: Mobs
scale: 1, 0.8
layers:
- map: [ "enum.HumanoidVisualLayers.Chest" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- map: [ "enum.HumanoidVisualLayers.Head" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: head_m
- map: [ "enum.HumanoidVisualLayers.Eyes" ]
color: "#008800"
sprite: Mobs/Customization/eyes.rsi
state: eyes
- map: [ "enum.HumanoidVisualLayers.RArm" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_arm
- map: [ "enum.HumanoidVisualLayers.LArm" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_arm
- map: [ "enum.HumanoidVisualLayers.RLeg" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_leg
- map: [ "enum.HumanoidVisualLayers.LLeg" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_leg
- shader: StencilClear
sprite: Mobs/Species/Human/parts.rsi
state: l_leg
- shader: StencilMask
map: [ "enum.HumanoidVisualLayers.StencilMask" ]
sprite: Mobs/Customization/masking_helpers.rsi
state: female_full
visible: false
- map: [ "jumpsuit" ]
shader: StencilDraw
- map: [ "enum.HumanoidVisualLayers.LHand" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_hand
- map: [ "enum.HumanoidVisualLayers.RHand" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_hand
- map: [ "enum.HumanoidVisualLayers.LFoot" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_foot
- map: [ "enum.HumanoidVisualLayers.RFoot" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_foot
- map: [ "enum.HumanoidVisualLayers.Handcuffs" ]
color: "#ffffff"
sprite: Objects/Misc/handcuffs.rsi
state: body-overlay-2
visible: false
- map: [ "id" ]
- map: [ "gloves" ]
- map: [ "shoes" ]
- map: [ "ears" ]
- map: [ "outerClothing" ]
- map: [ "eyes" ]
- map: [ "belt" ]
- map: [ "neck" ]
- map: [ "back" ]
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
state: shaved
sprite: Mobs/Customization/human_facial_hair.rsi
- map: [ "enum.HumanoidVisualLayers.Hair" ]
state: bald
sprite: Mobs/Customization/human_hair.rsi
- map: [ "mask" ]
- map: [ "head" ]
- map: [ "pocket1" ]
- map: [ "pocket2" ]
- type: Body
template: HumanoidTemplate
preset: HumanPreset
- type: DoorBumpOpener
- type: entity
save: false
name: Urist McHands
parent: MobHumanDummy
id: MobDwarfDummy
abstract: true
description: A dummy human meant to be used in character setup.
components:
- type: Sprite
netsync: false
noRot: true
drawdepth: Mobs
scale: 1, 0.8
layers:
- map: [ "enum.HumanoidVisualLayers.Chest" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- map: [ "enum.HumanoidVisualLayers.Head" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: head_m
- map: [ "enum.HumanoidVisualLayers.Eyes" ]
color: "#008800"
sprite: Mobs/Customization/eyes.rsi
state: eyes
- map: [ "enum.HumanoidVisualLayers.RArm" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_arm
- map: [ "enum.HumanoidVisualLayers.LArm" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_arm
- map: [ "enum.HumanoidVisualLayers.RLeg" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_leg
- map: [ "enum.HumanoidVisualLayers.LLeg" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_leg
- shader: StencilClear
sprite: Mobs/Species/Human/parts.rsi
state: l_leg
- shader: StencilMask
map: [ "enum.HumanoidVisualLayers.StencilMask" ]
sprite: Mobs/Customization/masking_helpers.rsi
state: female_full
visible: false
- map: [ "jumpsuit" ]
shader: StencilDraw
- map: [ "enum.HumanoidVisualLayers.LHand" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_hand
- map: [ "enum.HumanoidVisualLayers.RHand" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_hand
- map: [ "enum.HumanoidVisualLayers.LFoot" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: l_foot
- map: [ "enum.HumanoidVisualLayers.RFoot" ]
color: "#e8b59b"
sprite: Mobs/Species/Human/parts.rsi
state: r_foot
- map: [ "enum.HumanoidVisualLayers.Handcuffs" ]
color: "#ffffff"
sprite: Objects/Misc/handcuffs.rsi
state: body-overlay-2
visible: false
- map: [ "id" ]
- map: [ "gloves" ]
- map: [ "shoes" ]
- map: [ "ears" ]
- map: [ "outerClothing" ]
- map: [ "eyes" ]
- map: [ "belt" ]
- map: [ "neck" ]
- map: [ "back" ]
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
state: shaved
sprite: Mobs/Customization/human_facial_hair.rsi
- map: [ "enum.HumanoidVisualLayers.Hair" ]
state: bald
sprite: Mobs/Customization/human_hair.rsi
- map: [ "mask" ]
- map: [ "head" ]
- map: [ "pocket1" ]
- map: [ "pocket2" ]

View File

@@ -1,7 +1,7 @@
- type: entity - type: entity
name: Slime Person name: Urist McSlime
parent: MobHuman parent: MobHumanBase
id: BaseSlimePerson id: MobSlimePersonBase
description: A miserable pile of slime. description: A miserable pile of slime.
abstract: true abstract: true
components: components:
@@ -94,3 +94,102 @@
template: HumanoidTemplate template: HumanoidTemplate
preset: SlimePreset preset: SlimePreset
- type: DoorBumpOpener - type: DoorBumpOpener
- type: HumanoidAppearance
hairMatchesSkin: true
hairAlpha: 0.5
- type: entity
save: false
name: Urist McHands
parent: MobHumanDummy
id: MobSlimePersonDummy
abstract: true
description: A dummy slime meant to be used in character setup.
components:
- type: Sprite
netsync: false
drawdepth: Mobs
layers:
- map: [ "enum.HumanoidVisualLayers.Chest" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: torso_m
- map: [ "enum.HumanoidVisualLayers.Head" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: head_m
- map: [ "enum.HumanoidVisualLayers.Eyes" ]
color: "#008800"
sprite: Mobs/Customization/eyes.rsi
state: eyes
- map: [ "enum.HumanoidVisualLayers.RArm" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: r_arm
- map: [ "enum.HumanoidVisualLayers.LArm" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: l_arm
- map: [ "enum.HumanoidVisualLayers.RLeg" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: r_leg
- map: [ "enum.HumanoidVisualLayers.LLeg" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: l_leg
- shader: StencilClear
sprite: Mobs/Species/Slime/parts.rsi
state: l_leg
- shader: StencilMask
map: [ "enum.HumanoidVisualLayers.StencilMask" ]
sprite: Mobs/Customization/masking_helpers.rsi
state: female_full
visible: false
- map: [ "jumpsuit" ]
shader: StencilDraw
- map: [ "enum.HumanoidVisualLayers.LHand" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: l_hand
- map: [ "enum.HumanoidVisualLayers.RHand" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: r_hand
- map: [ "enum.HumanoidVisualLayers.LFoot" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: l_foot
- map: [ "enum.HumanoidVisualLayers.RFoot" ]
color: "#b8b8b8"
sprite: Mobs/Species/Slime/parts.rsi
state: r_foot
- map: [ "enum.HumanoidVisualLayers.Handcuffs" ]
color: "#ffffff"
sprite: Objects/Misc/handcuffs.rsi
state: body-overlay-2
visible: false
- map: [ "id" ]
- map: [ "gloves" ]
- map: [ "shoes" ]
- map: [ "ears" ]
- map: [ "outerClothing" ]
- map: [ "eyes" ]
- map: [ "belt" ]
- map: [ "neck" ]
- map: [ "back" ]
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
state: shaved
sprite: Mobs/Customization/human_facial_hair.rsi
color: '#FFFFFF80'
- map: [ "enum.HumanoidVisualLayers.Hair" ]
state: bald
sprite: Mobs/Customization/human_hair.rsi
color: '#FFFFFF80'
- map: [ "mask" ]
- map: [ "head" ]
- map: [ "pocket1" ]
- map: [ "pocket2" ]
- type: HumanoidAppearance
hairMatchesSkin: true
hairAlpha: 0.5

View File

@@ -0,0 +1,23 @@
- type: species
id: Human
name: Human
roundStart: true
prototype: MobHuman
dollPrototype: MobHumanDummy
skinColoration: HumanToned
- type: species
id: Dwarf
name: Dwarf
roundStart: true
prototype: MobDwarf
dollPrototype: MobDwarfDummy
skinColoration: HumanToned
- type: species
id: SlimePerson
name: Slime Person
roundStart: true
prototype: MobSlimePerson
dollPrototype: MobSlimePersonDummy
skinColoration: Hues