diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 5008b460c4..eb3cb9a3a2 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -7,9 +7,11 @@ using Content.Server.GameTicking.GamePresets; using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.GameTicking; using Content.Server.Mobs; +using Content.Server.Mobs.Roles; using Content.Server.Players; using Content.Shared; using Content.Shared.GameObjects.Components.Inventory; +using Content.Shared.Jobs; using Robust.Server.Interfaces.Maps; using Robust.Server.Interfaces.Player; using Robust.Server.Player; @@ -27,6 +29,7 @@ using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timers; using Robust.Shared.Timing; @@ -91,6 +94,7 @@ namespace Content.Server.GameTicking [Dependency] private IChatManager _chatManager; [Dependency] private IServerNetManager _netManager; [Dependency] private IDynamicTypeFactory _dynamicTypeFactory; + [Dependency] private IPrototypeManager _prototypeManager; [Dependency] private readonly ILocalizationManager _localization; [Dependency] private readonly IRobustRandom _robustRandom; #pragma warning restore 649 @@ -260,16 +264,23 @@ namespace Content.Server.GameTicking UpdateInfoText(); } - private IEntity _spawnPlayerMob() + private IEntity _spawnPlayerMob(Job job) { var entity = _entityManager.SpawnEntityAt(PlayerPrototypeName, _getLateJoinSpawnPoint()); if (entity.TryGetComponent(out InventoryComponent inventory)) { - var uniform = _entityManager.SpawnEntity("UniformAssistant"); - inventory.Equip(EquipmentSlotDefines.Slots.INNERCLOTHING, uniform.GetComponent()); + var gear = _prototypeManager.Index(job.StartingGear).Equipment; - var shoes = _entityManager.SpawnEntity("ShoesBlack"); - inventory.Equip(EquipmentSlotDefines.Slots.SHOES, shoes.GetComponent()); + foreach (var (slotStr, equipmentStr) in gear) + { + if (!Enum.TryParse(slotStr.ToUpper(), out EquipmentSlotDefines.Slots slot)) + { + Logger.Error("{0} is an invalid equipment slot.", slotStr); + continue; + } + var equipmentEntity = _entityManager.SpawnEntity(equipmentStr); + inventory.Equip(slot, equipmentEntity.GetComponent()); + } } return entity; @@ -441,8 +452,11 @@ namespace Content.Server.GameTicking var data = session.ContentData(); data.WipeMind(); data.Mind = new Mind(session.SessionId); + //TODO Replace "Assistant" with the job when char preference are done + var job = new Job(data.Mind, _prototypeManager.Index("Assistant")); + data.Mind.AddRole(job); - var mob = _spawnPlayerMob(); + var mob = _spawnPlayerMob(job); data.Mind.TransferTo(mob); } diff --git a/Content.Server/Mobs/Commands.cs b/Content.Server/Mobs/Commands.cs index 88f53476d4..2b04692431 100644 --- a/Content.Server/Mobs/Commands.cs +++ b/Content.Server/Mobs/Commands.cs @@ -1,13 +1,17 @@ using System.Text; +using Content.Server.Mobs.Roles; using Content.Server.Players; +using Content.Shared.Jobs; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.Reflection; using Robust.Shared.IoC; using Robust.Shared.Network; +using Robust.Shared.Prototypes; namespace Content.Server.Mobs { + public class MindInfoCommand : IClientCommand { public string Command => "mindinfo"; @@ -47,6 +51,10 @@ namespace Content.Server.Mobs public class AddRoleCommand : IClientCommand { +#pragma warning disable 649 + [Dependency] private IPrototypeManager _prototypeManager; +#pragma warning restore 649 + public string Command => "addrole"; public string Description => "Adds a role to a player's mind."; @@ -65,9 +73,8 @@ namespace Content.Server.Mobs if (mgr.TryGetPlayerData(new NetSessionId(args[0]), out var data)) { var mind = data.ContentData().Mind; - var refl = IoCManager.Resolve(); - var type = refl.LooseGetType(args[1]); - mind.AddRole(type); + var role = new Job(mind, _prototypeManager.Index(args[1])); + mind.AddRole(role); } else { @@ -78,6 +85,11 @@ namespace Content.Server.Mobs public class RemoveRoleCommand : IClientCommand { + +#pragma warning disable 649 + [Dependency] private IPrototypeManager _prototypeManager; +#pragma warning restore 649 + public string Command => "rmrole"; public string Description => "Removes a role from a player's mind."; @@ -96,9 +108,8 @@ namespace Content.Server.Mobs if (mgr.TryGetPlayerData(new NetSessionId(args[0]), out var data)) { var mind = data.ContentData().Mind; - var refl = IoCManager.Resolve(); - var type = refl.LooseGetType(args[1]); - mind.RemoveRole(type); + var role = new Job(mind, _prototypeManager.Index(args[1])); + mind.RemoveRole(role); } else { diff --git a/Content.Server/Mobs/Mind.cs b/Content.Server/Mobs/Mind.cs index 3fa7185ca0..88b446928f 100644 --- a/Content.Server/Mobs/Mind.cs +++ b/Content.Server/Mobs/Mind.cs @@ -22,7 +22,7 @@ namespace Content.Server.Mobs /// public sealed class Mind { - private readonly Dictionary _roles = new Dictionary(); + private readonly ISet _roles = new HashSet(); /// /// Creates the new mind attached to a specific player session. @@ -66,7 +66,7 @@ namespace Content.Server.Mobs /// An enumerable over all the roles this mind has. /// [ViewVariables] - public IEnumerable AllRoles => _roles.Values; + public IEnumerable AllRoles => _roles; /// /// The session of the player owning this mind. @@ -87,19 +87,6 @@ namespace Content.Server.Mobs } } - /// - /// Gives this mind a new role. - /// - /// The type of the role to give. - /// The instance of the role. - /// - /// Thrown if we already have a role with this type. - /// - public T AddRole() where T : Role - { - return (T)AddRole(typeof(T)); - } - /// /// Gives this mind a new role. /// @@ -108,31 +95,18 @@ namespace Content.Server.Mobs /// /// Thrown if we already have a role with this type. /// - public Role AddRole(Type t) + public Role AddRole(Role role) { - if (_roles.ContainsKey(t)) + if (_roles.Contains(role)) { - throw new ArgumentException($"We already have this role: {t}"); + throw new ArgumentException($"We already have this role: {role}"); } - var role = (Role)Activator.CreateInstance(t, this); - _roles[t] = role; + _roles.Add(role); role.Greet(); return role; } - /// - /// Removes a role from this mind. - /// - /// The type of the role to remove. - /// - /// Thrown if we do not have this role. - /// - public void RemoveRole() where T : Role - { - RemoveRole(typeof(T)); - } - /// /// Removes a role from this mind. /// @@ -140,42 +114,16 @@ namespace Content.Server.Mobs /// /// Thrown if we do not have this role. /// - public void RemoveRole(Type t) + public void RemoveRole(Role role) { - if (!_roles.ContainsKey(t)) + if (!_roles.Contains(role)) { - throw new ArgumentException($"We do not have this role: {t}"); + throw new ArgumentException($"We do not have this role: {role}"); } // This can definitely get more complex removal hooks later, // when we need it. - _roles.Remove(t); - } - - /// - /// Gets a role of a certain type. - /// - /// The type of the role to get. - /// The role's instance. - /// - /// Thrown if we do not have a role of this type. - /// - public T GetRole() where T : Role - { - return (T)_roles[typeof(T)]; - } - - /// - /// Gets a role of a certain type. - /// - /// The type of the role to get. - /// The role's instance. - /// - /// Thrown if we do not have a role of this type. - /// - public Role GetRole(Type t) - { - return _roles[t]; + _roles.Remove(role); } /// diff --git a/Content.Server/Mobs/Roles/Job.cs b/Content.Server/Mobs/Roles/Job.cs new file mode 100644 index 0000000000..cabfeeecfc --- /dev/null +++ b/Content.Server/Mobs/Roles/Job.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Content.Server.Interfaces.Chat; +using Content.Shared.Jobs; +using Robust.Shared.IoC; + +namespace Content.Server.Mobs.Roles +{ + public class Job : Role + { + private readonly JobPrototype _jobPrototype; + + public override string Name { get; } + + public String StartingGear => _jobPrototype.StartingGear; + + public Job(Mind mind, JobPrototype jobPrototype) : base(mind) + { + _jobPrototype = jobPrototype; + Name = jobPrototype.Name; + } + + public override void Greet() + { + base.Greet(); + + var chat = IoCManager.Resolve(); + chat.DispatchServerMessage( + Mind.Session, + String.Format("You're a new {0}. Do your best!", Name)); + } + } + + +} diff --git a/Content.Shared/Jobs/JobPrototype.cs b/Content.Shared/Jobs/JobPrototype.cs new file mode 100644 index 0000000000..5a6c47ad1b --- /dev/null +++ b/Content.Shared/Jobs/JobPrototype.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Shared.Jobs +{ + [Prototype("job")] + public class JobPrototype : IPrototype, IIndexedPrototype + { + public string ID { get; private set; } + public string Name { get; private set; } + + public string StartingGear { get; private set; } + public IEnumerable Department { get; private set; } + + public void LoadFrom(YamlMappingNode mapping) + { + ID = mapping.GetNode("id").AsString(); + Name = mapping.GetNode("name").ToString(); + StartingGear = mapping.GetNode("startingGear").ToString(); + Department = mapping.GetNode("department").AllNodes.Select(i => i.ToString()); + } + } +} diff --git a/Content.Shared/Jobs/StartingGearPrototype.cs b/Content.Shared/Jobs/StartingGearPrototype.cs new file mode 100644 index 0000000000..e538eabec3 --- /dev/null +++ b/Content.Shared/Jobs/StartingGearPrototype.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using YamlDotNet.RepresentationModel; + +namespace Content.Shared.Jobs +{ + [Prototype("startingGear")] + public class StartingGearPrototype : IPrototype, IIndexedPrototype + { + private string _id; + private Dictionary _equipment; + + [ViewVariables] + public string ID => _id; + + [ViewVariables] + public Dictionary Equipment => _equipment; + + public void LoadFrom(YamlMappingNode mapping) + { + var serializer = YamlObjectSerializer.NewReader(mapping); + + serializer.DataField(ref _id, "id", string.Empty); + serializer.DataField(ref _equipment, "equipment", new Dictionary()); + } + } +} diff --git a/Resources/Prototypes/Jobs/StartingGear/assistantGear.yml b/Resources/Prototypes/Jobs/StartingGear/assistantGear.yml new file mode 100644 index 0000000000..e6eb54aec4 --- /dev/null +++ b/Resources/Prototypes/Jobs/StartingGear/assistantGear.yml @@ -0,0 +1,6 @@ +- type: startingGear + id: AssistantGear + equipment: + innerclothing: UniformAssistant + backpack: BackpackClothing + shoes: ShoesBlack diff --git a/Resources/Prototypes/Jobs/job.yml b/Resources/Prototypes/Jobs/job.yml new file mode 100644 index 0000000000..372a34f98d --- /dev/null +++ b/Resources/Prototypes/Jobs/job.yml @@ -0,0 +1,231 @@ +- type: job + id: Captain + name: "Captain" + startingGear: CaptainGear + department: + - Command + +- type: job + id: HeadOfPersonnel + name: "Head Of Personnel" + startingGear: HeadOfPersonnelGear + department: + - Command + - Civilian + - Cargo + +- type: job + id: HeadOfSecurity + name: "Head Of Security" + startingGear: HeadOfSecurityGear + department: + - Command + - Security + +- type: job + id: ChiefEngineer + name: "Chief Engineer" + startingGear: ChiefEngineerGear + department: + - Command + - Engineering + +- type: job + id: ResearchDirector + name: "Research Director" + startingGear: ResearchDirectorGear + department: + - Command + - Science + +- type: job + id: ChiefMedicalOfficer + name: "Chief Medical Officer" + startingGear: ChiefMedicalOfficerGear + department: + - Command + - Medical + +- type: job + id: StationEngineer + name: "Station Engineer" + startingGear: StationEngineerGear + department: + - Engineering + +- type: job + id: AtmosphericTechnician + name: "Atmospheric Technician" + startingGear: AtmosphericTechnicianGear + department: + - Engineering + +- type: job + id: Mechanic + name: "Mechanic" + startingGear: MechanicGear + department: + - Engineering + - Science + +- type: job + id: MedicalDoctor + name: "Medical Doctor" + startingGear: MedicalDoctorGear + department: + - Medical + +- type: job + id: Geneticist + name: "Geneticist" + startingGear: GeneticistGear + department: + - Science + - Medical + +- type: job + id: Virologist + name: "Virologist" + startingGear: VirologistGear + department: + - Medical + +- type: job + id: Paramedic + name: "Paramedic" + startingGear: ParamedicGear + department: + - Medical + +- type: job + id: Chemist + name: "Chemist" + startingGear: ChemistGear + department: + - Medical + +- type: job + id: Scientist + name: "Scientist" + startingGear: ScientistGear + department: + - Science + +- type: job + id: Roboticist + name: "Roboticist" + startingGear: RoboticistGear + department: + - Science + +- type: job + id: Bartender + name: "Bartender" + startingGear: BartenderGear + department: + - Civilian + +- type: job + id: Botanist + name: "Botanist" + startingGear: BotanistGear + department: + - Civilian + +- type: job + id: Chef + name: "Chef" + startingGear: ChefGear + department: + - Civilian + +- type: job + id: Janitor + name: "Janitor" + startingGear: JanitorGear + department: + - Civilian + +- type: job + id: Librarian + name: "Librarian" + startingGear: LibrarianGear + department: + - Civilian + +- type: job + id: InternalAffairsAgent + name: "Internal Affairs Agent" + startingGear: InternalAffairsAgentGear + department: + - Civilian + +- type: job + id: Chaplain + name: "Chaplain" + startingGear: ChaplainGear + department: + - Civilian + +- type: job + id: Clown + name: "Clown" + startingGear: ClownGear + department: + - Civilian + +- type: job + id: Mime + name: "Mime" + startingGear: MimeGear + department: + - Civilian + +- type: job + id: Assistant + name: "Assistant" + startingGear: AssistantGear + department: + - Civilian + +- type: job + id: Quartermaster + name: "Quartermaster" + startingGear: QuartermasterGear + department: + - Cargo + +- type: job + id: CargoTechnician + name: "Cargo Technician" + startingGear: CargoTechnicianGear + department: + - Cargo + +- type: job + id: ShaftMiner + name: "Shaft Miner" + startingGear: ShaftMinerGear + department: + - Cargo + +- type: job + id: Warden + name: "Warden" + startingGear: WardenGear + department: + - Security + +- type: job + id: Detective + name: "Detective" + startingGear: DetectiveGear + department: + - Security + +- type: job + id: SecurityOfficer + name: "Security Officer" + startingGear: SecurityOfficerGear + department: + - Security