Merge branch 'master' into ed-god-hair

This commit is contained in:
Ed
2025-03-07 17:23:53 +03:00
committed by GitHub
262 changed files with 12086 additions and 10907 deletions

View File

@@ -1,11 +1,12 @@
using Content.Shared._CP14.Knowledge;
using Content.Shared._CP14.Knowledge.Events;
using Content.Shared._CP14.Knowledge.Prototypes;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Knowledge;
public sealed partial class ClientCP14KnowledgeSystem : SharedCP14KnowledgeSystem
public sealed class ClientCP14KnowledgeSystem : SharedCP14KnowledgeSystem
{
[Dependency] private readonly IPlayerManager _players = default!;
@@ -24,7 +25,7 @@ public sealed partial class ClientCP14KnowledgeSystem : SharedCP14KnowledgeSyste
if (entity is null)
return;
RaiseNetworkEvent(new RequestKnowledgeInfoEvent(GetNetEntity(entity.Value)));
RaiseNetworkEvent(new CP14RequestKnowledgeInfoEvent(GetNetEntity(entity.Value)));
}
private void OnCharacterKnowledgeEvent(CP14KnowledgeInfoEvent msg, EntitySessionEventArgs args)
@@ -37,6 +38,6 @@ public sealed partial class ClientCP14KnowledgeSystem : SharedCP14KnowledgeSyste
public readonly record struct KnowledgeData(
EntityUid Entity,
HashSet<ProtoId<CP14KnowledgePrototype>> AllKnowledges
HashSet<ProtoId<CP14KnowledgePrototype>> AllKnowledge
);
}

View File

@@ -1,11 +1,19 @@
<Control xmlns="https://spacestation14.io">
<Button Name="ProductButton" Access="Public">
<BoxContainer Orientation="Horizontal">
<EntityPrototypeView Name="EntityView"
MinSize="48 48"
MaxSize="48 48"
Scale="2,2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Visible="False"/>
<TextureRect Name="View"
MinSize="48 48"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Stretch="KeepAspectCentered" />
Stretch="KeepAspectCentered"
Visible="False"/>
<BoxContainer Orientation="Vertical">
<RichTextLabel Name="SpecialLabel" Text="{Loc 'cp14-store-ui-tab-special'}" VerticalAlignment="Center" Access="Public" Visible="False" />
<RichTextLabel Name="ProductName" VerticalAlignment="Center" Access="Public" />

View File

@@ -3,7 +3,6 @@ using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client._CP14.TravelingStoreShip;
@@ -26,10 +25,16 @@ public sealed partial class CP14StoreProductControl : Control
ProductName.Text = $"[bold]{entry.Name}[/bold]";
SpecialLabel.Visible = entry.Special;
View.Texture = _sprite.Frame0(entry.Icon);
}
private void UpdateView(SpriteSpecifier spriteSpecifier)
{
if (entry.Icon is not null)
{
View.Visible = true;
View.Texture = _sprite.Frame0(entry.Icon);
}
else if (entry.EntityView is not null)
{
EntityView.Visible = true;
EntityView.SetPrototype(entry.EntityView);
}
}
}

View File

@@ -1,6 +1,7 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'cp14-store-ui-title'}"
Name="Window"
Title=""
MinSize="800 600"
SetSize="800 600">
<BoxContainer Orientation="Horizontal">

View File

@@ -11,9 +11,6 @@ public sealed partial class CP14StoreWindow : DefaultWindow
{
[Dependency] private readonly IGameTiming _timing = default!;
private TimeSpan? _nextTravelTime;
private bool _onStation;
public CP14StoreWindow()
{
RobustXamlLoader.Load(this);
@@ -25,24 +22,8 @@ public sealed partial class CP14StoreWindow : DefaultWindow
public void UpdateUI(CP14StoreUiState state)
{
Window.Title = Loc.GetString("cp14-store-ui-title", ("name", state.ShopName));
UpdateProducts(state);
_nextTravelTime = state.NextTravelTime;
_onStation = state.OnStation;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
//Updating time
if (_nextTravelTime is not null)
{
var time = _nextTravelTime.Value - _timing.CurTime;
TravelTimeLabel.Text =
$"{Loc.GetString(_onStation ? "cp14-store-ui-next-travel-out" : "cp14-store-ui-next-travel-in")} {Math.Max(time.Minutes, 0):00}:{Math.Max(time.Seconds, 0):00}";
}
}
private void UpdateProducts(CP14StoreUiState state)

View File

@@ -0,0 +1,31 @@
using Content.Shared._CP14.Vampire;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
namespace Content.Client._CP14.Vampire;
public sealed class CP14ClientVampireVisualsSystem : CP14SharedVampireVisualsSystem
{
protected override void OnVampireVisualsInit(Entity<CP14VampireVisualsComponent> vampire, ref ComponentInit args)
{
base.OnVampireVisualsInit(vampire, ref args);
if (!EntityManager.TryGetComponent(vampire, out SpriteComponent? sprite))
return;
if (sprite.LayerMapTryGet(vampire.Comp.FangsMap, out var fangsLayerIndex))
sprite.LayerSetVisible(fangsLayerIndex, true);
}
protected override void OnVampireVisualsShutdown(Entity<CP14VampireVisualsComponent> vampire, ref ComponentShutdown args)
{
base.OnVampireVisualsShutdown(vampire, ref args);
if (!EntityManager.TryGetComponent(vampire, out SpriteComponent? sprite))
return;
if (sprite.LayerMapTryGet(vampire.Comp.FangsMap, out var fangsLayerIndex))
sprite.LayerSetVisible(fangsLayerIndex, false);
}
}

View File

@@ -1,3 +1,4 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Administration.Commands;
using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
@@ -36,6 +37,11 @@ public sealed partial class AdminVerbSystem
[ValidatePrototypeId<StartingGearPrototype>]
private const string PirateGearId = "PirateGear";
//CP14
[ValidatePrototypeId<EntityPrototype>]
private const string CP14VampireRule = "CP14Vampire";
//CP14 end
// All antag verbs have names so invokeverb works.
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
{
@@ -52,6 +58,21 @@ public sealed partial class AdminVerbSystem
var targetPlayer = targetActor.PlayerSession;
Verb vampire = new()
{
Text = Loc.GetString("cp14-admin-verb-text-make-vampire"),
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/_CP14/Actions/Spells/vampire.rsi"),
"bite"),
Act = () =>
{
_antag.ForceMakeAntag<CP14VampireRuleComponent>(targetPlayer, CP14VampireRule);
},
Impact = LogImpact.High,
Message = Loc.GetString("cp14-admin-verb-make-vampire"),
};
args.Verbs.Add(vampire);
/* CP14 disable default antags
Verb traitor = new()
{

View File

@@ -0,0 +1,55 @@
using Content.Server.Storage.Components;
using Content.Shared._CP14.Cargo;
using Content.Shared.Storage.Components;
namespace Content.Server._CP14.Cargo;
public sealed partial class CP14CargoSystem
{
private void InitializePortals()
{
SubscribeLocalEvent<CP14TradingPortalComponent, MapInitEvent>(OnTradePortalMapInit);
SubscribeLocalEvent<CP14TradingPortalComponent, StorageAfterCloseEvent>(OnTradePortalClose);
SubscribeLocalEvent<CP14TradingPortalComponent, StorageAfterOpenEvent>(OnTradePortalOpen);
}
private void UpdatePortals(float frameTime)
{
var query = EntityQueryEnumerator<CP14TradingPortalComponent, EntityStorageComponent>();
while (query.MoveNext(out var ent, out var portal, out var storage))
{
if (portal.ProcessFinishTime == TimeSpan.Zero || portal.ProcessFinishTime >= _timing.CurTime)
continue;
portal.ProcessFinishTime = TimeSpan.Zero;
SellingThings((ent, portal), storage);
TopUpBalance((ent, portal), storage);
BuyThings((ent, portal), storage);
CashOut((ent, portal), storage);
ThrowAllItems((ent, portal), storage);
}
}
private void OnTradePortalMapInit(Entity<CP14TradingPortalComponent> ent, ref MapInitEvent args)
{
AddRoundstartTradingPositions(ent);
UpdateStaticPositions(ent);
ent.Comp.CurrentSpecialBuyPositions.Clear();
ent.Comp.CurrentSpecialSellPositions.Clear();
AddRandomBuySpecialPosition(ent, ent.Comp.SpecialBuyPositionCount);
AddRandomSellSpecialPosition(ent, ent.Comp.SpecialSellPositionCount);
}
private void OnTradePortalClose(Entity<CP14TradingPortalComponent> ent, ref StorageAfterCloseEvent args)
{
ent.Comp.ProcessFinishTime = _timing.CurTime + ent.Comp.Delay;
}
private void OnTradePortalOpen(Entity<CP14TradingPortalComponent> ent, ref StorageAfterOpenEvent args)
{
ent.Comp.ProcessFinishTime = TimeSpan.Zero;
}
}

View File

@@ -1,118 +0,0 @@
using System.Numerics;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Shared._CP14.Cargo;
using Content.Shared.Gravity;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server._CP14.Cargo;
public sealed partial class CP14CargoSystem
{
private void InitializeShuttle()
{
SubscribeLocalEvent<CP14TravelingStoreShipComponent, FTLCompletedEvent>(OnFTLCompleted);
SubscribeLocalEvent<CP14TravelingStoreShipComponent, MapInitEvent>(OnMapInit);
}
private void OnMapInit(Entity<CP14TravelingStoreShipComponent> ent, ref MapInitEvent args)
{
//TODO: This is shitcode! Because shouldnt related to traveling ship
EnsureComp<GravityComponent>(ent, out var gravity);
gravity.Enabled = true;
gravity.Inherent = true;
}
private void UpdateShuttle()
{
var query = EntityQueryEnumerator<CP14StationTravelingStoreShipTargetComponent>();
while (query.MoveNext(out var uid, out var ship))
{
if (_timing.CurTime < ship.NextTravelTime || ship.NextTravelTime == TimeSpan.Zero)
continue;
if (ship.Shuttle is null || ship.TradePostMap is null)
continue;
if (Transform(ship.Shuttle.Value).MapUid == Transform(ship.TradePostMap.Value).MapUid)
{
// if landed on trade post
ship.NextTravelTime = _timing.CurTime + ship.StationWaitTime;
SendShuttleToStation(ship.Shuttle.Value);
}
else
{
// if landed on station
ship.NextTravelTime = _timing.CurTime + ship.TradePostWaitTime;
SendShuttleToTradepost(ship.Shuttle.Value, ship.TradePostMap.Value);
}
}
}
private void SendShuttleToStation(EntityUid shuttle, float startupTime = 0f)
{
var targetPoints = new List<EntityUid>();
var targetEnumerator =
EntityQueryEnumerator<CP14TravelingStoreShipFTLTargetComponent,
TransformComponent>(); //TODO - different method position location
while (targetEnumerator.MoveNext(out var uid, out _, out _))
{
targetPoints.Add(uid);
}
if (targetPoints.Count == 0)
return;
var target = _random.Pick(targetPoints);
var targetXform = Transform(target);
var shuttleComp = Comp<ShuttleComponent>(shuttle);
_shuttles.FTLToCoordinates(shuttle,
shuttleComp,
targetXform.Coordinates,
targetXform.LocalRotation,
hyperspaceTime: 20f,
startupTime: startupTime);
}
private void SendShuttleToTradepost(EntityUid shuttle, EntityUid tradePostMap)
{
var shuttleComp = Comp<ShuttleComponent>(shuttle);
_shuttles.FTLToCoordinates(shuttle,
shuttleComp,
new EntityCoordinates(tradePostMap, Vector2.Zero),
Angle.Zero,
startupTime: 10f,
hyperspaceTime: 20f);
}
private void OnFTLCompleted(Entity<CP14TravelingStoreShipComponent> ent, ref FTLCompletedEvent args)
{
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(ent.Comp.Station, out var station))
return;
if (station.TradePostMap is not null &&
Transform(ent).MapUid == Transform(station.TradePostMap.Value).MapUid) //Landed on tradepost
{
station.OnStation = false;
SellingThings((ent.Comp.Station, station)); // +balance
TopUpBalance((ent.Comp.Station, station)); //+balance
BuyToQueue((ent.Comp.Station, station)); //-balance +buyQueue
TrySpawnBuyedThings((ent.Comp.Station, station));
UpdateStorePositions((ent.Comp.Station, station));
}
else //Landed on station
{
station.OnStation = true;
CashOut((ent.Comp.Station, station));
station.Balance = 0;
}
UpdateAllStores();
}
}

View File

@@ -8,101 +8,109 @@ public sealed partial class CP14CargoSystem
{
public void InitializeUI()
{
SubscribeLocalEvent<CP14CargoStoreComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
SubscribeLocalEvent<CP14TradingInfoBoardComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
}
private void TryInitStore(Entity<CP14CargoStoreComponent> ent)
private void TryInitStore(Entity<CP14TradingInfoBoardComponent> ent)
{
//TODO: There's no support for multiple stations. (settlements).
var stations = _station.GetStations();
if (stations.Count == 0)
return;
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(stations[0], out var station))
return;
ent.Comp.Station = new Entity<CP14StationTravelingStoreShipTargetComponent>(stations[0], station);
//TODO: more accurate way to find the trading portal, without lookup
var entitiesInRange = _lookup.GetEntitiesInRange<CP14TradingPortalComponent>(Transform(ent).Coordinates, 2);
foreach (var trading in entitiesInRange)
{
ent.Comp.TradingPortal = trading;
ent.Comp.CahcedFaction = trading.Comp.Faction;
break;
}
}
private void OnBeforeUIOpen(Entity<CP14CargoStoreComponent> ent, ref BeforeActivatableUIOpenEvent args)
private void OnBeforeUIOpen(Entity<CP14TradingInfoBoardComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
//TODO: If you open a store on a mapping, and initStore() it, the entity will throw an error when you try to save the grid\map.
if (ent.Comp.Station is null)
if (ent.Comp.TradingPortal is null)
TryInitStore(ent);
UpdateUIProducts(ent);
}
private void UpdateAllStores()
private void UpdateUIProducts(Entity<CP14TradingInfoBoardComponent> ent)
{
//TODO: redo
var query = EntityQueryEnumerator<CP14CargoStoreComponent>();
while (query.MoveNext(out var uid, out var store))
{
UpdateUIProducts((uid, store));
}
}
private void UpdateUIProducts(Entity<CP14CargoStoreComponent> ent)
{
if (ent.Comp.Station is null)
if (ent.Comp.TradingPortal is null)
return;
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(ent.Comp.Station.Value, out var storeTargetComp))
if (!TryComp<CP14TradingPortalComponent>(ent.Comp.TradingPortal.Value, out var tradePortalComp))
return;
var prodBuy = new HashSet<CP14StoreUiProductEntry>();
var prodSell = new HashSet<CP14StoreUiProductEntry>();
//Add special buy positions
foreach (var (proto, price) in storeTargetComp.CurrentSpecialBuyPositions)
foreach (var (proto, price) in tradePortalComp.CurrentSpecialBuyPositions)
{
var name = Loc.GetString(proto.Name);
var name = proto.NameOverride ?? proto.Service.GetName(_proto);
var desc = new StringBuilder();
desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-buy-hint", ("name", Loc.GetString(proto.Name)), ("code", "[color=yellow][bold]#" + proto.Code + "[/bold][/color]")));
//desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-buy-hint", ("name", name), ("code", "[color=yellow][bold]#" + proto.Code + "[/bold][/color]")));
prodBuy.Add(new CP14StoreUiProductEntry(proto.ID, proto.Icon, name, desc.ToString(), price, true));
prodBuy.Add(new CP14StoreUiProductEntry(proto.ID, proto.IconOverride ?? proto.Service.GetTexture(_proto), proto.Service.GetEntityView(_proto), name, desc.ToString(), price, true));
}
//Add static buy positions
foreach (var (proto, price) in storeTargetComp.CurrentBuyPositions)
foreach (var (proto, price) in tradePortalComp.CurrentBuyPositions)
{
var name = Loc.GetString(proto.Name);
var name = proto.NameOverride ?? proto.Service.GetName(_proto);
var desc = new StringBuilder();
desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-buy-hint", ("name", Loc.GetString(proto.Name)), ("code", "[color=yellow][bold]#" + proto.Code + "[/bold][/color]")));
//desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-buy-hint", ("name", name), ("code", "[color=yellow][bold]#" + proto.Code + "[/bold][/color]")));
prodBuy.Add(new CP14StoreUiProductEntry(proto.ID, proto.Icon, name, desc.ToString(), price, false));
prodBuy.Add(new CP14StoreUiProductEntry(proto.ID, proto.IconOverride ?? proto.Service.GetTexture(_proto), proto.Service.GetEntityView(_proto), name, desc.ToString(), price, false));
}
//Add special sell positions
foreach (var (proto, price) in storeTargetComp.CurrentSpecialSellPositions)
foreach (var (proto, price) in tradePortalComp.CurrentSpecialSellPositions)
{
var name = Loc.GetString(proto.Name);
var name = proto.Service.GetName(_proto);
var desc = new StringBuilder();
desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-sell-hint", ("name", Loc.GetString(proto.Name))));
//desc.Append(Loc.GetString(proto.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-sell-hint", ("name", name)));
prodSell.Add(new CP14StoreUiProductEntry(proto.ID, proto.Icon, name, desc.ToString(), price, true));
prodSell.Add(new CP14StoreUiProductEntry(
proto.ID,
proto.Service.GetTexture(_proto),
proto.Service.GetEntityView(_proto),
name,
desc.ToString(),
price,
true));
}
//Add static sell positions
foreach (var proto in storeTargetComp.CurrentSellPositions)
foreach (var proto in tradePortalComp.CurrentSellPositions)
{
var name = Loc.GetString(proto.Key.Name);
var name = proto.Key.Service.GetName(_proto);
var desc = new StringBuilder();
desc.Append(Loc.GetString(proto.Key.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-sell-hint", ("name", Loc.GetString(proto.Key.Name))));
//desc.Append(Loc.GetString(proto.Key.Desc) + "\n");
desc.Append("\n" + Loc.GetString("cp14-store-sell-hint", ("name", name)));
prodSell.Add(new CP14StoreUiProductEntry(proto.Key.ID, proto.Key.Icon, name, desc.ToString(), proto.Value, false));
prodSell.Add(new CP14StoreUiProductEntry(
proto.Key.ID,
proto.Key.Service.GetTexture(_proto),
proto.Key.Service.GetEntityView(_proto),
name,
desc.ToString(),
proto.Value,
false));
}
var stationComp = storeTargetComp;
_userInterface.SetUiState(ent.Owner, CP14StoreUiKey.Key, new CP14StoreUiState(prodBuy, prodSell, stationComp.OnStation, stationComp.NextTravelTime));
var shopName = ":3";
//Get shop name
if (ent.Comp.CahcedFaction is not null && _proto.TryIndex(ent.Comp.CahcedFaction.Value, out var indexedShop))
{
shopName = Loc.GetString(indexedShop.Name);
}
_userInterface.SetUiState(ent.Owner, CP14StoreUiKey.Key, new CP14StoreUiState(shopName, prodBuy, prodSell));
}
}

View File

@@ -1,21 +1,13 @@
using System.Linq;
using System.Numerics;
using Content.Server._CP14.Currency;
using Content.Server._CP14.RoundRemoveShuttle;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Events;
using Content.Server.Station.Systems;
using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems;
using Content.Shared._CP14.Cargo;
using Content.Shared._CP14.Cargo.Prototype;
using Content.Shared.Maps;
using Content.Shared.Paper;
using Content.Shared.Physics;
using Content.Shared.Station.Components;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using JetBrains.Annotations;
using Content.Shared.Throwing;
using Robust.Server.GameObjects;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
@@ -24,20 +16,15 @@ namespace Content.Server._CP14.Cargo;
public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MapLoaderSystem _loader = default!;
[Dependency] private readonly ShuttleSystem _shuttles = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly CP14CurrencySystem _currency = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly TurfSystem _turf = default!;
private EntityQuery<TransformComponent> _xformQuery;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private IEnumerable<CP14StoreBuyPositionPrototype>? _buyProto;
private IEnumerable<CP14StoreSellPositionPrototype>? _sellProto;
@@ -48,15 +35,18 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
base.Initialize();
InitializeUI();
InitializeShuttle();
_xformQuery = GetEntityQuery<TransformComponent>();
InitializePortals();
_buyProto = _proto.EnumeratePrototypes<CP14StoreBuyPositionPrototype>();
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
SubscribeLocalEvent<CP14StationTravelingStoreShipTargetComponent, StationPostInitEvent>(OnPostInit);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdatePortals(frameTime);
}
private void OnProtoReload(PrototypesReloadedEventArgs ev)
@@ -65,61 +55,14 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateShuttle();
}
/// <summary>
/// Allows other systems to additionally add items to the queue that are brought to the settlement on a merchant ship.
/// </summary>
[PublicAPI]
public void AddBuyQueue(Entity<CP14StationTravelingStoreShipTargetComponent> station, List<EntProtoId> products)
{
foreach (var product in products)
{
station.Comp.BuyedQueue.Enqueue(product);
}
}
private void OnPostInit(Entity<CP14StationTravelingStoreShipTargetComponent> station, ref StationPostInitEvent args)
{
if (!Deleted(station.Comp.Shuttle))
return;
var tradepostMap = _mapManager.CreateMap();
if (!_loader.TryLoadGrid(tradepostMap ,station.Comp.ShuttlePath, out var shuttle))
return;
station.Comp.Shuttle = shuttle;
station.Comp.TradePostMap = _mapManager.GetMapEntityId(tradepostMap);
var travelingStoreShipComp = EnsureComp<CP14TravelingStoreShipComponent>(station.Comp.Shuttle.Value);
travelingStoreShipComp.Station = station;
var member = EnsureComp<StationMemberComponent>(shuttle.Value);
member.Station = station;
var roundRemover = EnsureComp<CP14RoundRemoveShuttleComponent>(shuttle.Value);
roundRemover.Station = station;
station.Comp.NextTravelTime = _timing.CurTime + TimeSpan.FromSeconds(10f);
AddRoundstartTradingPositions(station);
UpdateStorePositions(station);
}
private void AddRoundstartTradingPositions(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void AddRoundstartTradingPositions(Entity<CP14TradingPortalComponent> portal)
{
if (_buyProto is not null)
{
foreach (var buy in _buyProto)
{
if (buy.RoundstartAvailable)
station.Comp.AvailableBuyPosition.Add(buy);
portal.Comp.AvailableBuyPosition.Add(buy);
}
}
@@ -128,123 +71,149 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
foreach (var sell in _sellProto)
{
if (sell.RoundstartAvailable)
station.Comp.AvailableSellPosition.Add(sell);
portal.Comp.AvailableSellPosition.Add(sell);
}
}
}
private void UpdateStorePositions(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void UpdateStaticPositions(Entity<CP14TradingPortalComponent> portal)
{
station.Comp.CurrentBuyPositions.Clear();
station.Comp.CurrentSellPositions.Clear();
station.Comp.CurrentSpecialBuyPositions.Clear();
station.Comp.CurrentSpecialSellPositions.Clear();
var availableSpecialSellPositions = new List<CP14StoreSellPositionPrototype>();
var availableSpecialBuyPositions = new List<CP14StoreBuyPositionPrototype>();
portal.Comp.CurrentBuyPositions.Clear();
portal.Comp.CurrentSellPositions.Clear();
//Add static positions + cash special ones
foreach (var buyPos in station.Comp.AvailableBuyPosition)
foreach (var buyPos in portal.Comp.AvailableBuyPosition)
{
if (buyPos.Special)
availableSpecialBuyPositions.Add(buyPos);
else
station.Comp.CurrentBuyPositions.Add(buyPos, buyPos.Price);
continue;
if (buyPos.Factions.Count > 0 && !buyPos.Factions.Contains(portal.Comp.Faction))
continue;
portal.Comp.CurrentBuyPositions.Add(buyPos, buyPos.Price);
}
foreach (var sellPos in station.Comp.AvailableSellPosition)
foreach (var sellPos in portal.Comp.AvailableSellPosition)
{
if (sellPos.Special)
availableSpecialSellPositions.Add(sellPos);
else
station.Comp.CurrentSellPositions.Add(sellPos, sellPos.Price);
continue;
if (sellPos.Factions.Count > 0 && !sellPos.Factions.Contains(portal.Comp.Faction))
continue;
portal.Comp.CurrentSellPositions.Add(sellPos, sellPos.Price);
}
}
private void AddRandomBuySpecialPosition(Entity<CP14TradingPortalComponent> portal, int count)
{
if (_buyProto is null)
return;
var availableSpecialBuyPositions = new List<CP14StoreBuyPositionPrototype>();
foreach (var buyPos in _buyProto)
{
if (!buyPos.Special)
continue;
if (portal.Comp.CurrentSpecialBuyPositions.ContainsKey(buyPos))
continue;
if (buyPos.Factions.Count > 0 && !buyPos.Factions.Contains(portal.Comp.Faction))
continue;
availableSpecialBuyPositions.Add(buyPos);
}
//Random and select special positions
_random.Shuffle(availableSpecialSellPositions);
_random.Shuffle(availableSpecialBuyPositions);
var currentSpecialBuyPositions = station.Comp.SpecialBuyPositionCount.Next(_random);
var currentSpecialSellPositions = station.Comp.SpecialSellPositionCount.Next(_random);
var added = 0;
foreach (var buyPos in availableSpecialBuyPositions)
{
if (station.Comp.CurrentSpecialBuyPositions.Count >= currentSpecialBuyPositions)
if (added >= count)
break;
station.Comp.CurrentSpecialBuyPositions.Add(buyPos, buyPos.Price);
portal.Comp.CurrentSpecialBuyPositions.Add(buyPos, buyPos.Price);
added++;
}
}
private void AddRandomSellSpecialPosition(Entity<CP14TradingPortalComponent> portal, int count)
{
if (_sellProto is null)
return;
var availableSpecialSellPositions = new List<CP14StoreSellPositionPrototype>();
foreach (var sellPos in _sellProto)
{
if (!sellPos.Special)
continue;
if (portal.Comp.CurrentSpecialSellPositions.ContainsKey(sellPos))
continue;
if (sellPos.Factions.Count > 0 && !sellPos.Factions.Contains(portal.Comp.Faction))
continue;
availableSpecialSellPositions.Add(sellPos);
}
_random.Shuffle(availableSpecialSellPositions);
var added = 0;
foreach (var sellPos in availableSpecialSellPositions)
{
if (station.Comp.CurrentSpecialSellPositions.Count >= currentSpecialSellPositions)
if (added >= count)
break;
station.Comp.CurrentSpecialSellPositions.Add(sellPos, sellPos.Price);
portal.Comp.CurrentSpecialSellPositions.Add(sellPos, sellPos.Price);
added++;
}
}
/// <summary>
/// Sell all the items we can, and replenish the internal balance
/// </summary>
private void SellingThings(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void SellingThings(Entity<CP14TradingPortalComponent> portal, EntityStorageComponent storage)
{
var shuttle = station.Comp.Shuttle;
var containedEntities = storage.Contents.ContainedEntities.ToHashSet();
//var ev = new BeforeSellEntities(ref portal.Comp.EntitiesInPortal);
//RaiseLocalEvent(ev);
//Get all entities sent to trading posts
var toSell = new HashSet<EntityUid>();
var query = EntityQueryEnumerator<CP14SellingPalettComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var palletXform))
foreach (var sellPos in portal.Comp.CurrentSellPositions)
{
if (palletXform.ParentUid != shuttle || !palletXform.Anchored)
continue;
var sentEntities = new HashSet<EntityUid>();
_lookup.GetEntitiesInRange(uid, 0.5f, sentEntities, LookupFlags.Dynamic | LookupFlags.Sundries);
foreach (var ent in sentEntities)
//WHILE = sell all we can
while (sellPos.Key.Service.TrySell(EntityManager, containedEntities))
{
if (toSell.Contains(ent) || !_xformQuery.TryGetComponent(ent, out _))
continue;
toSell.Add(ent);
portal.Comp.Balance += sellPos.Value;
}
}
var ev = new BeforeSellEntities(ref toSell);
RaiseLocalEvent(ev);
foreach (var sellPos in station.Comp.CurrentSellPositions)
List<CP14StoreSellPositionPrototype> toRemove = new();
foreach (var sellPos in portal.Comp.CurrentSpecialSellPositions)
{
while (sellPos.Key.Service.TrySell(EntityManager, toSell))
//IF = only 1 try
if (sellPos.Key.Service.TrySell(EntityManager, containedEntities))
{
station.Comp.Balance += sellPos.Value;
portal.Comp.Balance += sellPos.Value;
toRemove.Add(sellPos.Key);
}
}
foreach (var sellPos in station.Comp.CurrentSpecialSellPositions)
//Remove this special position from the list and add new random one
foreach (var position in toRemove)
{
while (sellPos.Key.Service.TrySell(EntityManager, toSell))
{
station.Comp.Balance += sellPos.Value;
}
portal.Comp.CurrentSpecialSellPositions.Remove(position);
AddRandomSellSpecialPosition(portal, 1);
}
}
/// <summary>
/// Take all the money from the tradebox, and credit it to the internal balance
/// Take all the money from the portal, and credit it to the internal balance
/// </summary>
private void TopUpBalance(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void TopUpBalance(Entity<CP14TradingPortalComponent> portal, EntityStorageComponent storage)
{
var tradebox = GetTradeBox(station);
if (tradebox is null)
return;
if (!TryComp<StorageComponent>(tradebox, out var tradeStorage))
return;
//Get all currency in tradebox
int cash = 0;
foreach (var stored in tradeStorage.Container.ContainedEntities)
//Get all currency in portal
var cash = 0;
foreach (var stored in storage.Contents.ContainedEntities)
{
var price = _currency.GetTotalCurrency(stored);
if (price > 0)
@@ -254,22 +223,14 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
}
}
station.Comp.Balance += cash;
portal.Comp.Balance += cash;
}
private void BuyToQueue(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void BuyThings(Entity<CP14TradingPortalComponent> portal, EntityStorageComponent storage)
{
var tradebox = GetTradeBox(station);
if (tradebox is null)
return;
if (!TryComp<StorageComponent>(tradebox, out var tradeStorage))
return;
//Reading all papers in tradebox
//Reading all papers in portal
List<KeyValuePair<CP14StoreBuyPositionPrototype, int>> requests = new();
foreach (var stored in tradeStorage.Container.ContainedEntities)
foreach (var stored in storage.Contents.ContainedEntities)
{
if (!TryComp<PaperComponent>(stored, out var paper))
continue;
@@ -277,12 +238,13 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
var splittedText = paper.Content.Split("#");
foreach (var fragment in splittedText)
{
foreach (var buyPosition in station.Comp.CurrentBuyPositions)
foreach (var buyPosition in portal.Comp.CurrentBuyPositions)
{
if (fragment.StartsWith(buyPosition.Key.Code))
requests.Add(buyPosition);
}
foreach (var buyPosition in station.Comp.CurrentSpecialBuyPositions)
foreach (var buyPosition in portal.Comp.CurrentSpecialBuyPositions)
{
if (fragment.StartsWith(buyPosition.Key.Code))
requests.Add(buyPosition);
@@ -292,80 +254,57 @@ public sealed partial class CP14CargoSystem : CP14SharedCargoSystem
QueueDel(stored);
}
//Trying spend tradebox money to buy requested things
//Trying to spend inner money to buy requested things
foreach (var request in requests)
{
if (station.Comp.Balance < request.Value)
if (portal.Comp.Balance < request.Value)
continue;
station.Comp.Balance -= request.Value;
portal.Comp.Balance -= request.Value;
if (!_proto.TryIndex<CP14StoreBuyPositionPrototype>(request.Key, out var indexedBuyed))
continue;
foreach (var service in indexedBuyed.Services)
//Remove this position from the list and add new random one
if (request.Key.Special)
{
service.Buy(EntityManager, _proto, station);
portal.Comp.CurrentSpecialBuyPositions.Remove(request.Key);
AddRandomBuySpecialPosition(portal, 1);
}
}
}
//Dequeue buyed items, and spawn they on shuttle
private void TrySpawnBuyedThings(Entity<CP14StationTravelingStoreShipTargetComponent> station)
{
var shuttle = station.Comp.Shuttle;
var query = EntityQueryEnumerator<CP14BuyingPalettComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var palletXform))
{
if (station.Comp.BuyedQueue.Count <= 0)
break;
if (palletXform.ParentUid != shuttle || !palletXform.Anchored)
continue;
var tileRef = palletXform.Coordinates.GetTileRef();
if (tileRef is null)
continue;
if (_turf.IsTileBlocked(tileRef.Value, CollisionGroup.ItemMask))
continue;
var buyedThing = station.Comp.BuyedQueue.Dequeue();
Spawn(buyedThing, palletXform.Coordinates);
indexedBuyed.Service.Buy(EntityManager, _proto, portal);
}
}
/// <summary>
/// Transform all the accumulated balance into physical money, which we will give to the players.
/// </summary>
private void CashOut(Entity<CP14StationTravelingStoreShipTargetComponent> station)
private void CashOut(Entity<CP14TradingPortalComponent> portal, EntityStorageComponent storage)
{
var moneyBox = GetTradeBox(station);
if (moneyBox is not null)
var coins = _currency.GenerateMoney(portal.Comp.Balance, Transform(portal).Coordinates);
foreach (var coin in coins)
{
var coord = Transform(moneyBox.Value).Coordinates;
var coins = _currency.GenerateMoney(station.Comp.Balance, coord);
foreach (var coin in coins)
{
_storage.Insert(moneyBox.Value, coin, out _);
}
_entityStorage.Insert(coin, portal, storage);
}
portal.Comp.Balance = 0;
}
private EntityUid? GetTradeBox(Entity<CP14StationTravelingStoreShipTargetComponent> station)
/// <summary>
/// Return all items to the map
/// </summary>
private void ThrowAllItems(Entity<CP14TradingPortalComponent> portal, EntityStorageComponent storage)
{
var query = EntityQueryEnumerator<CP14CargoMoneyBoxComponent, TransformComponent>();
var containedEntities = storage.Contents.ContainedEntities.ToList();
while (query.MoveNext(out var uid, out _, out var xform))
_entityStorage.OpenStorage(portal, storage);
var xform = Transform(portal);
var rotation = xform.LocalRotation;
foreach (var stored in containedEntities)
{
if (xform.GridUid != station.Comp.Shuttle)
continue;
return uid;
_transform.AttachToGridOrMap(stored);
var targetThrowPosition = xform.Coordinates.Offset(rotation.ToWorldVec() * 1);
_throwing.TryThrow(stored, targetThrowPosition.Offset(new Vector2(_random.NextFloat(-0.5f, 0.5f), _random.NextFloat(-0.5f, 0.5f))));
}
return null;
}
}

View File

@@ -1,8 +0,0 @@
namespace Content.Server._CP14.Cargo;
[RegisterComponent, Access(typeof(CP14CargoSystem))]
public sealed partial class CP14TravelingStoreShipComponent : Component
{
[DataField]
public EntityUid Station;
}

View File

@@ -1,10 +0,0 @@
namespace Content.Server._CP14.Cargo;
/// <summary>
/// One of the possible points where an traveling store ship might land
/// </summary>
[RegisterComponent, Access(typeof(CP14CargoSystem))]
public sealed partial class CP14TravelingStoreShipFTLTargetComponent : Component
{
}

View File

@@ -7,6 +7,7 @@ using Content.Shared.Stacks;
using Content.Shared.Storage;
using Content.Shared.Whitelist;
using Robust.Server.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Currency;
@@ -28,12 +29,14 @@ public sealed partial class CP14CurrencySystem : CP14SharedCurrencySystem
SubscribeLocalEvent<CP14CurrencyExaminableComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<CP14CurrencyComponent, CP14GetCurrencyEvent>(OnGetCurrency);
//SubscribeLocalEvent<EntityStorageComponent, CP14GetCurrencyEvent>(OnEntityStorageGetCurrency);
//SubscribeLocalEvent<StorageComponent, CP14GetCurrencyEvent>(OnStorageGetCurrency);
SubscribeLocalEvent<ContainerManagerComponent, CP14GetCurrencyEvent>(OnContainerGetCurrency);
}
private void OnGetCurrency(Entity<CP14CurrencyComponent> ent, ref CP14GetCurrencyEvent args)
{
if (args.CheckedEntities.Contains(ent))
return;
var total = ent.Comp.Currency;
if (TryComp<StackComponent>(ent, out var stack))
{
@@ -41,29 +44,22 @@ public sealed partial class CP14CurrencySystem : CP14SharedCurrencySystem
}
args.Currency += total;
args.CheckedEntities.Add(ent);
}
//private void OnEntityStorageGetCurrency(Entity<EntityStorageComponent> ent, ref CP14GetCurrencyEvent args)
//{
// var total = 0;
// foreach (var entity in ent.Comp.Contents.ContainedEntities)
// {
// total += GetTotalCurrency(entity);
// }
//
// args.Currency += total;
//}
//
//private void OnStorageGetCurrency(Entity<StorageComponent> ent, ref CP14GetCurrencyEvent args)
//{
// var total = 0;
// foreach (var entity in ent.Comp.StoredItems)
// {
// total += GetTotalCurrency(entity.Key);
// }
//
// args.Currency += total;
//}
private void OnContainerGetCurrency(Entity<ContainerManagerComponent> ent, ref CP14GetCurrencyEvent args)
{
var total = 0;
foreach (var container in ent.Comp.Containers.Values)
{
foreach (var containedEnt in container.ContainedEntities)
{
total += GetTotalCurrency(containedEnt);
}
}
args.Currency += total;
}
private void OnExamine(Entity<CP14CurrencyExaminableComponent> currency, ref ExaminedEvent args)
{

View File

@@ -0,0 +1,97 @@
using Content.Server._CP14.DayCycle;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server._CP14.Vampire;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.GameTicking.Rules;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared._CP14.Vampire;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Shared.Popups;
using Robust.Shared.Timing;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14VampireRuleSystem : GameRuleSystem<CP14VampireRuleComponent>
{
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly TemperatureSystem _temperature = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly CP14DayCycleSystem _dayCycle = default!;
[Dependency] private readonly FlammableSystem _flammable = default!;
[Dependency] private readonly BodySystem _body = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14VampireComponent, MapInitEvent>(OnVampireInit);
SubscribeLocalEvent<CP14VampireComponent, CP14HungerChangedEvent>(OnVampireHungerChanged);
}
private void OnVampireHungerChanged(Entity<CP14VampireComponent> ent, ref CP14HungerChangedEvent args)
{
if (args.NewThreshold == HungerThreshold.Starving || args.NewThreshold == HungerThreshold.Dead)
{
RevealVampire(ent);
}
else
{
HideVampire(ent);
}
}
private void RevealVampire(Entity<CP14VampireComponent> ent)
{
EnsureComp<CP14VampireVisualsComponent>(ent);
}
private void HideVampire(Entity<CP14VampireComponent> ent)
{
RemCompDeferred<CP14VampireVisualsComponent>(ent);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<CP14VampireComponent, TemperatureComponent, FlammableComponent>();
while (query.MoveNext(out var uid, out var vampire, out var temperature, out var flammable))
{
if (_timing.CurTime < vampire.NextHeatTime)
continue;
vampire.NextHeatTime = _timing.CurTime + vampire.HeatFrequency;
if (!_dayCycle.TryDaylightThere(uid))
continue;
_temperature.ChangeHeat(uid, vampire.HeatUnderSunTemperature);
_popup.PopupEntity(Loc.GetString("cp14-heat-under-sun"), uid, uid, PopupType.SmallCaution);
if (temperature.CurrentTemperature > vampire.IgniteThreshold && !flammable.OnFire)
{
_flammable.AdjustFireStacks(uid, 1, flammable);
_flammable.Ignite(uid, uid, flammable);
}
}
}
private void OnVampireInit(Entity<CP14VampireComponent> ent, ref MapInitEvent args)
{
_bloodstream.ChangeBloodReagent(ent, ent.Comp.NewBloodReagent);
foreach (var (organUid, _) in _body.GetBodyOrgans(ent))
{
if (TryComp<MetabolizerComponent>(organUid, out var metabolizer) && metabolizer.MetabolizerTypes is not null)
{
metabolizer.MetabolizerTypes.Add(ent.Comp.MetabolizerType);
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Server._CP14.GameTicking.Rules.Components;
/// <summary>
/// Stores data for <see cref="CP14VampireRuleSystem"/>.
/// </summary>
[RegisterComponent, Access(typeof(CP14VampireRuleSystem))]
public sealed partial class CP14VampireRuleComponent : Component;

View File

@@ -0,0 +1,71 @@
using Content.Shared._CP14.Knowledge.Components;
using Content.Shared._CP14.Knowledge.Prototypes;
using Content.Shared.Administration;
using Content.Shared.Administration.Managers;
using Content.Shared.Database;
using Content.Shared.Verbs;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Knowledge;
// TODO: Add UI
public sealed class CP14KnowledgeAdminUtilitiesSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminManager _admin = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly CP14KnowledgeSystem _knowledge = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14KnowledgeStorageComponent, GetVerbsEvent<Verb>>(AddKnowledgeAdminVerb);
}
private void AddKnowledgeAdminVerb(Entity<CP14KnowledgeStorageComponent> entity, ref GetVerbsEvent<Verb> args)
{
if (!_admin.HasAdminFlag(args.User, AdminFlags.Admin))
return;
// Remove knowledge
foreach (var knowledge in entity.Comp.Knowledge)
{
if (!_prototype.TryIndex(knowledge, out var indexedKnowledge))
continue;
args.Verbs.Add(new Verb
{
Text = $"{Loc.GetString(indexedKnowledge.Name)}",
Message = Loc.GetString(indexedKnowledge.Desc),
Category = VerbCategory.CP14KnowledgeRemove,
Act = () =>
{
_knowledge.TryRemove(entity.Owner, knowledge);
},
Impact = LogImpact.High,
});
}
// Add knowledge
foreach (var knowledge in _prototype.EnumeratePrototypes<CP14KnowledgePrototype>())
{
// An entity with CP14AllKnowingComponent can't be taught anything,
// it already knows everything
if (_knowledge.HasKnowledge(entity.Owner, knowledge.ID))
continue;
args.Verbs.Add(new Verb
{
Text = $"{Loc.GetString(knowledge.Name)}",
Message = Loc.GetString(knowledge.Desc),
Category = VerbCategory.CP14KnowledgeAdd,
Act = () =>
{
_knowledge.TryAdd(entity.Owner, knowledge, true);
},
Impact = LogImpact.High,
});
}
}
}

View File

@@ -1,30 +1,23 @@
using System.Text;
using Content.Server.Chat.Managers;
using Content.Server.Mind;
using Content.Server.Popups;
using Content.Shared._CP14.Knowledge;
using Content.Shared._CP14.Knowledge.Components;
using Content.Shared._CP14.Knowledge.Events;
using Content.Shared._CP14.Knowledge.Prototypes;
using Content.Shared.Administration;
using Content.Shared.Administration.Logs;
using Content.Shared.Administration.Managers;
using Content.Shared.Chat;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Mind;
using Content.Shared.Verbs;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._CP14.Knowledge;
public sealed partial class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
public sealed class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
{
[Dependency] private readonly ISharedAdminManager _admin = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
@@ -35,32 +28,21 @@ public sealed partial class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
base.Initialize();
SubscribeLocalEvent<CP14AutoAddKnowledgeComponent, MapInitEvent>(AutoAddSkill);
SubscribeLocalEvent<CP14KnowledgeStorageComponent, GetVerbsEvent<Verb>>(AddKnowledgeAdminVerb);
SubscribeLocalEvent<CP14KnowledgeLearningSourceComponent, GetVerbsEvent<Verb>>(AddKnowledgeLearningVerb);
SubscribeLocalEvent<CP14KnowledgeStorageComponent, CP14KnowledgeLearnDoAfterEvent>(KnowledgeLearnedEvent);
SubscribeLocalEvent<CP14KnowledgeLearningSourceComponent, GetVerbsEvent<Verb>>(AddKnowledgeLearningVerb);
SubscribeNetworkEvent<RequestKnowledgeInfoEvent>(OnRequestKnowledgeInfoEvent);
}
private void KnowledgeLearnedEvent(Entity<CP14KnowledgeStorageComponent> ent, ref CP14KnowledgeLearnDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;
args.Handled = true;
TryLearnKnowledge(ent, args.Knowledge);
SubscribeNetworkEvent<CP14RequestKnowledgeInfoEvent>(OnRequestKnowledgeInfoEvent);
}
private void AddKnowledgeLearningVerb(Entity<CP14KnowledgeLearningSourceComponent> ent, ref GetVerbsEvent<Verb> args)
{
var user = args.User;
foreach (var knowledge in ent.Comp.Knowledges)
foreach (var knowledge in ent.Comp.Knowledge)
{
if (!_proto.TryIndex(knowledge, out var indexedKnowledge))
if (!_prototype.TryIndex(knowledge, out var indexedKnowledge))
continue;
args.Verbs.Add(new Verb()
args.Verbs.Add(new Verb
{
Text = $"{Loc.GetString(indexedKnowledge.Name)}",
Message = Loc.GetString(indexedKnowledge.Desc),
@@ -70,7 +52,7 @@ public sealed partial class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager,
user,
ent.Comp.DoAfter,
new CP14KnowledgeLearnDoAfterEvent() {Knowledge = knowledge},
new CP14KnowledgeLearnDoAfterEvent { Knowledge = knowledge },
user,
ent,
ent)
@@ -85,176 +67,137 @@ public sealed partial class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
}
}
private void AddKnowledgeAdminVerb(Entity<CP14KnowledgeStorageComponent> ent, ref GetVerbsEvent<Verb> args)
private void KnowledgeLearnedEvent(Entity<CP14KnowledgeStorageComponent> ent, ref CP14KnowledgeLearnDoAfterEvent args)
{
if (!_admin.HasAdminFlag(args.User, AdminFlags.Admin))
if (args.Cancelled || args.Handled)
return;
//Remove knowledge
foreach (var knowledge in ent.Comp.Knowledges)
{
if (!_proto.TryIndex(knowledge, out var indexedKnowledge))
continue;
args.Verbs.Add(new Verb()
{
Text = $"{Loc.GetString(indexedKnowledge.Name)}",
Message = Loc.GetString(indexedKnowledge.Desc),
Category = VerbCategory.CP14KnowledgeRemove,
Act = () =>
{
TryForgotKnowledge(ent, knowledge);
},
Impact = LogImpact.High,
});
}
//Add knowledge
foreach (var knowledge in _proto.EnumeratePrototypes<CP14KnowledgePrototype>())
{
if (ent.Comp.Knowledges.Contains(knowledge))
continue;
args.Verbs.Add(new Verb()
{
Text = $"{Loc.GetString(knowledge.Name)}",
Message = Loc.GetString(knowledge.Desc),
Category = VerbCategory.CP14KnowledgeAdd,
Act = () =>
{
TryLearnKnowledge(ent, knowledge, true);
},
Impact = LogImpact.High,
});
}
args.Handled = true;
TryAdd(ent.Owner, args.Knowledge);
}
private void AutoAddSkill(Entity<CP14AutoAddKnowledgeComponent> ent, ref MapInitEvent args)
{
foreach (var knowledge in ent.Comp.Knowledge)
{
TryLearnKnowledge(ent, knowledge);
TryAdd(ent.Owner, knowledge);
}
RemComp(ent, ent.Comp);
}
public bool TryLearnKnowledge(EntityUid uid, ProtoId<CP14KnowledgePrototype> proto, bool force = false, bool silent = false)
public bool TryAdd(Entity<CP14KnowledgeStorageComponent?> entity, ProtoId<CP14KnowledgePrototype> knowledgeId, bool force = false, bool silent = false)
{
if (!TryComp<CP14KnowledgeStorageComponent>(uid, out var knowledgeStorage))
if (!Resolve(entity, ref entity.Comp, false))
return false;
if (!_proto.TryIndex(proto, out var indexedKnowledge))
if (HasKnowledge(entity, knowledgeId))
return false;
if (knowledgeStorage.Knowledges.Contains(proto))
if (!_prototype.TryIndex(knowledgeId, out var knowledge))
return false;
foreach (var dependency in indexedKnowledge.Dependencies)
MindComponent? mindComponent;
foreach (var dependencyKnowledgeId in knowledge.Dependencies)
{
if (!_proto.TryIndex(dependency, out var indexedDependency))
if (!_prototype.TryIndex(dependencyKnowledgeId, out var dependencyKnowledge))
return false;
if (force)
{
//If we teach by force - we automatically teach all the basics that are necessary for that skill.
if (!TryLearnKnowledge(uid, dependency, true))
return false;
}
else
{
var passed = true;
var sb = new StringBuilder();
// If we teach by force - we automatically teach all the basics that are necessary for that skill.
if (force && !TryAdd(entity, dependencyKnowledge, true))
return false;
sb.Append(Loc.GetString("cp14-cant-learn-knowledge-dependencies",
("target", Loc.GetString(indexedKnowledge.Desc))));
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-cant-learn-knowledge-dependencies", ("target", Loc.GetString(knowledge.Desc))));
//We cant learnt
if (!HasKnowledge(uid, dependency))
{
passed = false;
sb.Append("\n- " + Loc.GetString(indexedDependency.Desc));
}
// We cant learnt
if (HasKnowledge(entity, dependencyKnowledge))
continue;
if (!passed)
{
if (!silent && _mind.TryGetMind(uid, out var mind, out var mindComp) && mindComp.Session is not null)
{
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", sb.ToString()));
_chat.ChatMessageToOne(
ChatChannel.Server,
sb.ToString(),
wrappedMessage,
default,
false,
mindComp.Session.Channel);
}
sb.Append($"\n- {Loc.GetString(dependencyKnowledge.Desc)}");
return false;
}
}
}
if (silent)
return false;
//TODO: coding on a sleepy head is a bad idea. Remove duplicate variables. >:(
if (!silent && _mind.TryGetMind(uid, out var mind2, out var mindComp2) && mindComp2.Session is not null)
{
var message = Loc.GetString("cp14-learned-new-knowledge", ("name", Loc.GetString(indexedKnowledge.Name)));
var wrappedMessage2 = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
if (!_mind.TryGetMind(entity, out _, out mindComponent) || mindComponent.Session is null)
return false;
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", sb.ToString()));
_chat.ChatMessageToOne(
ChatChannel.Server,
message,
wrappedMessage2,
sb.ToString(),
wrappedMessage,
default,
false,
mindComp2.Session.Channel);
mindComponent.Session.Channel);
return false;
}
_adminLogger.Add(
LogType.Mind,
LogImpact.Medium,
$"{EntityManager.ToPrettyString(uid):player} learned new knowledge: {Loc.GetString(indexedKnowledge.Name)}");
return knowledgeStorage.Knowledges.Add(proto);
}
public bool TryForgotKnowledge(EntityUid uid, ProtoId<CP14KnowledgePrototype> proto, bool silent = false)
{
if (!TryComp<CP14KnowledgeStorageComponent>(uid, out var knowledgeStorage))
if (!entity.Comp.Knowledge.Add(knowledgeId))
return false;
if (!knowledgeStorage.Knowledges.Contains(proto))
_adminLogger.Add(LogType.Mind, LogImpact.Medium, $"{EntityManager.ToPrettyString(entity):player} learned new knowledge: {Loc.GetString(knowledge.Name)}");
// TODO: coding on a sleepy head is a bad idea. Remove duplicate variables. >:(
if (silent)
return true;
if (!_mind.TryGetMind(entity, out _, out mindComponent) || mindComponent.Session is null)
return false;
if (!_proto.TryIndex(proto, out var indexedKnowledge))
return false;
var message = Loc.GetString("cp14-learned-new-knowledge", ("name", Loc.GetString(knowledge.Name)));
var wrappedMessage2 = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
knowledgeStorage.Knowledges.Remove(proto);
_chat.ChatMessageToOne(
ChatChannel.Server,
message,
wrappedMessage2,
default,
false,
mindComponent.Session.Channel);
if (_mind.TryGetMind(uid, out var mind, out var mindComp) && mindComp.Session is not null)
{
if (!silent)
{
var message = Loc.GetString("cp14-forgot-knowledge", ("name", Loc.GetString(indexedKnowledge.Name)));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chat.ChatMessageToOne(
ChatChannel.Server,
message,
wrappedMessage,
default,
false,
mindComp.Session.Channel);
}
}
_adminLogger.Add(
LogType.Mind,
LogImpact.Medium,
$"{EntityManager.ToPrettyString(uid):player} forgot knowledge: {Loc.GetString(indexedKnowledge.Name)}");
return true;
}
private void OnRequestKnowledgeInfoEvent(RequestKnowledgeInfoEvent msg, EntitySessionEventArgs args)
public bool TryRemove(Entity<CP14KnowledgeStorageComponent?> entity, ProtoId<CP14KnowledgePrototype> proto, bool silent = false)
{
if (!Resolve(entity, ref entity.Comp, false))
return false;
if (!entity.Comp.Knowledge.Contains(proto))
return false;
if (!_prototype.TryIndex(proto, out var indexedKnowledge))
return false;
if (!entity.Comp.Knowledge.Remove(proto))
return false;
_adminLogger.Add(LogType.Mind, LogImpact.Medium, $"{EntityManager.ToPrettyString(entity):player} forgot knowledge: {Loc.GetString(indexedKnowledge.Name)}");
if (silent)
return true;
if (!_mind.TryGetMind(entity, out _, out var mindComp) || mindComp.Session is null)
return true;
var message = Loc.GetString("cp14-forgot-knowledge", ("name", Loc.GetString(indexedKnowledge.Name)));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chat.ChatMessageToOne(
ChatChannel.Server,
message,
wrappedMessage,
default,
false,
mindComp.Session.Channel);
return true;
}
private void OnRequestKnowledgeInfoEvent(CP14RequestKnowledgeInfoEvent msg, EntitySessionEventArgs args)
{
if (!args.SenderSession.AttachedEntity.HasValue || args.SenderSession.AttachedEntity != GetEntity(msg.NetEntity))
return;
@@ -264,6 +207,6 @@ public sealed partial class CP14KnowledgeSystem : SharedCP14KnowledgeSystem
if (!TryComp<CP14KnowledgeStorageComponent>(entity, out var knowledgeComp))
return;
RaiseNetworkEvent(new CP14KnowledgeInfoEvent(GetNetEntity(entity),knowledgeComp.Knowledges), args.SenderSession);
RaiseNetworkEvent(new CP14KnowledgeInfoEvent(GetNetEntity(entity),knowledgeComp.Knowledge), args.SenderSession);
}
}

View File

@@ -1,10 +1,9 @@
using Content.Shared._CP14.Knowledge;
using Content.Shared._CP14.Knowledge.Prototypes;
using Content.Shared.Roles;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Knowledge;
namespace Content.Server._CP14.Knowledge.Specials;
/// <summary>
/// a component that can be hung on an entity to immediately teach it some skills
@@ -13,15 +12,16 @@ namespace Content.Server._CP14.Knowledge;
public sealed partial class CP14AddKnowledgeSpecial : JobSpecial
{
[DataField(required: true), ViewVariables(VVAccess.ReadOnly)]
public List<ProtoId<CP14KnowledgePrototype>> Knowledge = new();
public List<ProtoId<CP14KnowledgePrototype>> Knowledge = [];
public override void AfterEquip(EntityUid mob)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var knowledgeSystem = entMan.System<CP14KnowledgeSystem>();
foreach (var knowledge in Knowledge)
{
knowledgeSystem.TryLearnKnowledge(mob, knowledge, true, true);
knowledgeSystem.TryAdd(mob, knowledge, true, true);
}
}
}

View File

@@ -0,0 +1,10 @@
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.MagicSpell;
[RegisterComponent]
public sealed partial class CP14AutoLearnActionComponent : Component
{
[DataField(required: true)]
public HashSet<EntProtoId> Actions = new();
}

View File

@@ -7,6 +7,7 @@ using Content.Shared._CP14.MagicSpell;
using Content.Shared._CP14.MagicSpell.Components;
using Content.Shared._CP14.MagicSpell.Events;
using Content.Shared._CP14.MagicSpell.Spells;
using Content.Shared.Actions;
using Content.Shared.FixedPoint;
using Content.Shared.Projectiles;
using Content.Shared.Throwing;
@@ -25,6 +26,7 @@ public sealed partial class CP14MagicSystem : CP14SharedMagicSystem
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
public override void Initialize()
{
@@ -43,6 +45,17 @@ public sealed partial class CP14MagicSystem : CP14SharedMagicSystem
SubscribeLocalEvent<CP14MagicEffectManaCostComponent, CP14MagicEffectConsumeResourceEvent>(OnManaConsume);
SubscribeLocalEvent<CP14MagicEffectRequiredMusicToolComponent, CP14CastMagicEffectAttemptEvent>(OnMusicCheck);
SubscribeLocalEvent<CP14AutoLearnActionComponent, MapInitEvent>(OnAutoLearnAction);
}
private void OnAutoLearnAction(Entity<CP14AutoLearnActionComponent> ent, ref MapInitEvent args)
{
foreach (var action in ent.Comp.Actions)
{
_action.AddAction(ent, action);
}
RemCompDeferred<CP14AutoLearnActionComponent>(ent);
}
private void OnProjectileHit(Entity<CP14SpellEffectOnHitComponent> ent, ref ThrowDoHitEvent args)

View File

@@ -1,20 +0,0 @@
using Content.Server._CP14.Objectives.Systems;
using Robust.Shared.Utility;
namespace Content.Server._CP14.Objectives.Components;
[RegisterComponent, Access(typeof(CP14CurrencyCollectConditionSystem))]
public sealed partial class CP14CurrencyStoredConditionComponent : Component
{
[DataField]
public int Currency = 1000;
[DataField(required: true)]
public LocId ObjectiveText;
[DataField(required: true)]
public LocId ObjectiveDescription;
[DataField(required: true)]
public SpriteSpecifier ObjectiveSprite;
}

View File

@@ -0,0 +1,25 @@
using Content.Server._CP14.Objectives.Systems;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server._CP14.Objectives.Components;
/// <summary>
/// The player must be the richest among the players among the specified list of roles
/// </summary>
[RegisterComponent, Access(typeof(CP14RichestJobConditionSystem))]
public sealed partial class CP14RichestJobConditionComponent : Component
{
[DataField(required: true)]
public ProtoId<JobPrototype> Job;
[DataField(required: true)]
public LocId ObjectiveText;
[DataField(required: true)]
public LocId ObjectiveDescription;
[DataField(required: true)]
public SpriteSpecifier ObjectiveSprite;
}

View File

@@ -16,24 +16,13 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly CP14SharedCurrencySystem _currency = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
private EntityQuery<ContainerManagerComponent> _containerQuery;
private HashSet<Entity<TransformComponent>> _nearestEnts = new();
public override void Initialize()
{
base.Initialize();
_containerQuery = GetEntityQuery<ContainerManagerComponent>();
SubscribeLocalEvent<CP14CurrencyCollectConditionComponent, ObjectiveAfterAssignEvent>(OnCollectAfterAssign);
SubscribeLocalEvent<CP14CurrencyStoredConditionComponent, ObjectiveAfterAssignEvent>(OnStoredAfterAssign);
SubscribeLocalEvent<CP14CurrencyCollectConditionComponent, ObjectiveGetProgressEvent>(OnCollectGetProgress);
SubscribeLocalEvent<CP14CurrencyStoredConditionComponent, ObjectiveGetProgressEvent>(OnStoredGetProgress);
}
private void OnCollectAfterAssign(Entity<CP14CurrencyCollectConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
@@ -43,103 +32,23 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite);
}
private void OnStoredAfterAssign(Entity<CP14CurrencyStoredConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
_metaData.SetEntityName(condition.Owner, Loc.GetString(condition.Comp.ObjectiveText, ("coins", _currency.GetCurrencyPrettyString(condition.Comp.Currency))), args.Meta);
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription, ("coins", _currency.GetCurrencyPrettyString(condition.Comp.Currency))), args.Meta);
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite);
}
private void OnCollectGetProgress(Entity<CP14CurrencyCollectConditionComponent> condition, ref ObjectiveGetProgressEvent args)
{
args.Progress = GetProgress(args.Mind, condition);
}
private void OnStoredGetProgress(Entity<CP14CurrencyStoredConditionComponent> condition, ref ObjectiveGetProgressEvent args)
{
args.Progress = GetStoredProgress(args.Mind, condition);
}
private float GetProgress(MindComponent mind, CP14CurrencyCollectConditionComponent condition)
{
if (!_containerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
var count = 0;
if (mind.OwnedEntity is null)
return 0;
var containerStack = new Stack<ContainerManagerComponent>();
var count = 0;
//check pulling object
if (TryComp<PullerComponent>(mind.OwnedEntity,
out var pull)) //TO DO: to make the code prettier? don't like the repetition
{
var pulledEntity = pull.Pulling;
if (pulledEntity != null)
{
CheckEntity(pulledEntity.Value, ref containerStack, ref count);
}
}
// recursively check each container for the item
// checks inventory, bag, implants, etc.
do
{
foreach (var container in currentManager.Containers.Values)
{
foreach (var entity in container.ContainedEntities)
{
// check if this is the item
count += _currency.GetTotalCurrency(entity);
// if it is a container check its contents
if (_containerQuery.TryGetComponent(entity, out var containerManager))
containerStack.Push(containerManager);
}
}
} while (containerStack.TryPop(out currentManager));
count += _currency.GetTotalCurrency(mind.OwnedEntity.Value);
var result = count / (float)condition.Currency;
result = Math.Clamp(result, 0, 1);
return result;
}
private float GetStoredProgress(MindComponent mind, CP14CurrencyStoredConditionComponent condition)
{
var containerStack = new Stack<ContainerManagerComponent>();
var count = 0;
var areasQuery = AllEntityQuery<StealAreaComponent, TransformComponent>();
while (areasQuery.MoveNext(out var uid, out var area, out var xform))
{
if (!area.Owners.Contains(mind.Owner))
continue;
_nearestEnts.Clear();
_lookup.GetEntitiesInRange(xform.Coordinates, area.Range, _nearestEnts);
foreach (var ent in _nearestEnts)
{
if (!_interaction.InRangeUnobstructed((uid, xform), (ent, ent.Comp), area.Range))
continue;
CheckEntity(ent, ref containerStack, ref count);
}
}
var result = count / (float)condition.Currency;
result = Math.Clamp(result, 0, 1);
return result;
}
private void CheckEntity(EntityUid entity, ref Stack<ContainerManagerComponent> containerStack, ref int counter)
{
// check if this is the item
counter += _currency.GetTotalCurrency(entity);
//we don't check the inventories of sentient entity
if (!TryComp<MindContainerComponent>(entity, out _))
{
// if it is a container check its contents
if (_containerQuery.TryGetComponent(entity, out var containerManager))
containerStack.Push(containerManager);
}
}
}

View File

@@ -0,0 +1,74 @@
using Content.Server._CP14.Objectives.Components;
using Content.Shared._CP14.Currency;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.Objectives.Systems;
using Content.Shared.Roles.Jobs;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Objectives.Systems;
public sealed class CP14RichestJobConditionSystem : EntitySystem
{
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly CP14SharedCurrencySystem _currency = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedJobSystem _job = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14RichestJobConditionComponent, ObjectiveAfterAssignEvent>(OnCollectAfterAssign);
SubscribeLocalEvent<CP14RichestJobConditionComponent, ObjectiveGetProgressEvent>(OnCollectGetProgress);
}
private void OnCollectAfterAssign(Entity<CP14RichestJobConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
if (!_proto.TryIndex(condition.Comp.Job, out var indexedJob))
return;
_metaData.SetEntityName(condition.Owner, Loc.GetString(condition.Comp.ObjectiveText), args.Meta);
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription), args.Meta);
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite);
}
private void OnCollectGetProgress(Entity<CP14RichestJobConditionComponent> condition, ref ObjectiveGetProgressEvent args)
{
args.Progress = GetProgress(args.MindId, args.Mind, condition);
}
private float GetProgress(EntityUid mindId, MindComponent mind, CP14RichestJobConditionComponent condition)
{
if (mind.OwnedEntity is null)
return 0;
var ourValue = _currency.GetTotalCurrency(mind.OwnedEntity.Value);
var otherMaxValue = 0;
var allHumans = _mind.GetAliveHumans(mindId);
if (allHumans.Count == 0)
return 1; // No one to compare to, so we're the richest.
foreach (var otherHuman in allHumans)
{
if (!_job.MindTryGetJob(otherHuman, out var otherJob))
continue;
if (otherJob != condition.Job)
continue;
if (otherHuman.Comp.OwnedEntity is null)
continue;
var otherValue = _currency.GetTotalCurrency(otherHuman.Comp.OwnedEntity.Value);
if (otherValue > otherMaxValue)
otherMaxValue = otherValue;
}
// if several players have the same amount of money, no one wins.
return ourValue == otherMaxValue ? 0.99f : Math.Clamp(ourValue / (float)otherMaxValue, 0, 1);
}
}

View File

@@ -1,16 +1,11 @@
using System.Text;
using Content.Server._CP14.Cargo;
using Content.Server._CP14.Currency;
using Content.Server.Mind;
using Content.Server.Station.Components;
using Content.Server.Station.Events;
using Content.Shared._CP14.Cargo;
using Content.Shared._CP14.Currency;
using Content.Shared.Paper;
using Content.Shared.Station.Components;
using Content.Shared.Storage.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
@@ -41,15 +36,15 @@ public sealed partial class CP14SalarySystem : EntitySystem
{
base.Update(frameTime);
var query = EntityQueryEnumerator<CP14StationSalaryComponent, CP14StationTravelingStoreShipTargetComponent>();
while (query.MoveNext(out var uid, out var salary, out var store))
{
if (_timing.CurTime < salary.NextSalaryTime)
continue;
salary.NextSalaryTime = _timing.CurTime + salary.SalaryFrequency;
_cargo.AddBuyQueue((uid, store), new List<EntProtoId> {salary.SalaryProto});
}
//var query = EntityQueryEnumerator<CP14StationSalaryComponent, CP14StationTravelingStoreShipTargetComponent>();
//while (query.MoveNext(out var uid, out var salary, out var store))
//{
// if (_timing.CurTime < salary.NextSalaryTime)
// continue;
//
// salary.NextSalaryTime = _timing.CurTime + salary.SalaryFrequency;
// _cargo.AddBuyQueue((uid, store), new List<EntProtoId> {salary.SalaryProto});
//}
}
private void OnSalaryInit(Entity<CP14SalarySpawnerComponent> ent, ref MapInitEvent args)

View File

@@ -0,0 +1,11 @@
using Content.Shared.Roles;
namespace Content.Server._CP14.Roles;
/// <summary>
/// Added to mind role entities to tag that they are a Vampire.
/// </summary>
[RegisterComponent]
public sealed partial class CP14VampireRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -0,0 +1,29 @@
using Content.Server._CP14.GameTicking.Rules;
using Content.Shared.Body.Prototypes;
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Vampire;
[RegisterComponent]
[Access(typeof(CP14VampireRuleSystem))]
public sealed partial class CP14VampireComponent : Component
{
[DataField]
public ProtoId<ReagentPrototype> NewBloodReagent = "CP14BloodVampire";
[DataField]
public ProtoId<MetabolizerTypePrototype> MetabolizerType = "CP14Vampire";
[DataField]
public float HeatUnderSunTemperature = 12000f;
[DataField]
public TimeSpan HeatFrequency = TimeSpan.FromSeconds(1);
[DataField]
public TimeSpan NextHeatTime = TimeSpan.Zero;
[DataField]
public float IgniteThreshold = 350f;
}

View File

@@ -0,0 +1,7 @@
using Content.Shared._CP14.Vampire;
namespace Content.Server._CP14.Vampire;
public sealed class CP14VampireVisualsSystem : CP14SharedVampireVisualsSystem
{
}

View File

@@ -134,6 +134,11 @@ public sealed class HungerSystem : EntitySystem
if (calculatedHungerThreshold == component.CurrentThreshold)
return;
//CP14 Raise hunger event for vampire
var ev = new CP14HungerChangedEvent(component.CurrentThreshold, calculatedHungerThreshold);
RaiseLocalEvent(uid, ev);
//CP14 Raise hunger event for vampire end
component.CurrentThreshold = calculatedHungerThreshold;
DoHungerThresholdEffects(uid, component);
}
@@ -277,3 +282,10 @@ public sealed class HungerSystem : EntitySystem
}
}
}
public sealed class CP14HungerChangedEvent(HungerThreshold oldThreshold, HungerThreshold newThreshold) : EntityEventArgs
{
public HungerThreshold OldThreshold { get; } = oldThreshold;
public HungerThreshold NewThreshold { get; } = newThreshold;
}

View File

@@ -1,9 +0,0 @@
namespace Content.Shared._CP14.Cargo;
/// <summary>
/// marks an entity into which trade with the city will put money when selling, or take it when buying.
/// </summary>
[RegisterComponent]
public sealed partial class CP14CargoMoneyBoxComponent : Component
{
}

View File

@@ -1,11 +0,0 @@
namespace Content.Shared._CP14.Cargo;
/// <summary>
/// Allows users to view information on city trading opportunities
/// </summary>
[RegisterComponent]
public sealed partial class CP14CargoStoreComponent : Component
{
[DataField]
public EntityUid? Station = null;
}

View File

@@ -1,11 +0,0 @@
namespace Content.Shared._CP14.Cargo;
[RegisterComponent]
public sealed partial class CP14SellingPalettComponent : Component
{
}
[RegisterComponent]
public sealed partial class CP14BuyingPalettComponent : Component
{
}

View File

@@ -14,18 +14,15 @@ public enum CP14StoreUiKey
[Serializable, NetSerializable]
public sealed class CP14StoreUiState : BoundUserInterfaceState
{
public readonly string ShopName;
public readonly HashSet<CP14StoreUiProductEntry> ProductsBuy;
public readonly HashSet<CP14StoreUiProductEntry> ProductsSell;
public bool OnStation;
public readonly TimeSpan NextTravelTime;
public CP14StoreUiState(HashSet<CP14StoreUiProductEntry> productsBuy, HashSet<CP14StoreUiProductEntry> productsSell, bool onStation, TimeSpan time)
public CP14StoreUiState(string name, HashSet<CP14StoreUiProductEntry> productsBuy, HashSet<CP14StoreUiProductEntry> productsSell)
{
ShopName = name;
ProductsBuy = productsBuy;
ProductsSell = productsSell;
OnStation = onStation;
NextTravelTime = time;
}
}
@@ -33,16 +30,18 @@ public sealed class CP14StoreUiState : BoundUserInterfaceState
public readonly struct CP14StoreUiProductEntry : IEquatable<CP14StoreUiProductEntry>
{
public readonly string ProtoId;
public readonly SpriteSpecifier Icon;
public readonly SpriteSpecifier? Icon;
public readonly EntProtoId? EntityView;
public readonly string Name;
public readonly string Desc;
public readonly int Price;
public readonly bool Special;
public CP14StoreUiProductEntry(string protoId, SpriteSpecifier icon, string name, string desc, int price, bool special)
public CP14StoreUiProductEntry(string protoId, SpriteSpecifier? icon, EntProtoId? entityView, string name, string desc, int price, bool special)
{
ProtoId = protoId;
Icon = icon;
EntityView = entityView;
Name = name;
Desc = desc;
Price = price;

View File

@@ -0,0 +1,17 @@
using Content.Shared._CP14.Cargo.Prototype;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Cargo;
/// <summary>
/// Allows users to view information on faction trading opportunities
/// </summary>
[RegisterComponent]
public sealed partial class CP14TradingInfoBoardComponent : Component
{
[DataField]
public EntityUid? TradingPortal = null;
[DataField]
public ProtoId<CP14StoreFactionPrototype>? CahcedFaction;
}

View File

@@ -1,36 +1,29 @@
using Content.Shared._CP14.Cargo.Prototype;
using Content.Shared.Destructible.Thresholds;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo;
/// <summary>
/// Add to the station so that traveling store ship starts running on it
/// Add to the station so ...
/// </summary>
[RegisterComponent]
public sealed partial class CP14StationTravelingStoreShipTargetComponent : Component
[NetworkedComponent, RegisterComponent, AutoGenerateComponentPause, AutoGenerateComponentState]
public sealed partial class CP14TradingPortalComponent : Component
{
[DataField]
public EntityUid? Shuttle;
[DataField(required: true), AutoNetworkedField]
public ProtoId<CP14StoreFactionPrototype> Faction = default;
[DataField]
public EntityUid? TradePostMap;
public EntityCoordinates? TradingPosition = null;
[DataField]
public bool OnStation;
[DataField, AutoNetworkedField]
public TimeSpan Delay = TimeSpan.FromSeconds(3f);
[DataField]
public ResPath ShuttlePath = new("/Maps/_CP14/Ships/cargo_shuttle.yml");
[DataField]
public TimeSpan NextTravelTime = TimeSpan.Zero;
[DataField]
public TimeSpan StationWaitTime = TimeSpan.FromMinutes(6);
[DataField]
public TimeSpan TradePostWaitTime = TimeSpan.FromMinutes(4);
[DataField, AutoNetworkedField]
[AutoPausedField]
public TimeSpan ProcessFinishTime = TimeSpan.Zero;
/// <summary>
/// Available to random selecting and pusharing
@@ -54,7 +47,7 @@ public sealed partial class CP14StationTravelingStoreShipTargetComponent : Compo
public Dictionary<CP14StoreBuyPositionPrototype, int> CurrentSpecialBuyPositions = new(); //Proto, price
[DataField]
public MinMax SpecialBuyPositionCount = new(1, 2);
public int SpecialBuyPositionCount = 2;
[DataField]
public Dictionary<CP14StoreSellPositionPrototype, int> CurrentSellPositions = new(); //Proto, price
@@ -63,14 +56,8 @@ public sealed partial class CP14StationTravelingStoreShipTargetComponent : Compo
public Dictionary<CP14StoreSellPositionPrototype, int> CurrentSpecialSellPositions = new(); //Proto, price
[DataField]
public MinMax SpecialSellPositionCount = new(1, 2);
public int SpecialSellPositionCount = 2;
[DataField]
public int Balance = 0;
/// <summary>
/// a queue of purchased items. The oldest purchases are taken out one by one to be unloaded onto the ship
/// </summary>
[DataField]
public Queue<EntProtoId> BuyedQueue = new();
}

View File

@@ -1,21 +1,50 @@
using System.Text;
using Content.Shared.Stacks;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo.Prototype.BuyServices;
public sealed partial class CP14BuyItemsService : CP14StoreBuyService
{
[DataField(required: true)]
public Dictionary<EntProtoId, int> Product = new();
public EntProtoId Product;
public override void Buy(EntityManager entManager, IPrototypeManager prototype, Entity<CP14StationTravelingStoreShipTargetComponent> station)
[DataField]
public int Count = 1;
public override void Buy(EntityManager entManager,
IPrototypeManager prototype,
Entity<CP14TradingPortalComponent> portal)
{
foreach (var (protoId, count) in Product)
var storageSystem = entManager.System<SharedEntityStorageSystem>();
for (var i = 0; i < Count; i++)
{
for (var i = 0; i < count; i++)
{
station.Comp.BuyedQueue.Enqueue(protoId);
}
var spawned = entManager.Spawn(Product);
storageSystem.Insert(spawned, portal);
}
}
public override string GetName(IPrototypeManager protoMan)
{
if (!protoMan.TryIndex(Product, out var indexedProduct))
return ":3";
var count = Count;
if (indexedProduct.TryGetComponent<StackComponent>(out var stack))
count *= stack.Count;
return Count > 0 ? $"{indexedProduct.Name} x{count}" : indexedProduct.Name;
}
public override EntProtoId? GetEntityView(IPrototypeManager protoManager)
{
return Product;
}
public override SpriteSpecifier? GetTexture(IPrototypeManager protoManager)
{
return null;
}
}

View File

@@ -1,5 +1,5 @@
using System.Text;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo.Prototype.BuyServices;
@@ -14,17 +14,23 @@ public sealed partial class CP14UnlockPositionsService : CP14StoreBuyService
[DataField]
public HashSet<ProtoId<CP14StoreBuyPositionPrototype>> RemoveBuyPositions = new();
public override void Buy(EntityManager entManager, IPrototypeManager prototype, Entity<CP14StationTravelingStoreShipTargetComponent> station)
[DataField]
public HashSet<ProtoId<CP14StoreSellPositionPrototype>> RemoveSellPositions = new();
[DataField]
public SpriteSpecifier? Icon = null;
[DataField]
public LocId Name = string.Empty;
public override void Buy(EntityManager entManager, IPrototypeManager prototype, Entity<CP14TradingPortalComponent> portal)
{
foreach (var buy in AddBuyPositions)
{
if (!prototype.TryIndex(buy, out var indexedBuy))
continue;
if (station.Comp.AvailableBuyPosition.Contains(indexedBuy))
continue;
station.Comp.AvailableBuyPosition.Add(indexedBuy);
portal.Comp.AvailableBuyPosition.Add(indexedBuy);
}
foreach (var sell in AddSellPositions)
@@ -32,10 +38,7 @@ public sealed partial class CP14UnlockPositionsService : CP14StoreBuyService
if (!prototype.TryIndex(sell, out var indexedSell))
continue;
if (station.Comp.AvailableSellPosition.Contains(indexedSell))
continue;
station.Comp.AvailableSellPosition.Add(indexedSell);
portal.Comp.AvailableSellPosition.Add(indexedSell);
}
foreach (var rBuy in RemoveBuyPositions)
@@ -43,10 +46,36 @@ public sealed partial class CP14UnlockPositionsService : CP14StoreBuyService
if (!prototype.TryIndex(rBuy, out var indexedBuy))
continue;
if (!station.Comp.AvailableBuyPosition.Contains(indexedBuy))
if (!portal.Comp.AvailableBuyPosition.Contains(indexedBuy))
continue;
station.Comp.AvailableBuyPosition.Remove(indexedBuy);
portal.Comp.AvailableBuyPosition.Remove(indexedBuy);
}
foreach (var rSell in RemoveSellPositions)
{
if (!prototype.TryIndex(rSell, out var indexedSell))
continue;
if (!portal.Comp.AvailableSellPosition.Contains(indexedSell))
continue;
portal.Comp.AvailableSellPosition.Remove(indexedSell);
}
}
public override string GetName(IPrototypeManager protoMan)
{
return Loc.GetString(Name);
}
public override EntProtoId? GetEntityView(IPrototypeManager protoManager)
{
return null;
}
public override SpriteSpecifier? GetTexture(IPrototypeManager protoManager)
{
return Icon;
}
}

View File

@@ -1,4 +1,3 @@
using Content.Shared.Destructible.Thresholds;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -17,23 +16,20 @@ public sealed partial class CP14StoreBuyPositionPrototype : IPrototype
[DataField(required: true)]
public int Price = 100;
[DataField(required: true)]
public LocId Name = string.Empty;
/// <summary>
/// a unique code that can be used to purchase items.
/// </summary>
[DataField(required: true)]
public string Code = string.Empty;
[DataField(required: true)]
public CP14StoreBuyService Service = default!;
[DataField]
public LocId Desc = string.Empty;
public SpriteSpecifier? IconOverride;
[DataField(required: true)]
public SpriteSpecifier Icon = default!;
[DataField(required: true)]
public List<CP14StoreBuyService> Services = new();
[DataField]
public LocId? NameOverride;
[DataField]
public bool RoundstartAvailable = true;
@@ -42,12 +38,27 @@ public sealed partial class CP14StoreBuyPositionPrototype : IPrototype
/// If true, this item will randomly appear under the Special Offer heading. With a chance to show up every time the ship arrives.
/// </summary>
[DataField]
public bool Special = false;
public bool Special;
[DataField]
public HashSet<ProtoId<CP14StoreFactionPrototype>> Factions = new();
}
[ImplicitDataDefinitionForInheritors]
[MeansImplicitUse]
public abstract partial class CP14StoreBuyService
{
public abstract void Buy(EntityManager entManager, IPrototypeManager prototype, Entity<CP14StationTravelingStoreShipTargetComponent> station);
public abstract void Buy(EntityManager entManager, IPrototypeManager prototype, Entity<CP14TradingPortalComponent> portal);
public abstract string GetName(IPrototypeManager protoMan);
/// <summary>
/// You can specify an icon generated from an entity. It will support layering, colour changes and other layer options. Return null to disable.
/// </summary>
public abstract EntProtoId? GetEntityView(IPrototypeManager protoManager);
/// <summary>
/// You can specify the texture directly. Return null to disable.
/// </summary>
public abstract SpriteSpecifier? GetTexture(IPrototypeManager protoManager);
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Cargo.Prototype;
[Prototype("storeFaction")]
public sealed partial class CP14StoreFactionPrototype : IPrototype
{
[IdDataField, ViewVariables]
public string ID { get; private set; } = default!;
[DataField(required: true)]
public LocId Name = string.Empty;
[DataField]
public LocId Desc = string.Empty;
}

View File

@@ -17,15 +17,6 @@ public sealed partial class CP14StoreSellPositionPrototype : IPrototype
[DataField(required: true)]
public int Price = 100;
[DataField(required: true)]
public LocId Name = string.Empty;
[DataField]
public LocId Desc = string.Empty;
[DataField(required: true)]
public SpriteSpecifier Icon = default!;
[DataField(required: true)]
public CP14StoreSellService Service = default!;
@@ -37,6 +28,9 @@ public sealed partial class CP14StoreSellPositionPrototype : IPrototype
/// </summary>
[DataField]
public bool Special = false;
[DataField]
public HashSet<ProtoId<CP14StoreFactionPrototype>> Factions = new();
}
[ImplicitDataDefinitionForInheritors]
@@ -44,4 +38,16 @@ public sealed partial class CP14StoreSellPositionPrototype : IPrototype
public abstract partial class CP14StoreSellService
{
public abstract bool TrySell(EntityManager entManager, HashSet<EntityUid> entities);
public abstract string GetName(IPrototypeManager protoMan);
/// <summary>
/// You can specify an icon generated from an entity. It will support layering, colour changes and other layer options. Return null to disable.
/// </summary>
public abstract EntProtoId? GetEntityView(IPrototypeManager protoManager);
/// <summary>
/// You can specify the texture directly. Return null to disable.
/// </summary>
public abstract SpriteSpecifier? GetTexture(IPrototypeManager protoManager);
}

View File

@@ -0,0 +1,65 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo.Prototype.SellServices;
public sealed partial class CP14SellPrototypeService : CP14StoreSellService
{
[DataField(required: true)]
public EntProtoId Proto;
[DataField]
public int Count = 1;
public override bool TrySell(EntityManager entManager, HashSet<EntityUid> entities)
{
HashSet<EntityUid> suitable = new();
var needCount = Count;
foreach (var ent in entities)
{
if (needCount <= 0)
break;
if (!entManager.TryGetComponent<MetaDataComponent>(ent, out var metaData))
continue;
if (metaData.EntityPrototype is null)
continue;
if (metaData.EntityPrototype != Proto)
continue;
suitable.Add(ent);
needCount -= 1;
}
if (needCount > 0)
return false;
foreach (var selledEnt in suitable)
{
entManager.QueueDeleteEntity(selledEnt);
}
return true;
}
public override string GetName(IPrototypeManager protoMan)
{
if (!protoMan.TryIndex(Proto, out var proto))
return ":3";
return $"{proto.Name} x{Count}";
}
public override EntProtoId? GetEntityView(IPrototypeManager protoManager)
{
return Proto;
}
public override SpriteSpecifier? GetTexture(IPrototypeManager protoManager)
{
return null;
}
}

View File

@@ -1,12 +1,13 @@
using Content.Shared.Stacks;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo.Prototype.SellServices;
public sealed partial class CP14SellStackService : CP14StoreSellService
{
[DataField(required: true)]
public ProtoId<StackPrototype> StackId = new();
public ProtoId<StackPrototype> StackId;
[DataField(required: true)]
public int Count = 1;
@@ -17,7 +18,7 @@ public sealed partial class CP14SellStackService : CP14StoreSellService
Dictionary<Entity<StackComponent>, int> suitable = new();
int needCount = Count;
var needCount = Count;
foreach (var ent in entities)
{
if (needCount <= 0)
@@ -49,4 +50,25 @@ public sealed partial class CP14SellStackService : CP14StoreSellService
return true;
}
public override string GetName(IPrototypeManager protoMan)
{
if (!protoMan.TryIndex(StackId, out var proto))
return ":3";
return $"{Loc.GetString(proto.Name)} x{Count}";
}
public override EntProtoId? GetEntityView(IPrototypeManager protoManager)
{
return null;
}
public override SpriteSpecifier? GetTexture(IPrototypeManager protoManager)
{
if (!protoManager.TryIndex(StackId, out var proto))
return null;
return proto.Icon;
}
}

View File

@@ -1,5 +1,6 @@
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Cargo.Prototype.SellServices;
@@ -11,13 +12,19 @@ public sealed partial class CP14SellWhitelistService : CP14StoreSellService
[DataField(required: true)]
public int Count = 1;
[DataField(required: true)]
public LocId Name = string.Empty;
[DataField(required: true)]
public SpriteSpecifier? Sprite = null;
public override bool TrySell(EntityManager entManager, HashSet<EntityUid> entities)
{
var whitelistSystem = entManager.System<EntityWhitelistSystem>();
HashSet<EntityUid> suitable = new();
int needCount = Count;
var needCount = Count;
foreach (var ent in entities)
{
if (needCount <= 0)
@@ -44,4 +51,19 @@ public sealed partial class CP14SellWhitelistService : CP14StoreSellService
return true;
}
public override string GetName(IPrototypeManager protoMan)
{
return $"{Loc.GetString(Name)} x{Count}";
}
public override EntProtoId? GetEntityView(IPrototypeManager protoManager)
{
return null;
}
public override SpriteSpecifier? GetTexture(IPrototypeManager protoManager)
{
return Sprite;
}
}

View File

@@ -46,14 +46,9 @@ public partial class CP14SharedCurrencySystem : EntitySystem
}
}
public sealed class CP14GetCurrencyEvent : EntityEventArgs
public sealed class CP14GetCurrencyEvent(int currency = 0, int multiplier = 1) : EntityEventArgs
{
public int Currency;
public float Multiplier;
public CP14GetCurrencyEvent(int cur = 0, int mult = 1)
{
Currency = cur;
Multiplier = mult;
}
public HashSet<EntityUid> CheckedEntities = new();
public int Currency = currency;
public float Multiplier = multiplier;
}

View File

@@ -0,0 +1,62 @@
using System.Text;
using Content.Shared._CP14.Knowledge.Components;
using Content.Shared.Paper;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Knowledge;
public sealed class CP14KnowledgePaperTextSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly PaperSystem _paper = default!;
public override void Initialize()
{
SubscribeLocalEvent<CP14KnowledgePaperTextComponent, MapInitEvent>(OnPaperMapInit);
}
private void OnPaperMapInit(Entity<CP14KnowledgePaperTextComponent> ent, ref MapInitEvent args)
{
if (!TryComp<PaperComponent>(ent, out var paperComponent))
return;
var paper = new Entity<PaperComponent>(ent, paperComponent);
if (!TryComp<CP14KnowledgeLearningSourceComponent>(ent, out var knowledge))
return;
if (knowledge.Knowledge.Count == 0)
return;
var content = GenerateText(knowledge);
_paper.SetContent(paper, content);
paper.Comp.EditingDisabled = true;
// Yes we need to do synchronization after such changes,
// yes predict, but we need to
Dirty(paper);
}
private string GenerateText(CP14KnowledgeLearningSourceComponent source)
{
var stringBuilder = new StringBuilder();
// Header
stringBuilder.Append(Loc.GetString("cp14-knowledge-book-pre-text"));
// Main body
foreach (var prototypeId in source.Knowledge)
{
if (!_prototype.TryIndex(prototypeId, out var indexedKnowledge))
continue;
stringBuilder.Append($"\n{Loc.GetString(indexedKnowledge.Desc)}");
}
// Footer
stringBuilder.Append($"\n\n{Loc.GetString("cp14-knowledge-book-post-text")}");
return stringBuilder.ToString();
}
}

View File

@@ -0,0 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.Knowledge.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14AllKnowingComponent : Component;

View File

@@ -4,11 +4,12 @@ using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Knowledge.Components;
/// <summary>
/// The ability to add a skill to an entity and quickly teach it some skills
/// The ability to add a <see cref="CP14KnowledgePrototype"/> to an entity
/// and quickly teach it some skills.
/// </summary>
[RegisterComponent, Access(typeof(SharedCP14KnowledgeSystem))]
public sealed partial class CP14AutoAddKnowledgeComponent : Component
{
[DataField]
public List<ProtoId<CP14KnowledgePrototype>> Knowledge = new();
public List<ProtoId<CP14KnowledgePrototype>> Knowledge = [];
}

View File

@@ -10,7 +10,7 @@ namespace Content.Shared._CP14.Knowledge.Components;
public sealed partial class CP14KnowledgeLearningSourceComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadOnly)]
public HashSet<ProtoId<CP14KnowledgePrototype>> Knowledges { get; private set; } = new();
public HashSet<ProtoId<CP14KnowledgePrototype>> Knowledge { get; private set; } = [];
[DataField]
public TimeSpan DoAfter = TimeSpan.FromSeconds(5f);

View File

@@ -1,10 +1,8 @@
namespace Content.Shared._CP14.Knowledge.Components;
/// <summary>
/// automatically generates content for PaperComponent, based on the knowledge that can be learnt from this object
/// Automatically generates content for PaperComponent,
/// based on the knowledge that can be learnt from this object.
/// </summary>
[RegisterComponent, Access(typeof(SharedCP14KnowledgeSystem))]
public sealed partial class CP14KnowledgePaperTextComponent : Component
{
}
public sealed partial class CP14KnowledgePaperTextComponent : Component;

View File

@@ -4,11 +4,11 @@ using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Knowledge.Components;
/// <summary>
/// a list of skills learned by this entity
/// A list of <see cref="CP14KnowledgePrototype"/> learned by this entity.
/// </summary>
[RegisterComponent, Access(typeof(SharedCP14KnowledgeSystem))]
public sealed partial class CP14KnowledgeStorageComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadOnly)]
public HashSet<ProtoId<CP14KnowledgePrototype>> Knowledges { get; private set; } = new();
public HashSet<ProtoId<CP14KnowledgePrototype>> Knowledge { get; private set; } = [];
}

View File

@@ -0,0 +1,18 @@
using Content.Shared._CP14.Knowledge.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Knowledge.Events;
[Serializable, NetSerializable]
public sealed class CP14KnowledgeInfoEvent : EntityEventArgs
{
public readonly NetEntity NetEntity;
public readonly HashSet<ProtoId<CP14KnowledgePrototype>> AllKnowledge;
public CP14KnowledgeInfoEvent(NetEntity netEntity, HashSet<ProtoId<CP14KnowledgePrototype>> allKnowledge)
{
NetEntity = netEntity;
AllKnowledge = allKnowledge;
}
}

View File

@@ -0,0 +1,17 @@
using Content.Shared._CP14.Knowledge.Prototypes;
using Content.Shared.DoAfter;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Knowledge.Events;
[Serializable, NetSerializable]
public sealed partial class CP14KnowledgeLearnDoAfterEvent : DoAfterEvent
{
public ProtoId<CP14KnowledgePrototype> Knowledge;
public override DoAfterEvent Clone()
{
return this;
}
}

View File

@@ -0,0 +1,18 @@
using Content.Shared._CP14.Knowledge.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Knowledge.Events;
public sealed class CP14KnowledgeUsedEvent : EntityEventArgs
{
public readonly EntityUid User;
public readonly ProtoId<CP14KnowledgePrototype> Knowledge;
public readonly float Factor;
public CP14KnowledgeUsedEvent(EntityUid uid, ProtoId<CP14KnowledgePrototype> knowledge, float factor)
{
User = uid;
Knowledge = knowledge;
Factor = factor;
}
}

View File

@@ -0,0 +1,14 @@
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Knowledge.Events;
[Serializable, NetSerializable]
public sealed class CP14RequestKnowledgeInfoEvent : EntityEventArgs
{
public readonly NetEntity NetEntity;
public CP14RequestKnowledgeInfoEvent(NetEntity netEntity)
{
NetEntity = netEntity;
}
}

View File

@@ -8,19 +8,18 @@ namespace Content.Shared._CP14.Knowledge.Prototypes;
[Prototype("CP14Knowledge")]
public sealed partial class CP14KnowledgePrototype : IPrototype
{
[ViewVariables]
[IdDataField]
public string ID { get; private set; } = default!;
[DataField(required: true)]
public LocId Name { get; private set; } = default!;
public LocId Name { get; private set; }
[DataField]
public LocId Desc{ get; private set; } = default!;
public LocId Desc { get; private set; }
/// <summary>
/// to study this knowledge, other knowledge on which it is based may be necessary.
/// To study this knowledge, other knowledge on which it is based may be necessary.
/// </summary>
[DataField]
public HashSet<ProtoId<CP14KnowledgePrototype>> Dependencies = new();
public HashSet<ProtoId<CP14KnowledgePrototype>> Dependencies = [];
}

View File

@@ -1,122 +1,31 @@
using System.Text;
using Content.Shared._CP14.Knowledge.Components;
using Content.Shared._CP14.Knowledge.Events;
using Content.Shared._CP14.Knowledge.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.Ghost;
using Content.Shared.MagicMirror;
using Content.Shared.Paper;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Knowledge;
public abstract partial class SharedCP14KnowledgeSystem : EntitySystem
public abstract class SharedCP14KnowledgeSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly PaperSystem _paper = default!;
public override void Initialize()
public bool HasKnowledge(Entity<CP14KnowledgeStorageComponent?> entity, ProtoId<CP14KnowledgePrototype> knowledge)
{
SubscribeLocalEvent<CP14KnowledgePaperTextComponent, MapInitEvent>(OnPaperMapInit);
if (HasComp<CP14AllKnowingComponent>(entity))
return true;
return Resolve(entity, ref entity.Comp, false) && entity.Comp.Knowledge.Contains(knowledge);
}
private void OnPaperMapInit(Entity<CP14KnowledgePaperTextComponent> ent, ref MapInitEvent args)
public bool TryUseKnowledge(Entity<CP14KnowledgeStorageComponent?> entity, ProtoId<CP14KnowledgePrototype> knowledge, float factor = 1f)
{
if (!TryComp<PaperComponent>(ent, out var paper))
return;
if (!TryComp<CP14KnowledgeLearningSourceComponent>(ent, out var knowledge))
return;
if (knowledge.Knowledges.Count <= 0)
return;
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-knowledge-book-pre-text"));
foreach (var k in knowledge.Knowledges)
{
if (!_proto.TryIndex(k, out var indexedKnowledge))
continue;
sb.Append($"\n{Loc.GetString(indexedKnowledge.Desc)}");
}
sb.Append($"\n\n{Loc.GetString("cp14-knowledge-book-post-text")}");
_paper.SetContent((ent, paper), sb.ToString());
paper.EditingDisabled = true;
}
public bool UseKnowledge(EntityUid uid,
ProtoId<CP14KnowledgePrototype> knowledge,
float factor = 1f,
CP14KnowledgeStorageComponent? knowledgeStorage = null)
{
if (!Resolve(uid, ref knowledgeStorage, false))
if (!Resolve(entity, ref entity.Comp, false))
return false;
if (!knowledgeStorage.Knowledges.Contains(knowledge))
if (!entity.Comp.Knowledge.Contains(knowledge))
return false;
var ev = new CP14KnowledgeUsedEvent(uid, knowledge, factor);
RaiseLocalEvent(uid, ev);
var ev = new CP14KnowledgeUsedEvent(entity, knowledge, factor);
RaiseLocalEvent(entity, ev);
return true;
}
public bool HasKnowledge(EntityUid uid,
ProtoId<CP14KnowledgePrototype> knowledge,
CP14KnowledgeStorageComponent? knowledgeStorage = null)
{
if (HasComp<GhostComponent>(uid)) //All-knowing ghosts
return true;
if (!Resolve(uid, ref knowledgeStorage, false))
return false;
return knowledgeStorage.Knowledges.Contains(knowledge);
}
}
public sealed class CP14KnowledgeUsedEvent : EntityEventArgs
{
public readonly EntityUid User;
public readonly ProtoId<CP14KnowledgePrototype> Knowledge;
public readonly float Factor;
public CP14KnowledgeUsedEvent(EntityUid uid, ProtoId<CP14KnowledgePrototype> knowledge, float factor)
{
User = uid;
Knowledge = knowledge;
Factor = factor;
}
}
[Serializable, NetSerializable]
public sealed partial class CP14KnowledgeLearnDoAfterEvent : DoAfterEvent
{
public ProtoId<CP14KnowledgePrototype> Knowledge;
public override DoAfterEvent Clone() => this;
}
[Serializable, NetSerializable]
public sealed class CP14KnowledgeInfoEvent : EntityEventArgs
{
public readonly NetEntity NetEntity;
public readonly HashSet<ProtoId<CP14KnowledgePrototype>> AllKnowledge;
public CP14KnowledgeInfoEvent(NetEntity netEntity, HashSet<ProtoId<CP14KnowledgePrototype>> allKnowledge)
{
NetEntity = netEntity;
AllKnowledge = allKnowledge;
}
}
[Serializable, NetSerializable]
public sealed class RequestKnowledgeInfoEvent : EntityEventArgs
{
public readonly NetEntity NetEntity;
public RequestKnowledgeInfoEvent(NetEntity netEntity)
{
NetEntity = netEntity;
}
}

View File

@@ -47,7 +47,8 @@ public sealed partial class CP14MagicManacostModifySystem : EntitySystem
msg.PushNewline();
var plus = (float)comp.GlobalModifier > 1 ? "+" : "";
msg.AddMarkupOrThrow($"{Loc.GetString("cp14-clothing-magic-global")}: {plus}{((float)comp.GlobalModifier - 1)*100}%");
msg.AddMarkupOrThrow(
$"{Loc.GetString("cp14-clothing-magic-global")}: {plus}{MathF.Round((float)(comp.GlobalModifier - 1) * 100, MidpointRounding.AwayFromZero)}%");
}
foreach (var modifier in comp.Modifiers)

View File

@@ -0,0 +1,30 @@
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
namespace Content.Shared._CP14.MagicSpell.Spells;
public sealed partial class CP14SpellSuckBlood : CP14SpellEffect
{
[DataField]
public FixedPoint2 SuckAmount = 25;
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
{
if (args.Target is null)
return;
if (args.User is null)
return;
var solutionContainerSystem = entManager.System<SharedSolutionContainerSystem>();
if (!solutionContainerSystem.TryGetSolution(args.Target.Value, "bloodstream", out var targetBloodstreamSolution))
return;
if (!solutionContainerSystem.TryGetSolution(args.User.Value, "chemicals", out var userSolution))
return;
solutionContainerSystem.TryTransferSolution(userSolution.Value,
targetBloodstreamSolution.Value.Comp.Solution,
SuckAmount);
}
}

View File

@@ -0,0 +1,43 @@
using Content.Shared.Examine;
using Content.Shared.Humanoid;
namespace Content.Shared._CP14.Vampire;
public abstract class CP14SharedVampireVisualsSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14VampireVisualsComponent, ExaminedEvent>(OnVampireExamine);
SubscribeLocalEvent<CP14VampireVisualsComponent, ComponentInit>(OnVampireVisualsInit);
SubscribeLocalEvent<CP14VampireVisualsComponent, ComponentShutdown>(OnVampireVisualsShutdown);
}
protected virtual void OnVampireVisualsShutdown(Entity<CP14VampireVisualsComponent> vampire, ref ComponentShutdown args)
{
if (!EntityManager.TryGetComponent(vampire, out HumanoidAppearanceComponent? humanoidAppearance))
return;
humanoidAppearance.EyeColor = vampire.Comp.OriginalEyesColor;
Dirty(vampire, humanoidAppearance);
}
protected virtual void OnVampireVisualsInit(Entity<CP14VampireVisualsComponent> vampire, ref ComponentInit args)
{
if (!EntityManager.TryGetComponent(vampire, out HumanoidAppearanceComponent? humanoidAppearance))
return;
vampire.Comp.OriginalEyesColor = humanoidAppearance.EyeColor;
humanoidAppearance.EyeColor = vampire.Comp.EyesColor;
Dirty(vampire, humanoidAppearance);
}
private void OnVampireExamine(Entity<CP14VampireVisualsComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("cp14-vampire-examine"));
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.Vampire;
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14VampireVisualsComponent : Component
{
[DataField]
public Color EyesColor = Color.Red;
[DataField]
public Color OriginalEyesColor = Color.White;
[DataField]
public string FangsMap = "vampire_fangs";
}

View File

@@ -26,7 +26,7 @@ public sealed partial class KnowledgeRequired : CP14WorkbenchCraftRequirement
public override void PostCraft(EntityManager entManager, HashSet<EntityUid> placedEntities, EntityUid user)
{
var knowledgeSystem = entManager.System<SharedCP14KnowledgeSystem>();
knowledgeSystem.UseKnowledge(user, Knowledge);
knowledgeSystem.TryUseKnowledge(user, Knowledge);
}
public override string GetRequirementTitle(IPrototypeManager protoManager)
@@ -41,8 +41,8 @@ public sealed partial class KnowledgeRequired : CP14WorkbenchCraftRequirement
return null;
}
public override SpriteSpecifier? GetRequirementTexture(IPrototypeManager protoManager)
public override SpriteSpecifier GetRequirementTexture(IPrototypeManager protoManager)
{
return new SpriteSpecifier.Texture(new("/Textures/Interface/students-cap.svg.192dpi.png"));
return new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/students-cap.svg.192dpi.png"));
}
}

View File

@@ -63,8 +63,12 @@
copyright: 'by SpaceLife on Freesound.org'
source: "https://freesound.org/people/SpaceLife/sounds/545938/"
- files: ["essence_consume.ogg"]
license: "CC0-1.0"
copyright: 'by DustyWind on Freesound.org'
source: "https://freesound.org/people/DustyWind/sounds/715784/"
- files: ["vampire_bite.ogg"]
license: "CC0-1.0"
copyright: 'by magnuswaker on Freesound.org'
source: "https://freesound.org/people/magnuswaker/sounds/563491/"

Binary file not shown.

View File

@@ -0,0 +1,2 @@
cp14-admin-verb-text-make-vampire = Make vampire
cp14-admin-verb-make-vampire = Add to target antagonist role “Vampire”

View File

@@ -0,0 +1,3 @@
cp14-roles-antag-vampire-name = Vampire
cp14-roles-antag-vampire-objective = You are a parasite on the body of society, hated by those around you, burned by the sun, and eternally hungry. You need to feed on the blood of the sentient to survive. And finding those who will volunteer to be your feeder is not easy...
cp14-roles-antag-vampire-briefing = You are a parasite on society. It hates and fears you, but the blood of the living is your only food. Nature destroys you with sunlight, so you have to hide in the shadows. It's like the whole world is trying to destroy you, but your will to live is stronger than all of that. SURVIVE. That's all you have to do.

View File

@@ -18,7 +18,9 @@ cp14-modifier-mole = predatory moles
cp14-modifier-rabbits = rabbits
cp14-modifier-boars = wild boars
cp14-modifier-invisible-whistler = invisible whistlers
cp14-modifier-sheeps = sheeps
cp14-modifier-chasm = bottomless chasms
cp14-modifier-air-lily = air lilies
cp14-modifier-time-limit-10 = temporary disintegration (10 minutes)
cp14-modifier-shadow-kudzu = spreading darkness
cp14-modifier-shadow-kudzu = spreading astral haze
cp14-modifier-night = darkness

View File

@@ -54,6 +54,7 @@ cp14-loadout-merchant-shoes = Merchant's shoes
# Guildmaster
cp14-loadout-guildmaster-outer = Guildmaster outer clothes
cp14-loadout-guildmaster-head = Guildmaster hat
cp14-loadout-guildmaster-cloak = Guildmaster cloak
cp14-loadout-guildmaster-shirt = Guildmaster shirt
cp14-loadout-guildmaster-pants = Guildmaster pants

View File

@@ -1,9 +1,3 @@
cp14-lock-shape-bank-entrance = bank hall
cp14-lock-shape-bank-staff = bank offices
cp14-lock-shape-bank-commandant = commandant's house
cp14-lock-shape-bank-safe = bank safes
cp14-lock-shape-bank-vault = bank vault
cp14-lock-shape-tavern-hall = tavern hall
cp14-lock-shape-tavern-staff = tavern staff quarters
cp14-lock-shape-tavern-dorm1 = tavern room №1
@@ -20,6 +14,10 @@ cp14-lock-shape-blacksmith1 = forge №1
cp14-lock-shape-blacksmith2 = forge №2
cp14-lock-shape-blacksmith3 = forge №3
cp14-lock-shape-merchant1 = shop №1
cp14-lock-shape-merchant2 = shop №2
cp14-lock-shape-merchant3 = shop №3
cp14-lock-shape-personalhouse1 = house №1
cp14-lock-shape-personalhouse2 = house №2
cp14-lock-shape-personalhouse3 = house №3

View File

@@ -1,4 +1,7 @@
cp14-objective-issuer-personal = [color="#95a6c2"]Personal objectives[/color]
cp14-objective-personal-currency-collect-title = Earn{$coins}
cp14-objective-personal-currency-collect-desc = I plan to earn at least{$coins} by working here.
cp14-objective-personal-currency-collect-desc = I plan to earn at least{$coins} by working here.
cp14-objective-personal-richest-merchant-title = Become the richest of merchants
cp14-objective-personal-richest-merchant-desc = I must beat all other merchants by making more money than all of them!

View File

@@ -1,3 +1,9 @@
cp14-reagent-name-blood-animal = Animal blood
cp14-reagent-desc-blood-animal = The life energy of a living unintelligent being.
cp14-reagent-name-blood-vampire = Vampire blood
cp14-reagent-desc-blood-vampire = The life energy of a powerful blood-vampire.
cp14-reagent-name-blood = Blood
cp14-reagent-desc-blood = The life energy of a living warm-blooded creatures.

View File

@@ -0,0 +1,11 @@
cp14-faction-name-helmir = Helmir's descendants
cp14-faction-desc-helmir = TODO
cp14-faction-name-sylphoria = The winds of Sylphoria
cp14-faction-desc-sylphoria = TODO
cp14-faction-name-spice-stream = Spice Stream
cp14-faction-desc-spice-stream = TODO
cp14-faction-name-brad-family = Brad's imperial family
cp14-faction-desc-brad-family = TODO

View File

@@ -0,0 +1,11 @@
cp14-store-ui-title = Trading outpost "{$name}"
cp14-store-ui-order = More info
cp14-store-ui-next-travel-out = Before departure:
cp14-store-ui-next-travel-in = Before arrival:
cp14-store-ui-tab-buy = Buying
cp14-store-ui-tab-sell = Selling
cp14-store-ui-tab-special = [bold][color=#eba346]One-off offer![/color][/bold]
cp14-store-sell-hint = To sell {$name}, put the item you want in the trade cabinet, and close it. Our people will figure out what's what, pick up the item, and send you the money through the same sales cabinet.

View File

@@ -11,7 +11,7 @@ cp14-tips-10 = Tall bushes are good for hiding your character! But they slow you
cp14-tips-11 = Don't forget to lock your doors if you don't want anyone to get in!
cp14-tips-12 = You can examine the demiplane key to see what you can find in it. The information may be incomplete, but you can still navigate by it, and choose where you want to go.
cp14-tips-13 = As a farmer, don't forget to water your vegetable garden! Plants die without watering.
cp14-tips-14 = To pierce the dish, try combining different ingredients on a plate.
cp14-tips-14 = Demiplanes can be very dangerous, don't neglect medical consumables and alchemist potions.
cp14-tips-15 = When you use the demiplane key, an unstable rift opens up that will draw in up to 4 nearby players after a while.
cp14-tips-16 = When moving between or from the demiplane, you can additionally grab a large item (or the corpse of a dead friend) by pulling it with you during the teleportation time.
cp14-tips-17 = If you wish to leave the round, you may board a traveling ship. When it travels to the empire, you will leave the round and free up your role for another player.

View File

@@ -1,37 +0,0 @@
cp14-store-sell-hint = To sell {$name}, load the desired goods onto the selling pallets. Our people in town will sort it out, pick up the goods and leave the money in the trade box on the ship.
cp14-store-sell-wood-name = 30 wooden planks
cp14-store-sell-wood-desc = Do you really think anyone needs planks from a faraway island? Well, you're right, we hope your settlement has something to keep you warm in the winter.
cp14-store-sell-glass-name = 10 glass
cp14-store-sell-glass-desc = A fine material prized in the Empire for its versatility. The windows of palaces, the lenses of craftsmen, and even the mirrors of mages all require glass. Your labors are sure to find a use for it!
cp14-store-sell-alchemical-herbals-name = 10 alchemical herbals
cp14-store-sell-alchemical-herbals-desc = TODO
# Metalls
cp14-store-sell-copperbar-name = 10 copper bars
cp14-store-sell-copperbar-desc = Although copper is used mainly as a coin material, it is also often enjoyed by blacksmiths in various alloys.
cp14-store-sell-ironbar-name = 10 iron bars
cp14-store-sell-ironbar-desc = Iron is an indispensable material in the manufacture of... almost anything that has any longevity in this world. And surely the Empire could use an extra shipment.
cp14-store-sell-goldbar-name = 10 gold bars
cp14-store-sell-goldbar-desc = The mining and processing of gold ore is heavily sponsored by the empire, which uses gold as currency and material for jewelry.
cp14-store-sell-mithrilbar-name = 10 mithril bars
cp14-store-sell-mithrilbar-desc = TODO
cp14-store-sell-copperore-name = 10 copper ore
cp14-store-sell-copperore-desc = TODO
cp14-store-sell-ironore-name = 10 iron ore
cp14-store-sell-ironore-desc = TODO
cp14-store-sell-goldore-name = 10 gold ore
cp14-store-sell-goldore-desc = TODO
cp14-store-sell-mithrilore-name = 10 mithril ore
cp14-store-sell-mithrilore-desc = TODO

View File

@@ -1,20 +0,0 @@
cp14-store-sell-special-wheat-name = 10 sheaves of wheat
cp14-store-sell-special-wheat-desc = Urgent wheat order! Our lizards have gone berserk and devoured the entire supply! Any supplies of wheat would be appreciated!
cp14-store-sell-special-dye-name = 10 dyes
cp14-store-sell-special-dye-desc = An aristocrat from the capital has suddenly shown an interest in painting, and requires dyes for his experiments. The Empire is buying up all the dyes you can provide her with.
cp14-store-sell-special-meat-name = 10 pieces of meat
cp14-store-sell-special-meat-desc = Any kind of meat will do. Lambs, goats, cows, even giant worms! In fact, send anything you want, Isil Island has no food for carnivores at the moment.
cp14-store-sell-special-torch-name = 20 torches
cp14-store-sell-special-torch-desc = The settlement of Grimstroke is requesting a large shipment of torches from the Empire! We're making a big march on the Great Tomb of Lazaric! And we have nothing to light it with.
cp14-store-sell-special-ash-name = 10 ashes
cp14-store-sell-special-ash-desc = The Mermaid Council is requesting a shipment of ash to their oceanic domain for the reason, quote Where do you think we should get ash from underwater?
cp14-store-sell-special-lucen-name = 10 lucen planks
cp14-store-sell-special-lucen-desc = A shipment of magical wood is needed to build an enchanted country house for an aristocrat. The whims of the rich are mysterious!
cp14-store-sell-special-spell-scroll-name = 5 spell scrolls
cp14-store-sell-special-spell-scroll-desc = We don't really care what spells are in the scrolls, they are only for reporting the magical prosperity of the settlement to the local Commandant.

View File

@@ -1,9 +0,0 @@
cp14-store-ui-title = Retail information board
cp14-store-ui-order = More info
cp14-store-ui-next-travel-out = Before departure:
cp14-store-ui-next-travel-in = Before arrival:
cp14-store-ui-tab-buy = Buying
cp14-store-ui-tab-sell = Selling
cp14-store-ui-tab-special = [bold][color=#eba346]Temporary offer![/color][/bold]

View File

@@ -0,0 +1,3 @@
cp14-heat-under-sun = The sunlight stings unbearably...
cp14-vampire-examine = [color=red]Bright red eyes and long fangs tell you that you are facing a very dangerous vampire. Your instincts are telling you to run or fight![/color]

View File

@@ -601,6 +601,9 @@ ent-CP14ClothingHeadAlchemistBeret = берет алхимика
ent-CP14ClothingHeadCaptainCap = капитанская кепка
.desc = Нет, ну вы посмотрите какой красавчик!
ent-CP14ClothingHeadGildmaster = шляпа гильдмастера
.desc = Знак безграничного опыта и чести.
ent-CP14ClothingHeadBeretBase = { ent-CP14ClothingHeadBase }
.desc = Это берет.

View File

@@ -0,0 +1,2 @@
cp14-admin-verb-text-make-vampire = Сделать вампиром
cp14-admin-verb-make-vampire = Добавить цели роль "Вампир"

View File

@@ -0,0 +1,3 @@
cp14-roles-antag-vampire-name = Вампир
cp14-roles-antag-vampire-objective = Вы - паразит на теле общества, ненавидимый окружающими, сгораемый под солнцем и вечно голодный. Вам необходимо питаться кровью разумных, чтобы выжить. И найти тех, кто добровольно будет готов стать вашей кормушкой непросто...
cp14-roles-antag-vampire-briefing = Вы - паразит на теле общества. Оно вас ненавидит и боится, но кровь живых - ваша единственная пища. Природа уничтожает вас солнечным светом, и вам приходится скрываться в тени. Словно весь мир пытается вас уничтожить, но ваше желание жить сильнее всего этого. ВЫЖИВИТЕ. Это все что от вас требуется.

View File

@@ -17,8 +17,10 @@ cp14-modifier-dyno = доисторической фауны
cp14-modifier-mole = хищных кротов
cp14-modifier-rabbits = кроликов
cp14-modifier-boars = диких кабанов
cp14-modifier-sheeps = овец
cp14-modifier-invisible-whistler = невидимых свистунов
cp14-modifier-chasm = бездонных пропастей
cp14-modifier-air-lily = воздушных лилий
cp14-modifier-time-limit-10 = временного распада (10 минут)
cp14-modifier-shadow-kudzu = распространяющейся тьмы
cp14-modifier-shadow-kudzu = распространяющгося астрального мрака
cp14-modifier-night = темноты

View File

@@ -54,6 +54,7 @@ cp14-loadout-merchant-shoes = Ботинки торговца
# Guildmaster
cp14-loadout-guildmaster-outer = Верхняя одежда гильдмастера
cp14-loadout-guildmaster-head = Шляпа гильдмастера
cp14-loadout-guildmaster-cloak = Накидка гильдмастера
cp14-loadout-guildmaster-shirt = Рубашка гильдмастера
cp14-loadout-guildmaster-pants = Штаны гильдмастера

View File

@@ -1,9 +1,3 @@
cp14-lock-shape-bank-entrance = холл банка
cp14-lock-shape-bank-staff = служебные помещения банка
cp14-lock-shape-bank-commandant = дом комменданта
cp14-lock-shape-bank-safe = сейфы банка
cp14-lock-shape-bank-vault = хранилище банка
cp14-lock-shape-tavern-hall = зал таверны
cp14-lock-shape-tavern-staff = служебные помещения таверны
cp14-lock-shape-tavern-dorm1 = комната таверны №1
@@ -20,6 +14,10 @@ cp14-lock-shape-blacksmith1 = кузня №1
cp14-lock-shape-blacksmith2 = кузня №2
cp14-lock-shape-blacksmith3 = кузня №3
cp14-lock-shape-merchant1 = магазин №1
cp14-lock-shape-merchant2 = магазин №2
cp14-lock-shape-merchant3 = магазин №3
cp14-lock-shape-personalhouse1 = дом №1
cp14-lock-shape-personalhouse2 = дом №2
cp14-lock-shape-personalhouse3 = дом №3

View File

@@ -1,4 +1,7 @@
cp14-objective-issuer-personal = [color="#95a6c2"]Личные цели[/color]
cp14-objective-personal-currency-collect-title = Заработать{$coins}
cp14-objective-personal-currency-collect-desc = Я планирую заработать как минимум{$coins}, работая здесь.
cp14-objective-personal-currency-collect-desc = Я планирую заработать как минимум{$coins}, работая здесь.
cp14-objective-personal-richest-merchant-title = Стать самым богатым из торговцев
cp14-objective-personal-richest-merchant-desc = Я должен обставить всех других торговцев, заработав денег больше чем все они!

View File

@@ -1,3 +1,9 @@
cp14-reagent-name-blood-animal = Кровь животного
cp14-reagent-desc-blood-animal = Жизненная энергия живого неразумного существа.
cp14-reagent-name-blood-vampire = Кровь вампира
cp14-reagent-desc-blood-vampire = Жизненная энергия могущественного кровопийпы.
cp14-reagent-name-blood = Кровь
cp14-reagent-desc-blood = Жизненная энергия живого теплокровного существа.

View File

@@ -0,0 +1,11 @@
cp14-faction-name-helmir = Хельмировы потомки
cp14-faction-desc-helmir = TODO
cp14-faction-name-sylphoria = Ветра Сильфории
cp14-faction-desc-sylphoria = TODO
cp14-faction-name-spice-stream = Поток пряностей
cp14-faction-desc-spice-stream = TODO
cp14-faction-name-brad-family = Имперская семья Брада
cp14-faction-desc-brad-family = TODO

View File

@@ -0,0 +1,11 @@
cp14-store-ui-title = Торговый аванпост "{$name}"
cp14-store-ui-order = Дополнительная информация
cp14-store-ui-next-travel-out = До отправления:
cp14-store-ui-next-travel-in = До прибытия:
cp14-store-ui-tab-buy = Покупка
cp14-store-ui-tab-sell = Продажа
cp14-store-ui-tab-special = [bold][color=#eba346]Разовое предложение![/color][/bold]
cp14-store-sell-hint = Чтобы продать {$name}, засуньте необходимый товар в торговый шкаф, и закройте его. Наши люди разберутся что к чему, заберут товар и отправят вам деньги через этот же торговый шкаф.

View File

@@ -11,7 +11,7 @@ cp14-tips-10 = Высокие кусты неплохо прячут вашег
cp14-tips-11 = Не забывайте закрывать двери на ключ, если не хотите чтобы туда заходил кто попало!
cp14-tips-12 = Вы можете осмотреть ключ демиплана, чтобы узнать, что вы можете в нем найти. Информация может быть неполной, но по ней вы все равно можете ориентироваться, и выбирать куда вы хотите отправиться.
cp14-tips-13 = Будучи фермером, не забывайте поливать свой огород! Растения умирают без полива.
cp14-tips-14 = Чтобы приготовить блюдо, попробуйте скомбинировать на тарелке разные ингредиенты.
cp14-tips-14 = Демипланы могут быть очень опасны, не пренебрегайте медицинскими расходными материалами и алхимическими зельями.
cp14-tips-15 = Когда вы используете ключ демиплана, открывается нестабильный разлом, который через некоторое время затянет в себя до 4 ближайших игроков.
cp14-tips-16 = Перемещаясь между демипланом или из него, вы можете дополнительно захватить с собой большой предмет (или труп погибшего союзника), держа его во время момента телепортации.
cp14-tips-17 = Если вы хотите покинуть раунд, вы можете сесть на странствующий корабль. Когда он отправится в империю, вы покинете раунд и освободите свою роль для другого игрока.

View File

@@ -1,20 +0,0 @@
cp14-store-sell-special-wheat-name = 10 снопов пшеницы
cp14-store-sell-special-wheat-desc = Срочный заказ пшеницы! Наши ящеры взбесились и сожрали вообще все запасы! Мы будем благодарны любым запасам пшена!
cp14-store-sell-special-dye-name = 10 красителей
cp14-store-sell-special-dye-desc = Аристократ из столицы внезапно проявил интерес с рисованию, и требует красителей для своих экспериментов. Империя скупаем все красители, которые вы сможете ей предоставить.
cp14-store-sell-special-meat-name = 10 кусков мяса
cp14-store-sell-special-meat-desc = Нам подойдет любое мясо. Бараны, козы, коровы, да хоть гигантские черви! В общем, присылайте любое, острову Изиль сейчас нечем кормить хищный скот.
cp14-store-sell-special-torch-name = 20 факелов
cp14-store-sell-special-torch-desc = Поселение Гримстроук запрашивает большую партию факелов от Империи! Мы устраиваем большой поход на Великую Гробницу Лазарика! И нам нечем освещать ее.
cp14-store-sell-special-ash-name = 10 пепла
cp14-store-sell-special-ash-desc = Совет русалок запрашивает партию пепла в свои океанические владения по причине, цитата 'Откуда мы должны под водой получать пепел по вашему?'
cp14-store-sell-special-lucen-name = 10 люценовых досок
cp14-store-sell-special-lucen-desc = Необходима партия магической древесины для строительства зачарованной загородной дачи одного из аристократов. Причуды богатых неисповедимы!
cp14-store-sell-special-spell-scroll-name = 5 свитков заклинаний
cp14-store-sell-special-spell-scroll-desc = Нам на самом деле не важно какие именно заклинания находятся в свитках, они нам только для отчетности магического процветания поселения перед местным Комендантом.

View File

@@ -1,37 +0,0 @@
cp14-store-sell-hint = Чтобы продать {$name}, погрузите необходимый товар на продающие поддоны. Наши люди в городе разберутся что к чему, заберут товар и оставят деньги в торговом ящике на корабле.
cp14-store-sell-wood-name = 30 деревянных досок
cp14-store-sell-wood-desc = Вы правда думаете что хоть кому то нужны доски с далекого острова? Что ж вы правы, надеемся у вашего поселения есть чем греться зимой.
cp14-store-sell-glass-name = 10 стекла
cp14-store-sell-glass-desc = Изящный материал, который ценится в Империи за свою универсальность. Окна дворцов, линзы мастеров и даже зеркала магов — все это требует стекла. Ваши труды точно найдут применение!
cp14-store-sell-alchemical-herbals-name = 10 алхимических растений
cp14-store-sell-alchemical-herbals-desc = TODO
# Metalls
cp14-store-sell-copperbar-name = 10 медных слитков
cp14-store-sell-copperbar-desc = Хоть медь и используется в основном как материал для монет но и в разных сплавах он часто нравится кузнецам.
cp14-store-sell-ironbar-name = 10 железных слитков
cp14-store-sell-ironbar-desc = Железо - незаменимый материал для производства... почти всего, что имеет хоть какую либо долговечность в этом мире. И конечно же, Империя не откажется от дополнительной партии.
cp14-store-sell-goldbar-name = 10 золотых слитков
cp14-store-sell-goldbar-desc = Добыча и обработка золотой руды активно спонсируется империей, использующей золото как валюту и материал для ювелирных украшений.
cp14-store-sell-mithrilbar-name = 10 мифриловых слитков
cp14-store-sell-mithrilbar-desc = TODO
cp14-store-sell-copperore-name = 10 медной руды
cp14-store-sell-copperore-desc = TODO
cp14-store-sell-ironore-name = 10 железной руды
cp14-store-sell-ironore-desc = TODO
cp14-store-sell-goldore-name = 10 золотой руды
cp14-store-sell-goldore-desc = TODO
cp14-store-sell-mithrilore-name = 10 мифриловой руды
cp14-store-sell-mithrilore-desc = TODO

View File

@@ -1,9 +0,0 @@
cp14-store-ui-title = Информационное торговое табло
cp14-store-ui-order = Дополнительная информация
cp14-store-ui-next-travel-out = До отправления:
cp14-store-ui-next-travel-in = До прибытия:
cp14-store-ui-tab-buy = Покупка
cp14-store-ui-tab-sell = Продажа
cp14-store-ui-tab-special = [bold][color=#eba346]Временное предложение![/color][/bold]

View File

@@ -0,0 +1,3 @@
cp14-heat-under-sun = Солнечый свет нестерпимо жжется...
cp14-vampire-examine = [color=red]Ярко красные глаза и длинные клыки говорят вам что перед вами опаснейший вампир. Ваши инстинкты кричат вам бежать или сражаться![/color]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -78,6 +78,7 @@ entities:
parent: 1
- type: BecomesStation
id: Dev
- type: Roof
- type: MapGrid
chunks:
0,0:
@@ -2489,7 +2490,7 @@ entities:
- type: Transform
pos: 5.5,-9.5
parent: 2
- proto: CP14TravelingShop
- proto: CP14TradingBoardBase
entities:
- uid: 418
components:
@@ -2503,15 +2504,6 @@ entities:
rot: -1.5707963267948966 rad
pos: 0.5,11.5
parent: 2
- proto: CP14TravelingStoreshipAnchor
entities:
- uid: 467
components:
- type: Transform
anchored: False
rot: 1.5707963267948966 rad
pos: 1.5,14.5
parent: 1
- proto: CP14VialMedium
entities:
- uid: 45
@@ -3103,7 +3095,7 @@ entities:
- type: Transform
pos: 3.5,-0.5
parent: 2
- proto: CP14WoodenDoorTavernAlchemy1
- proto: CP14WoodenDoorAlchemy1
entities:
- uid: 287
components:

Some files were not shown because too many files have changed in this diff Show More