Cargo system (#487)

* simple storeship arriving

* pupu

* ship cycling

* buy positions prototypes

* i hate UI

* PriceControl

* second tab ui

* baloon! pallets!

* update shop in town

* setup billboard timer

* split to sell and buy categories

* renaming gaming

* actually selling

* fix infinity selling

* improve timer

* move description too rigt part UI

* bar selling

* iron cabinet

* purge currency categories

* remove town balance, add money box

* special proposal, FTLImmune anchor

* fix UI

* remove tests buying

* Update CP14StoreWindow.xaml.cs

* currency converter

* currency clean up

* Update CP14CargoSystem.cs

* clean up part 2

* rider petpet

* coins audio

* coin improvment

* Update coins.yml

* translate

* more coins roundstart

* Update wallet.yml

* Update wallet.yml

* generate coin problem fix

* refactor proto reading

* fixes

* huh

* shuttle logshit fix, add to tavern map

* Update CP14StationTravelingStoreShipTargetComponent.cs
This commit is contained in:
Ed
2024-10-15 15:22:06 +03:00
committed by GitHub
parent 4425b77c90
commit d259191d3d
93 changed files with 2793 additions and 2241 deletions

View File

@@ -0,0 +1,22 @@
<Control xmlns="https://spacestation14.io">
<GridContainer Columns="6">
<TextureRect Name="GoldView"
MinSize="10 10"
Stretch="KeepAspectCentered"/>
<Label Name="GoldText"
Margin="0,0,5,0"/>
<TextureRect Name="SilverView"
MinSize="10 10"
Stretch="KeepAspectCentered"/>
<Label Name="SilverText"
Margin="0,0,5,0"/>
<TextureRect Name="CopperView"
MinSize="10 10"
Stretch="KeepAspectCentered"/>
<Label Name="CopperText"
Margin="0,0,5,0"/>
</GridContainer>
</Control>

View File

@@ -0,0 +1,46 @@
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client._CP14.TravelingStoreShip;
[GenerateTypedNameReferences]
public sealed partial class CP14PriceControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly SpriteSystem _sprite;
public CP14PriceControl(int price)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
var rsiPath = new ResPath("_CP14/Interface/Misc/coins.rsi");
var total = price;
var gp = total / 100;
total %= 100;
var sp = total / 10;
total %= 10;
var cp = total;
CopperView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "c"));
CopperText.Text = cp.ToString();
SilverView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "s"));
SilverText.Text = sp.ToString();
GoldView.Texture = _sprite.Frame0(new SpriteSpecifier.Rsi(rsiPath, "g"));
GoldText.Text = gp.ToString();
}
}

View File

@@ -0,0 +1,34 @@
using Content.Shared._CP14.TravelingStoreShip;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client._CP14.TravelingStoreShip;
public sealed class CP14StoreBoundUserInterface : BoundUserInterface
{
private CP14StoreWindow? _window;
public CP14StoreBoundUserInterface(EntityUid owner, [NotNull] Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = this.CreateWindow<CP14StoreWindow>();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
switch (state)
{
case CP14StoreUiState storeState:
_window?.UpdateUI(storeState);
break;
}
}
}

View File

@@ -0,0 +1,15 @@
<Control xmlns="https://spacestation14.io">
<Button Name="ProductButton" Access="Public">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<TextureRect Name="View"
MinSize="48 48"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Stretch="KeepAspectCentered"/>
<RichTextLabel Name="ProductName" VerticalAlignment="Center" Access="Public"/>
<BoxContainer Name="PriceHolder" VerticalAlignment="Center" HorizontalExpand="True" HorizontalAlignment="Right"/>
</BoxContainer>
</BoxContainer>
</Button>
</Control>

View File

@@ -0,0 +1,44 @@
using Content.Shared._CP14.TravelingStoreShip;
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;
[GenerateTypedNameReferences]
public sealed partial class CP14StoreProductControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
private readonly SpriteSystem _sprite;
public CP14StoreProductControl(CP14StoreUiProductEntry entry)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
UpdateName(entry.Name);
UpdateView(entry.Icon);
UpdatePrice(entry.Price);
}
private void UpdatePrice(int price)
{
PriceHolder.RemoveAllChildren();
PriceHolder.AddChild(new CP14PriceControl(price));
}
private void UpdateName(string name)
{
ProductName.Text = $"[bold]{name}[/bold]";
}
private void UpdateView(SpriteSpecifier spriteSpecifier)
{
View.Texture = _sprite.Frame0(spriteSpecifier);
}
}

View File

@@ -0,0 +1,31 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'cp14-store-ui-title'}"
MinSize="800 600"
SetSize="800 600">
<BoxContainer Orientation="Horizontal">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
<!-- Product list (left side UI) -->
<TabContainer SizeFlagsStretchRatio="0.5" Name="Tabs" HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
<BoxContainer Name="BuyProductsContainer" Orientation="Vertical" HorizontalExpand="True"/>
</ScrollContainer>
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
<BoxContainer Name="SellProductsContainer" Orientation="Vertical" HorizontalExpand="True"/>
</ScrollContainer>
</TabContainer>
<!-- Station trading data (right side UI) -->
<BoxContainer SizeFlagsStretchRatio="0.5" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Margin="0 0 10 0">
<controls:StripeBack>
<PanelContainer>
<Label Text="{Loc 'cp14-store-ui-order'}" Align="Center" Margin="0 5 0 3"/>
</PanelContainer>
</controls:StripeBack>
<Label Name="TravelTimeLabel" Text="00:00" HorizontalAlignment="Center" HorizontalExpand="True" Margin="0 15 0 0"/>
<controls:HLine Color="#404040" Thickness="2" Margin="0 5"/>
<RichTextLabel Name="SelectedName" HorizontalExpand="True" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="5"/>
<RichTextLabel Name="SelectedDesc" HorizontalExpand="True" VerticalExpand="True" VerticalAlignment="Top" Margin="5"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,79 @@
using Content.Shared._CP14.TravelingStoreShip;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client._CP14.TravelingStoreShip;
[GenerateTypedNameReferences]
public sealed partial class CP14StoreWindow : DefaultWindow
{
[Dependency] private readonly IGameTiming _timing = default!;
private TimeSpan? _nextTravelTime;
private bool _onStation;
public CP14StoreWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
Tabs.SetTabTitle(0, Loc.GetString("cp14-store-ui-tab-buy"));
Tabs.SetTabTitle(1, Loc.GetString("cp14-store-ui-tab-sell"));
}
public void UpdateUI(CP14StoreUiState state)
{
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)
{
BuyProductsContainer.RemoveAllChildren();
SellProductsContainer.RemoveAllChildren();
foreach (var product in state.ProductsBuy)
{
var control = new CP14StoreProductControl(product);
control.ProductButton.OnPressed += _ =>
{
SelectProduct(product);
};
BuyProductsContainer.AddChild(control);
}
foreach (var product in state.ProductsSell)
{
var control = new CP14StoreProductControl(product);
control.ProductButton.OnPressed += _ =>
{
SelectProduct(product);
};
SellProductsContainer.AddChild(control);
}
}
private void SelectProduct(CP14StoreUiProductEntry? entry)
{
SelectedName.Text = entry is null ? string.Empty : $"[bold]{entry.Value.Name}[/bold]";
SelectedDesc.Text = entry is null ? string.Empty : entry.Value.Desc;
}
}

View File

@@ -0,0 +1,216 @@
using Content.Server.Popups;
using Content.Server.Stack;
using Content.Shared._CP14.Currency;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Stacks;
using Content.Shared.Verbs;
using Content.Shared.Whitelist;
using Robust.Server.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server._CP14.Currency;
public sealed partial class CP14CurrencySystem : CP14SharedCurrencySystem
{
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14CurrencyComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<CP14CurrencyConverterComponent, ExaminedEvent>(OnConverterExamine);
SubscribeLocalEvent<CP14CurrencyConverterComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<CP14CurrencyConverterComponent, GetVerbsEvent<Verb>>(OnGetVerb);
}
private void OnExamine(Entity<CP14CurrencyComponent> currency, ref ExaminedEvent args)
{
var total = GetTotalCurrency(currency, currency.Comp);
var push = Loc.GetString("cp14-currency-examine-title");
push += GetCurrencyPrettyString(total);
args.PushMarkup(push);
}
private void OnConverterExamine(Entity<CP14CurrencyConverterComponent> ent, ref ExaminedEvent args)
{
var push = $"{Loc.GetString("cp14-currency-converter-examine-title")} {GetCurrencyPrettyString(ent.Comp.Balance)}";
args.PushMarkup(push);
}
private void OnInteractUsing(Entity<CP14CurrencyConverterComponent> ent, ref InteractUsingEvent args)
{
if (!TryComp<CP14CurrencyComponent>(args.Used, out var currency))
return;
if (ent.Comp.Whitelist is not null && !_whitelist.IsValid(ent.Comp.Whitelist, args.Used))
return;
var delta = GetTotalCurrency(args.Used);
ent.Comp.Balance += delta;
QueueDel(args.Used);
_popup.PopupEntity(Loc.GetString("cp14-currency-converter-insert", ("cash", delta)), ent, args.User);
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3));
}
private void OnGetVerb(Entity<CP14CurrencyConverterComponent> ent, ref GetVerbsEvent<Verb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
var transform = Transform(ent);
var coord = transform.Coordinates.Offset(transform.LocalRotation.RotateVec(ent.Comp.SpawnOffset));
Verb copperVerb = new()
{
Text = Loc.GetString("cp14-currency-converter-get-cp"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/cp_coin.rsi/coin10.png")),
Category = VerbCategory.CP14CurrencyConvert,
Priority = 1,
CloseMenu = false,
Act = () =>
{
if (ent.Comp.Balance < CP.Value)
return;
ent.Comp.Balance -= CP.Value;
var newEnt = Spawn(CP.Key, coord);
_stack.TryMergeToContacts(newEnt);
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(0.9f));
},
};
args.Verbs.Add(copperVerb);
Verb silverVerb = new()
{
Text = Loc.GetString("cp14-currency-converter-get-sp"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/sp_coin.rsi/coin10.png")),
Category = VerbCategory.CP14CurrencyConvert,
Priority = 2,
CloseMenu = false,
Act = () =>
{
if (ent.Comp.Balance < SP.Value)
return;
ent.Comp.Balance -= SP.Value;
var newEnt = Spawn(SP.Key, coord);
_stack.TryMergeToContacts(newEnt);
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.1f));
},
};
args.Verbs.Add(silverVerb);
Verb goldVerb = new()
{
Text = Loc.GetString("cp14-currency-converter-get-gp"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/gp_coin.rsi/coin10.png")),
Category = VerbCategory.CP14CurrencyConvert,
Priority = 3,
CloseMenu = false,
Act = () =>
{
if (ent.Comp.Balance < GP.Value)
return;
ent.Comp.Balance -= GP.Value;
var newEnt = Spawn(GP.Key, coord);
_stack.TryMergeToContacts(newEnt);
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.3f));
},
};
args.Verbs.Add(goldVerb);
Verb platinumVerb = new()
{
Text = Loc.GetString("cp14-currency-converter-get-pp"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/_CP14/Objects/Economy/pp_coin.rsi/coin10.png")),
Category = VerbCategory.CP14CurrencyConvert,
Priority = 4,
CloseMenu = false,
Act = () =>
{
if (ent.Comp.Balance < PP.Value)
return;
ent.Comp.Balance -= PP.Value;
var newEnt = Spawn(PP.Key, coord);
_stack.TryMergeToContacts(newEnt);
_audio.PlayPvs(ent.Comp.InsertSound, ent, AudioParams.Default.WithMaxDistance(3).WithPitchScale(1.5f));
},
};
args.Verbs.Add(platinumVerb);
}
public HashSet<EntityUid> GenerateMoney(EntProtoId currencyType, int target, EntityCoordinates coordinates)
{
return GenerateMoney(currencyType, target, coordinates, out _);
}
public HashSet<EntityUid> GenerateMoney(EntProtoId currencyType, int target, EntityCoordinates coordinates, out int remainder)
{
remainder = target;
HashSet<EntityUid> spawns = new();
if (!_proto.TryIndex(currencyType, out var indexedCurrency))
return spawns;
var ent = Spawn(currencyType, coordinates);
if (ProcessEntity(ent, ref remainder, spawns))
return spawns;
while (remainder > 0)
{
var newEnt = Spawn(currencyType, coordinates);
if (ProcessEntity(newEnt, ref remainder, spawns))
break;
}
return spawns;
}
private bool ProcessEntity(EntityUid ent, ref int remainder, HashSet<EntityUid> spawns)
{
var singleCurrency = GetTotalCurrency(ent);
if (singleCurrency > remainder)
{
QueueDel(ent);
return true;
}
spawns.Add(ent);
remainder -= singleCurrency;
if (TryComp<StackComponent>(ent, out var stack))
{
AdjustStack(ent, stack, singleCurrency, ref remainder);
}
return false;
}
private void AdjustStack(EntityUid ent, StackComponent stack, float singleCurrency, ref int remainder)
{
var singleStackCurrency = singleCurrency / stack.Count;
var stackLeftSpace = stack.MaxCountOverride - stack.Count;
if (stackLeftSpace is not null)
{
var addedStack = MathF.Min((float)stackLeftSpace, MathF.Floor(remainder / singleStackCurrency));
if (addedStack > 0)
{
_stack.SetCount(ent, stack.Count + (int)addedStack);
remainder -= (int)(addedStack * singleStackCurrency);
}
}
}
}

View File

@@ -9,12 +9,6 @@ public sealed partial class CP14CurrencyCollectConditionComponent : Component
[DataField]
public int Currency = 1000;
/// <summary>
/// Limits the goal to collecting values from a specific category.
/// </summary>
[DataField]
public string? Category;
[DataField(required: true)]
public LocId ObjectiveText;

View File

@@ -13,7 +13,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
{
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly CP14CurrencySystem _currency = default!;
[Dependency] private readonly CP14SharedCurrencySystem _currency = default!;
private EntityQuery<ContainerManagerComponent> _containerQuery;
@@ -35,7 +35,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
private void OnAfterAssign(Entity<CP14CurrencyCollectConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
_metaData.SetEntityName(condition.Owner, Loc.GetString(condition.Comp.ObjectiveText), args.Meta);
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription, ("coins", _currency.GetPrettyCurrency(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);
}
@@ -71,7 +71,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
foreach (var entity in container.ContainedEntities)
{
// check if this is the item
count += CheckCurrency(entity, condition);
count += _currency.GetTotalCurrency(entity);
// if it is a container check its contents
if (_containerQuery.TryGetComponent(entity, out var containerManager))
@@ -88,7 +88,7 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
private void CheckEntity(EntityUid entity, CP14CurrencyCollectConditionComponent condition, ref Stack<ContainerManagerComponent> containerStack, ref int counter)
{
// check if this is the item
counter += CheckCurrency(entity, condition);
counter += _currency.GetTotalCurrency(entity);
//we don't check the inventories of sentient entity
if (!TryComp<MindContainerComponent>(entity, out _))
@@ -98,16 +98,4 @@ public sealed class CP14CurrencyCollectConditionSystem : EntitySystem
containerStack.Push(containerManager);
}
}
private int CheckCurrency(EntityUid entity, CP14CurrencyCollectConditionComponent condition)
{
// check if this is the target
if (!TryComp<CP14CurrencyComponent>(entity, out var target))
return 0;
if (target.Category != condition.Category)
return 0;
return _currency.GetTotalCurrency(entity);
}
}

View File

@@ -0,0 +1,87 @@
using System.Numerics;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Shared._CP14.TravelingStoreShip;
using Content.Shared._CP14.TravelingStoreShip.Prototype;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server._CP14.TravelingStoreShip;
public sealed partial class CP14CargoSystem
{
private EntityQuery<ArrivalsBlacklistComponent> _blacklistQuery;
private void InitializeShuttle()
{
_blacklistQuery = GetEntityQuery<ArrivalsBlacklistComponent>();
SubscribeLocalEvent<CP14TravelingStoreShipComponent, FTLCompletedEvent>(OnFTLCompleted);
}
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 (Transform(ship.Shuttle).MapUid == Transform(ship.TradePostMap).MapUid)
{
// if landed on trade post
ship.NextTravelTime = _timing.CurTime + ship.StationWaitTime;
SendShuttleToStation((uid, ship));
}
else
{
// if landed on station
ship.NextTravelTime = _timing.CurTime + ship.TradePostWaitTime;
SendShuttleToTradepost((uid, ship));
}
}
}
private void SendShuttleToStation(Entity<CP14StationTravelingStoreShipTargetComponent> station, 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>(station.Comp.Shuttle);
_shuttles.FTLToCoordinates(station.Comp.Shuttle, shuttleComp, targetXform.Coordinates, targetXform.LocalRotation, hyperspaceTime: 5f, startupTime: startupTime);
}
private void SendShuttleToTradepost(Entity<CP14StationTravelingStoreShipTargetComponent> station)
{
var shuttleComp = Comp<ShuttleComponent>(station.Comp.Shuttle);
_shuttles.FTLToCoordinates(station.Comp.Shuttle, shuttleComp, new EntityCoordinates(station.Comp.TradePostMap, Vector2.Zero), Angle.Zero, hyperspaceTime: 5f);
}
private void OnFTLCompleted(Entity<CP14TravelingStoreShipComponent> ent, ref FTLCompletedEvent args)
{
if (!TryComp<CP14StationTravelingStoreShipTargetComponent>(ent.Comp.Station, out var station))
return;
if (Transform(ent).MapUid == Transform(station.TradePostMap).MapUid) //Landed on tradepost
{
station.OnStation = false;
SellingThings((ent.Comp.Station, station));
UpdateStorePositions((ent.Comp.Station, station));
}
else //Landed on station
{
station.OnStation = true;
}
UpdateAllStores();
}
}

View File

@@ -0,0 +1,87 @@
using System.Text;
using Content.Shared._CP14.TravelingStoreShip;
using Content.Shared.UserInterface;
namespace Content.Server._CP14.TravelingStoreShip;
public sealed partial class CP14CargoSystem
{
public void InitializeStore()
{
SubscribeLocalEvent<CP14CargoStoreComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
}
private void TryInitStore(Entity<CP14CargoStoreComponent> 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);
}
private void OnBeforeUIOpen(Entity<CP14CargoStoreComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
if (ent.Comp.Station is null)
TryInitStore(ent);
UpdateUIProducts(ent);
}
//TODO: redo
private void UpdateAllStores()
{
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)
return;
var prodBuy = new HashSet<CP14StoreUiProductEntry>();
var prodSell = new HashSet<CP14StoreUiProductEntry>();
foreach (var proto in ent.Comp.Station.Value.Comp.CurrentBuyPositions)
{
if (!_proto.TryIndex(proto.Key, out var indexedProto))
continue;
var name = Loc.GetString(indexedProto.Name);
var desc = new StringBuilder();
desc.Append(Loc.GetString(indexedProto.Desc) + "\n");
foreach (var service in indexedProto.Services)
{
desc.Append(service.GetDescription(_proto, EntityManager));
}
prodBuy.Add(new CP14StoreUiProductEntry(proto.Key.Id, indexedProto.Icon, name, desc.ToString(), proto.Value));
}
foreach (var proto in ent.Comp.Station.Value.Comp.CurrentSellPositions)
{
if (!_proto.TryIndex(proto.Key, out var indexedProto))
continue;
var name = Loc.GetString(indexedProto.Name);
var desc = new StringBuilder();
desc.Append(Loc.GetString(indexedProto.Desc) + "\n");
desc.Append(indexedProto.Service.GetDescription(_proto, EntityManager));
prodSell.Add(new CP14StoreUiProductEntry(proto.Key.Id, indexedProto.Icon, name, desc.ToString(), proto.Value));
}
var stationComp = ent.Comp.Station.Value.Comp;
_userInterface.SetUiState(ent.Owner, CP14StoreUiKey.Key, new CP14StoreUiState(prodBuy, prodSell, stationComp.OnStation, stationComp.NextTravelTime));
}
}

View File

@@ -0,0 +1,205 @@
using Content.Server._CP14.Currency;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Events;
using Content.Server.Station.Systems;
using Content.Shared._CP14.Currency;
using Content.Shared._CP14.TravelingStoreShip;
using Content.Shared._CP14.TravelingStoreShip.Prototype;
using Content.Shared.Storage.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._CP14.TravelingStoreShip;
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 SharedTransformSystem _transform = 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!;
private EntityQuery<TransformComponent> _xformQuery;
private IEnumerable<CP14StoreBuyPositionPrototype>? _buyProto;
private IEnumerable<CP14StoreSellPositionPrototype>? _sellProto;
public override void Initialize()
{
base.Initialize();
InitializeStore();
InitializeShuttle();
_xformQuery = GetEntityQuery<TransformComponent>();
_buyProto = _proto.EnumeratePrototypes<CP14StoreBuyPositionPrototype>();
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnProtoReload);
SubscribeLocalEvent<CP14StationTravelingStoreShipTargetComponent, StationPostInitEvent>(OnPostInit);
}
private void OnProtoReload(PrototypesReloadedEventArgs ev)
{
_buyProto = _proto.EnumeratePrototypes<CP14StoreBuyPositionPrototype>();
_sellProto = _proto.EnumeratePrototypes<CP14StoreSellPositionPrototype>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateShuttle();
}
private void OnPostInit(Entity<CP14StationTravelingStoreShipTargetComponent> station, ref StationPostInitEvent args)
{
if (!Deleted(station.Comp.Shuttle))
return;
var tradepostMap = _mapManager.CreateMap();
if (!_loader.TryLoad(tradepostMap, station.Comp.ShuttlePath.ToString(), out var shuttleUids))
return;
var shuttle = shuttleUids[0];
station.Comp.Shuttle = shuttle;
station.Comp.TradePostMap = _mapManager.GetMapEntityId(tradepostMap);
var travelingStoreShipComp = EnsureComp<CP14TravelingStoreShipComponent>(station.Comp.Shuttle);
travelingStoreShipComp.Station = station;
station.Comp.NextTravelTime = _timing.CurTime + TimeSpan.FromSeconds(10f);
UpdateStorePositions(station);
}
private void UpdateStorePositions(Entity<CP14StationTravelingStoreShipTargetComponent> station)
{
station.Comp.CurrentBuyPositions.Clear();
station.Comp.CurrentSellPositions.Clear();
if (_buyProto is not null)
{
foreach (var buyPos in _buyProto)
{
station.Comp.CurrentBuyPositions.Add(buyPos, buyPos.Price.Next(_random));
}
}
if (_sellProto is not null)
{
foreach (var sellPos in _sellProto)
{
station.Comp.CurrentSellPositions.Add(sellPos, sellPos.Price.Next(_random));
}
}
}
private void SellingThings(Entity<CP14StationTravelingStoreShipTargetComponent> station)
{
var shuttle = station.Comp.Shuttle;
//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))
{
if (palletXform.ParentUid != shuttle || !palletXform.Anchored)
continue;
var sentEntities = new HashSet<EntityUid>();
_lookup.GetEntitiesInRange(uid, 1, sentEntities, LookupFlags.Dynamic | LookupFlags.Sundries);
foreach (var ent in sentEntities)
{
if (toSell.Contains(ent) || !_xformQuery.TryGetComponent(ent, out _))
continue;
toSell.Add(ent);
}
}
var cash = 0;
foreach (var sellPos in station.Comp.CurrentSellPositions)
{
if (!_proto.TryIndex(sellPos.Key, out var indexedPos))
continue;
while (indexedPos.Service.TrySell(EntityManager, toSell))
{
cash += sellPos.Value;
}
}
var moneyBox = GetMoneyBox(station);
if (moneyBox is not null)
{
var coord = Transform(moneyBox.Value).Coordinates;
if (cash > 0)
{
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.PP.Key, cash, coord, out var remainder);
cash = remainder;
foreach (var coin in coins)
{
_storage.Insert(moneyBox.Value, coin, out _);
}
}
if (cash > 0)
{
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.GP.Key, cash, coord, out var remainder);
cash = remainder;
foreach (var coin in coins)
{
_storage.Insert(moneyBox.Value, coin, out _);
}
}
if (cash > 0)
{
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.SP.Key, cash, coord, out var remainder);
cash = remainder;
foreach (var coin in coins)
{
_storage.Insert(moneyBox.Value, coin, out _);
}
}
if (cash > 0)
{
var coins = _currency.GenerateMoney(CP14SharedCurrencySystem.CP.Key, cash, coord);
foreach (var coin in coins)
{
_storage.Insert(moneyBox.Value, coin, out _);
}
}
}
}
private EntityUid? GetMoneyBox(Entity<CP14StationTravelingStoreShipTargetComponent> station)
{
var query = EntityQueryEnumerator<CP14CargoMoneyBoxComponent, TransformComponent>();
while (query.MoveNext(out var uid, out _, out var xform))
{
if (xform.GridUid != station.Comp.Shuttle)
continue;
return uid;
}
return null;
}
}

View File

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

View File

@@ -0,0 +1,10 @@
namespace Content.Server._CP14.TravelingStoreShip;
/// <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

@@ -90,5 +90,7 @@ namespace Content.Shared.Verbs
public static readonly VerbCategory PowerLevel = new("verb-categories-power-level", null);
public static readonly VerbCategory CP14RitualBook = new("cp14-verb-categories-ritual-book", null); //CP14
public static readonly VerbCategory CP14CurrencyConvert = new("cp14-verb-categories-currency-converter", null); //CP14
}
}

View File

@@ -4,15 +4,9 @@ namespace Content.Shared._CP14.Currency;
/// Reflects the market value of an item, to guide players through the economy.
/// </summary>
[RegisterComponent, Access(typeof(CP14CurrencySystem))]
[RegisterComponent, Access(typeof(CP14SharedCurrencySystem))]
public sealed partial class CP14CurrencyComponent : Component
{
[DataField]
public int Currency = 1;
/// <summary>
/// allows you to categorize different valuable items in order to, for example, give goals for buying weapons, or earning money specifically.
/// </summary>
[DataField]
public string? Category;
}

View File

@@ -0,0 +1,25 @@
using System.Numerics;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
namespace Content.Shared._CP14.Currency;
/// <summary>
/// Reflects the market value of an item, to guide players through the economy.
/// </summary>
[RegisterComponent]
public sealed partial class CP14CurrencyConverterComponent : Component
{
[DataField]
public int Balance;
[DataField]
public EntityWhitelist? Whitelist;
[DataField]
public Vector2 SpawnOffset = new Vector2(0, -0.4f);
[DataField]
public SoundSpecifier InsertSound = new SoundCollectionSpecifier("CP14Coins");
}

View File

@@ -1,62 +0,0 @@
using Content.Shared.Examine;
using Content.Shared.Stacks;
namespace Content.Shared._CP14.Currency;
public sealed partial class CP14CurrencySystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14CurrencyComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(Entity<CP14CurrencyComponent> currency, ref ExaminedEvent args)
{
var total = GetTotalCurrency(currency, currency.Comp);
var push = Loc.GetString("cp14-currency-examine-title");
push += GetPrettyCurrency(total);
args.PushMarkup(push);
}
public string GetPrettyCurrency(int currency)
{
var total = currency;
if (total <= 0)
return string.Empty;
var gp = total / 100;
total %= 100;
var sp = total / 10;
total %= 10;
var cp = total;
var push = string.Empty;
if (gp > 0) push += " " + Loc.GetString("cp14-currency-examine-gp", ("coin", gp));
if (sp > 0) push += " " + Loc.GetString("cp14-currency-examine-sp", ("coin", sp));
if (cp > 0) push += " " + Loc.GetString("cp14-currency-examine-cp", ("coin", cp));
return push;
}
public int GetTotalCurrency(EntityUid uid, CP14CurrencyComponent? currency = null)
{
if (!Resolve(uid, ref currency))
return 0;
var total = currency.Currency;
if (TryComp<StackComponent>(uid, out var stack))
{
total *= stack.Count;
}
return total;
}
}

View File

@@ -0,0 +1,54 @@
using System.Text;
using Content.Shared.Stacks;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Currency;
public partial class CP14SharedCurrencySystem : EntitySystem
{
public static readonly KeyValuePair<EntProtoId, int> CP = new("CP14CopperCoin1", 1);
public static readonly KeyValuePair<EntProtoId, int> SP = new("CP14SilverCoin1", 10);
public static readonly KeyValuePair<EntProtoId, int> GP = new("CP14GoldCoin1", 100);
public static readonly KeyValuePair<EntProtoId, int> PP = new("CP14PlatinumCoin1", 1000);
public string GetCurrencyPrettyString(int currency)
{
var total = currency;
if (total <= 0)
return string.Empty;
var gp = total / 100;
total %= 100;
var sp = total / 10;
total %= 10;
var cp = total;
var sb = new StringBuilder();
if (gp > 0)
sb.Append( " " + Loc.GetString("cp14-currency-examine-gp", ("coin", gp)));
if (sp > 0)
sb.Append( " " + Loc.GetString("cp14-currency-examine-sp", ("coin", sp)));
if (cp > 0)
sb.Append( " " + Loc.GetString("cp14-currency-examine-cp", ("coin", cp)));
return sb.ToString();
}
public int GetTotalCurrency(EntityUid uid, CP14CurrencyComponent? currency = null)
{
if (!Resolve(uid, ref currency))
return 0;
var total = currency.Currency;
if (TryComp<StackComponent>(uid, out var stack))
{
total *= stack.Count;
}
return total;
}
}

View File

@@ -0,0 +1,9 @@
namespace Content.Shared._CP14.TravelingStoreShip;
/// <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

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

View File

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

View File

@@ -0,0 +1,5 @@
namespace Content.Shared._CP14.TravelingStoreShip;
public class CP14SharedCargoSystem : EntitySystem
{
}

View File

@@ -0,0 +1,46 @@
using Content.Shared._CP14.TravelingStoreShip.Prototype;
using Content.Shared.Destructible.Thresholds;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.TravelingStoreShip;
/// <summary>
/// Add to the station so that traveling store ship starts running on it
/// </summary>
[RegisterComponent]
public sealed partial class CP14StationTravelingStoreShipTargetComponent : Component
{
[DataField]
public EntityUid Shuttle;
[DataField]
public EntityUid TradePostMap;
[DataField]
public bool OnStation;
[DataField]
public ResPath ShuttlePath = new("/Maps/_CP14/Ships/balloon.yml");
[DataField]
public TimeSpan NextTravelTime = TimeSpan.Zero;
[DataField]
public TimeSpan StationWaitTime = TimeSpan.FromMinutes(6);
[DataField]
public TimeSpan TradePostWaitTime = TimeSpan.FromMinutes(4);
[DataField]
public Dictionary<ProtoId<CP14StoreBuyPositionPrototype>, int> CurrentBuyPositions = new(); //Proto, price
[DataField]
public MinMax SpecialBuyPositionCount = new(1, 2);
[DataField]
public Dictionary<ProtoId<CP14StoreSellPositionPrototype>, int> CurrentSellPositions = new(); //Proto, price
[DataField]
public MinMax SpecialSellPositionCount = new(1, 2);
}

View File

@@ -0,0 +1,64 @@
using Content.Shared._CP14.Workbench;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.TravelingStoreShip;
[Serializable, NetSerializable]
public enum CP14StoreUiKey
{
Key,
}
[Serializable, NetSerializable]
public sealed class CP14StoreUiState : BoundUserInterfaceState
{
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)
{
ProductsBuy = productsBuy;
ProductsSell = productsSell;
OnStation = onStation;
NextTravelTime = time;
}
}
[Serializable, NetSerializable]
public readonly struct CP14StoreUiProductEntry : IEquatable<CP14StoreUiProductEntry>
{
public readonly string ProtoId;
public readonly SpriteSpecifier Icon;
public readonly string Name;
public readonly string Desc;
public readonly int Price;
public CP14StoreUiProductEntry(string protoId, SpriteSpecifier icon, string name, string desc, int price)
{
ProtoId = protoId;
Icon = icon;
Name = name;
Desc = desc;
Price = price;
}
public bool Equals(CP14StoreUiProductEntry other)
{
return ProtoId == other.ProtoId;
}
public override bool Equals(object? obj)
{
return obj is CP14StoreUiProductEntry other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(ProtoId, Icon, Name, Desc, Price);
}
}

View File

@@ -0,0 +1,32 @@
using System.Text;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.TravelingStoreShip.Prototype.BuyServices;
public sealed partial class CP14BuyItemsService : CP14StoreBuyService
{
[DataField(required: true)]
public Dictionary<EntProtoId, int> Product = new();
public override void Buy(EntityManager entManager, EntityUid station)
{
foreach (var pai in Product)
{
Logger.Debug($"куплено: {pai.Key} x{pai.Value}");
}
}
public override string? GetDescription(IPrototypeManager prototype, IEntityManager entSys)
{
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-store-service-buy-items") + " \n");
foreach (var pai in Product)
{
if (!prototype.TryIndex(pai.Key, out var indexedProto))
continue;
sb.Append($"{indexedProto.Name} x{pai.Value} \n");
}
return sb.ToString();
}
}

View File

@@ -0,0 +1,46 @@
using Content.Shared.Destructible.Thresholds;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.TravelingStoreShip.Prototype;
/// <summary>
/// Stores the price and product/service pair that players can buy.
/// </summary>
[Prototype("storePositionBuy")]
public sealed partial class CP14StoreBuyPositionPrototype : IPrototype
{
[IdDataField, ViewVariables]
public string ID { get; private set; } = default!;
/// <summary>
/// if true, this item becomes available for purchase only after unlocking by other purchases
/// </summary>
[DataField]
public bool Unlockable = false;
[DataField(required: true)]
public MinMax Price = new();
[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 List<CP14StoreBuyService> Services = new();
}
[ImplicitDataDefinitionForInheritors]
[MeansImplicitUse]
public abstract partial class CP14StoreBuyService
{
public abstract void Buy(EntityManager entManager, EntityUid station);
public abstract string? GetDescription(IPrototypeManager prototype, IEntityManager entSys);
}

View File

@@ -0,0 +1,46 @@
using Content.Shared.Destructible.Thresholds;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.TravelingStoreShip;
/// <summary>
/// Stores the price and product/service pair that players can buy.
/// </summary>
[Prototype("storePositionSell")]
public sealed partial class CP14StoreSellPositionPrototype : IPrototype
{
[IdDataField, ViewVariables]
public string ID { get; private set; } = default!;
/// <summary>
/// if true, this item becomes available for purchase only after unlocking by other purchases
/// </summary>
[DataField]
public bool Unlockable = false;
[DataField(required: true)]
public MinMax Price = new();
[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!;
}
[ImplicitDataDefinitionForInheritors]
[MeansImplicitUse]
public abstract partial class CP14StoreSellService
{
public abstract bool TrySell(EntityManager entManager, HashSet<EntityUid> entities);
public abstract string? GetDescription(IPrototypeManager prototype, IEntityManager entSys);
}

View File

@@ -0,0 +1,57 @@
using Content.Shared.Stacks;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.TravelingStoreShip.Prototype.SellServices;
public sealed partial class CP14SellStackService : CP14StoreSellService
{
[DataField(required: true)]
public ProtoId<StackPrototype> StackId = new();
[DataField(required: true)]
public int Count = 1;
public override bool TrySell(EntityManager entManager, HashSet<EntityUid> entities)
{
var stackSystem = entManager.System<SharedStackSystem>();
Dictionary<Entity<StackComponent>, int> suitable = new();
int needCount = Count;
foreach (var ent in entities)
{
if (needCount <= 0)
break;
if (!entManager.TryGetComponent<StackComponent>(ent, out var stack) || stack.StackTypeId != StackId.Id)
continue;
var consumed = Math.Min(needCount, stack.Count);
suitable.Add((ent,stack), consumed);
needCount -= consumed;
}
if (needCount > 0)
return false;
foreach (var selledEnt in suitable)
{
if (selledEnt.Key.Comp.Count == selledEnt.Value)
{
entities.Remove(selledEnt.Key);
entManager.QueueDeleteEntity(selledEnt.Key);
}
else
{
stackSystem.Use(selledEnt.Key, selledEnt.Value);
}
}
return true;
}
public override string? GetDescription(IPrototypeManager prototype, IEntityManager entSys)
{
return null;
}
}

View File

@@ -0,0 +1,52 @@
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.TravelingStoreShip.Prototype.SellServices;
public sealed partial class CP14SellWhitelistService : CP14StoreSellService
{
[DataField(required: true)]
public EntityWhitelist Whitelist = new();
[DataField(required: true)]
public int Count = 1;
public override bool TrySell(EntityManager entManager, HashSet<EntityUid> entities)
{
var whitelistSystem = entManager.System<EntityWhitelistSystem>();
HashSet<EntityUid> suitable = new();
int needCount = Count;
foreach (var ent in entities)
{
if (needCount <= 0)
break;
if (!entManager.TryGetComponent<MetaDataComponent>(ent, out var metaData) || metaData.EntityPrototype is null)
continue;
if (!whitelistSystem.IsValid(Whitelist, ent))
continue;
suitable.Add(ent);
needCount -= 1;
}
if (needCount > 0)
return false;
foreach (var selledEnt in suitable)
{
entities.Remove(selledEnt);
entManager.QueueDeleteEntity(selledEnt);
}
return true;
}
public override string? GetDescription(IPrototypeManager prototype, IEntityManager entSys)
{
return null;
}
}

View File

@@ -66,4 +66,9 @@
- files: ["book2.ogg"]
license: "CC-BY-4.0"
copyright: 'by InspectorJ of Freesound.org. edit to Mono by TheShuEd.'
source: "https://freesound.org/people/InspectorJ/sounds/416179/"
source: "https://freesound.org/people/InspectorJ/sounds/416179/"
- files: ["coins1.ogg", "coins2.ogg", "coins3.ogg", "coins_fall.ogg"]
license: "CC0-1.0"
copyright: 'by severaltimes of Freesound.org. Cropped by TheShuEd.'
source: "https://freesound.org/people/severaltimes/sounds/173989/"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
cp14-store-buy-alchemy-normalizer-name = Solution normalizer
cp14-store-buy-alchemy-normalizer-desc = Are your alchemists making poor quality potions? Fix it with a modern technological device made by Dwarf! “Alchemical Normalizer” - will remove any residue from your potions!

View File

@@ -0,0 +1,8 @@
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-ironbar-name = 10 iron bars
cp14-store-sell-ironbar-desc = Iron is an indispensable material for the production of... almost anything that has any durability in this world. And surely the Empire could use an extra shipment.
cp14-store-sell-copperbar-name = 10 copper bars
cp14-store-sell-copperbar-desc = We're waiting for a description from the lorekeepers.

View File

@@ -0,0 +1,8 @@
# Buy
cp14-store-service-buy-items = Purchase of goods:
# Sell
cp14-store-service-sell-entities = Sale items:

View File

@@ -0,0 +1,9 @@
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=red]Temporary offer![/color][/bold]

View File

@@ -1,4 +1,12 @@
cp14-currency-examine-title = Рыночная цена:
cp14-currency-converter-examine-title = Валюты:
cp14-currency-examine-gp = [color=#ebad3b]{$coin}зм[/color]
cp14-currency-examine-sp = [color=#bad1d6]{$coin}см[/color]
cp14-currency-examine-cp = [color=#824e27]{$coin}мм[/color]
cp14-currency-examine-cp = [color=#824e27]{$coin}мм[/color]
cp14-currency-converter-insert = Внесено {$cash}мм!
cp14-verb-categories-currency-converter = Вывести валюту:
cp14-currency-converter-get-cp = Вывести как мм (1мм)
cp14-currency-converter-get-sp = Вывести как см (10мм)
cp14-currency-converter-get-gp = Вывести как зм (100мм)
cp14-currency-converter-get-pp = Вывести как пм (1000мм)

View File

@@ -169,4 +169,20 @@ cp14-chatsan-replacement-84 = убийство
cp14-chatsan-word-85 = лкм
cp14-chatsan-replacement-85 = левая рука
cp14-chatsan-word-86 = пкм
cp14-chatsan-replacement-86 = правая рука
cp14-chatsan-replacement-86 = правая рука
cp14-chatsan-word-87 = мм
cp14-chatsan-replacement-87 = меди
cp14-chatsan-word-88 = см
cp14-chatsan-replacement-88 = серебра
cp14-chatsan-word-89 = зм
cp14-chatsan-replacement-89 = золота
cp14-chatsan-word-90 = пм
cp14-chatsan-replacement-90 = платины
cp14-chatsan-word-91 = cp
cp14-chatsan-replacement-91 = copper
cp14-chatsan-word-92 = sp
cp14-chatsan-replacement-92 = silver
cp14-chatsan-word-93 = gp
cp14-chatsan-replacement-93 = gold
cp14-chatsan-word-94 = pp
cp14-chatsan-replacement-94 = platinum

View File

@@ -0,0 +1,2 @@
cp14-store-buy-alchemy-normalizer-name = Нормализатор растворов
cp14-store-buy-alchemy-normalizer-desc = Ваши алхимики делают некачественные зелья? Исправьте это при помощи современного технологического устройства дворфского производства! "Алхимический нормализатор" - удалит из ваших зелий любой осадок!

View File

@@ -0,0 +1,8 @@
cp14-store-sell-goldbar-name = 10 золотых слитков
cp14-store-sell-goldbar-desc = Добыча и обработка золотой руды активно спонсируется империей, использующей золото как валюту и материал для ювелирных украшений.
cp14-store-sell-ironbar-name = 10 железных слитков
cp14-store-sell-ironbar-desc = Железо - незаменимый материал для производства... почти всего, что имеет какую либо долговечность в этом мире. И конечно же, Империя не откажется от дополнительной партии.
cp14-store-sell-copperbar-name = 10 медных слитков
cp14-store-sell-copperbar-desc = Ждем описания от лороведов.

View File

@@ -0,0 +1,8 @@
# Buy
cp14-store-service-buy-items = Покупка товара:
# Sell
cp14-store-service-sell-entities = Продажа предметов:

View File

@@ -0,0 +1,9 @@
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=red]Временное предложение![/color][/bold]

View File

@@ -0,0 +1,348 @@
meta:
format: 6
postmapinit: false
tilemap:
0: Space
49: CP14FloorStonebricksSmallCarved1
entities:
- proto: ""
entities:
- uid: 1
components:
- type: MetaData
name: grid
- type: Transform
pos: -0.73058385,-0.60119945
parent: invalid
- type: MapGrid
chunks:
0,0:
ind: 0,0
tiles: MQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAA
version: 6
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
- type: Broadphase
- type: Physics
bodyStatus: InAir
angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
- type: Fixtures
fixtures: {}
- type: OccluderTree
- type: SpreaderGrid
- type: Shuttle
- type: GridPathfinding
- type: Gravity
gravityShakeSound: !type:SoundPathSpecifier
path: /Audio/Effects/alert.ogg
- type: DecalGrid
chunkCollection:
version: 2
nodes: []
- type: GridAtmosphere
version: 2
data:
chunkSize: 4
- type: GasTileOverlay
- type: RadiationGridResistance
- proto: C14IronCabinetCargo
entities:
- uid: 50
components:
- type: Transform
pos: 0.5,-1.5
parent: 1
- proto: CP14WallmountLamp
entities:
- uid: 51
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -1.5,-3.5
parent: 1
- type: Fixtures
fixtures: {}
- uid: 52
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 2.5,-3.5
parent: 1
- type: Fixtures
fixtures: {}
- uid: 53
components:
- type: Transform
pos: -1.5,4.5
parent: 1
- type: Fixtures
fixtures: {}
- uid: 54
components:
- type: Transform
pos: 2.5,4.5
parent: 1
- type: Fixtures
fixtures: {}
- proto: CP14WallStonebrick
entities:
- uid: 2
components:
- type: Transform
pos: -0.5,-4.5
parent: 1
- uid: 3
components:
- type: Transform
pos: -1.5,-4.5
parent: 1
- uid: 4
components:
- type: Transform
pos: -2.5,-4.5
parent: 1
- uid: 5
components:
- type: Transform
pos: -2.5,-3.5
parent: 1
- uid: 6
components:
- type: Transform
pos: -2.5,-1.5
parent: 1
- uid: 7
components:
- type: Transform
pos: -2.5,-0.5
parent: 1
- uid: 8
components:
- type: Transform
pos: -2.5,0.5
parent: 1
- uid: 11
components:
- type: Transform
pos: -2.5,-2.5
parent: 1
- uid: 13
components:
- type: Transform
pos: -2.5,4.5
parent: 1
- uid: 14
components:
- type: Transform
pos: -2.5,5.5
parent: 1
- uid: 15
components:
- type: Transform
pos: -1.5,5.5
parent: 1
- uid: 18
components:
- type: Transform
pos: 2.5,5.5
parent: 1
- uid: 19
components:
- type: Transform
pos: 3.5,5.5
parent: 1
- uid: 21
components:
- type: Transform
pos: 3.5,4.5
parent: 1
- uid: 25
components:
- type: Transform
pos: 3.5,0.5
parent: 1
- uid: 26
components:
- type: Transform
pos: 3.5,-0.5
parent: 1
- uid: 27
components:
- type: Transform
pos: 3.5,-2.5
parent: 1
- uid: 28
components:
- type: Transform
pos: 3.5,-1.5
parent: 1
- uid: 29
components:
- type: Transform
pos: 3.5,-3.5
parent: 1
- uid: 30
components:
- type: Transform
pos: 3.5,-4.5
parent: 1
- uid: 31
components:
- type: Transform
pos: 2.5,-4.5
parent: 1
- uid: 32
components:
- type: Transform
pos: 1.5,-4.5
parent: 1
- uid: 49
components:
- type: Transform
pos: 0.5,-0.5
parent: 1
- proto: CP14WindowStoneBrick
entities:
- uid: 9
components:
- type: Transform
pos: -2.5,1.5
parent: 1
- uid: 10
components:
- type: Transform
pos: -2.5,2.5
parent: 1
- uid: 12
components:
- type: Transform
pos: -2.5,3.5
parent: 1
- uid: 16
components:
- type: Transform
pos: 0.5,5.5
parent: 1
- uid: 17
components:
- type: Transform
pos: 1.5,5.5
parent: 1
- uid: 20
components:
- type: Transform
pos: -0.5,5.5
parent: 1
- uid: 22
components:
- type: Transform
pos: 3.5,3.5
parent: 1
- uid: 23
components:
- type: Transform
pos: 3.5,2.5
parent: 1
- uid: 24
components:
- type: Transform
pos: 3.5,1.5
parent: 1
- proto: CP14WoodenPalletBuy
entities:
- uid: 33
components:
- type: Transform
pos: 1.5,4.5
parent: 1
- uid: 34
components:
- type: Transform
pos: 1.5,3.5
parent: 1
- uid: 35
components:
- type: Transform
pos: 1.5,2.5
parent: 1
- uid: 36
components:
- type: Transform
pos: 2.5,4.5
parent: 1
- uid: 37
components:
- type: Transform
pos: 2.5,3.5
parent: 1
- uid: 38
components:
- type: Transform
pos: 2.5,2.5
parent: 1
- uid: 39
components:
- type: Transform
pos: 1.5,1.5
parent: 1
- uid: 40
components:
- type: Transform
pos: 2.5,1.5
parent: 1
- proto: CP14WoodenPalletSell
entities:
- uid: 41
components:
- type: Transform
pos: -0.5,4.5
parent: 1
- uid: 42
components:
- type: Transform
pos: -0.5,2.5
parent: 1
- uid: 43
components:
- type: Transform
pos: -0.5,1.5
parent: 1
- uid: 44
components:
- type: Transform
pos: -1.5,4.5
parent: 1
- uid: 45
components:
- type: Transform
pos: -1.5,3.5
parent: 1
- uid: 46
components:
- type: Transform
pos: -1.5,2.5
parent: 1
- uid: 47
components:
- type: Transform
pos: -1.5,1.5
parent: 1
- uid: 48
components:
- type: Transform
pos: -0.5,3.5
parent: 1
...

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -381,7 +381,7 @@
- type: accent
id: chatsanitize
wordReplacements:
#CP14-RU-ChatSanitize-Start
#CP14-ChatSanitize-Start
cp14-chatsan-word-1: cp14-chatsan-replacement-1
cp14-chatsan-word-2: cp14-chatsan-replacement-2
cp14-chatsan-word-3: cp14-chatsan-replacement-3
@@ -468,7 +468,15 @@
cp14-chatsan-word-84: cp14-chatsan-replacement-84
cp14-chatsan-word-85: cp14-chatsan-replacement-85
cp14-chatsan-word-86: cp14-chatsan-replacement-86
#CP14-RU-ChatSanitize-End
cp14-chatsan-word-87: cp14-chatsan-replacement-87
cp14-chatsan-word-88: cp14-chatsan-replacement-88
cp14-chatsan-word-89: cp14-chatsan-replacement-89
cp14-chatsan-word-90: cp14-chatsan-replacement-90
cp14-chatsan-word-91: cp14-chatsan-replacement-91
cp14-chatsan-word-92: cp14-chatsan-replacement-92
cp14-chatsan-word-93: cp14-chatsan-replacement-93
cp14-chatsan-word-94: cp14-chatsan-replacement-94
#CP14-ChatSanitize-End
chatsan-word-1: chatsan-replacement-1
chatsan-word-2: chatsan-replacement-2
chatsan-word-3: chatsan-replacement-3

View File

@@ -26,14 +26,23 @@
- type: CP14Currency
currency: 1
category: Currency
- type: EmitSoundOnLand
sound:
path: /Audio/_CP14/Items/coins_fall.ogg
- type: EmitSoundOnDrop
sound:
collection: CP14Coins
- type: EmitSoundOnPickup
sound:
collection: CP14Coins
# Copper
- type: entity
id: CP14CopperCoin
parent: CP14BaseCoin
name: copper crown
description: The minimum unit of currency in the world of Eberron. One tenth of a silver sovereign.
name: copper coin
description: The smallest denomination coin. Copper.
suffix: 10 coins
components:
- type: Sprite
@@ -72,8 +81,8 @@
- type: entity
id: CP14SilverCoin
parent: CP14BaseCoin
name: silver sovereign
description: Equivalent to 10 copper crowns, and is 1 tenth of a gold galifar.
name: silver coin
description: A valuable coin made from a precisely calibrated silver alloy..... and something else. Equal in value to ten coppers.
suffix: 10 coins
components:
- type: Sprite
@@ -112,8 +121,8 @@
- type: entity
id: CP14GoldCoin
parent: CP14BaseCoin
name: gold galifar
description: Equivalent to 10 silver sovereign, and is 1 tenth of a platinum coin.
name: gold coin
description: A gold, big, beautiful coin. Valuable enough to be stolen by bandits. Equal in value to ten silver coins.
suffix: 10 coins
components:
- type: Sprite
@@ -152,8 +161,8 @@
- type: entity
id: CP14PlatinumCoin
parent: CP14BaseCoin
name: platinum dragon
description: Equivalent to 10 gold galifar, and is the most expensive coin in Eberron's world.
name: platinum coin
description: A platinum coin? It's so rare. Ten gold pieces is the price of one.
suffix: 10 coins
components:
- type: Sprite

View File

@@ -28,7 +28,9 @@
- type: StorageFillVisualizer
maxFillLevels: 4
fillBaseName: wallet
- type: Dumpable #TODO sounds
- type: Dumpable
soundDump:
collection: CP14Coins
multiplier: 0.8
- type: Clothing
slots: [belt]
@@ -44,3 +46,7 @@
contents:
- id: CP14CopperCoin1
- id: CP14CopperCoin1
- id: CP14CopperCoin1
- id: CP14CopperCoin1
- id: CP14CopperCoin1
- id: CP14SilverCoin1

View File

@@ -60,6 +60,4 @@
path: /Audio/_CP14/Effects/thud.ogg
params:
variation: 0.03
volume: 2
- type: CP14Currency
currency: 1500
volume: 2

View File

@@ -39,6 +39,4 @@
cPAnimationLength: 0.3
cPAnimationOffset: -1.3
- type: StaminaDamageOnHit
damage: 4
- type: CP14Currency
currency: 20
damage: 4

View File

@@ -42,6 +42,4 @@
offset: 0.15,0.15
removalTime: 1
- type: ThrowingAngle
angle: 225
- type: CP14Currency
currency: 200
angle: 225

View File

@@ -38,6 +38,4 @@
- type: DamageOnLand
damage:
types:
Slash: 12
- type: CP14Currency
currency: 500
Slash: 12

View File

@@ -44,6 +44,4 @@
collection: CP14Hammering
params:
variation: 0.03
volume: 2
- type: CP14Currency
currency: 200
volume: 2

View File

@@ -29,6 +29,4 @@
collection: MetalThud
cPAnimationLength: 0.25
- type: StaminaDamageOnHit
damage: 6
- type: CP14Currency
currency: 500
damage: 6

View File

@@ -40,6 +40,4 @@
collection: MetalThud
- type: Tag
tags:
- CP14HerbalGathering
- type: CP14Currency
currency: 100
- CP14HerbalGathering

View File

@@ -37,6 +37,4 @@
- type: CP14SkillRequirement
fuckupChance: 0.5
requiredSkills:
- Warcraft
- type: CP14Currency
currency: 1200
- Warcraft

View File

@@ -84,6 +84,4 @@
- type: IncreaseDamageOnWield
damage:
types:
Slash: 6
- type: CP14Currency
currency: 2000
Slash: 6

View File

@@ -0,0 +1,15 @@
- type: entity
id: CP14BaseExpedition
categories: [ HideSpawnMenu ]
parent:
- BaseStation
- BaseStationAllEventsEligible
- BaseStationJobsSpawning
- BaseStationAlertLevels #Checks fail without it
- CP14BaseTrading
- type: entity
id: CP14BaseTrading
abstract: true
components:
- type: CP14StationTravelingStoreShipTarget

View File

@@ -1,7 +0,0 @@
- type: entity
id: CP14BaseExpedition
parent:
- BaseStation
- BaseStationAllEventsEligible
- BaseStationJobsSpawning
- BaseStationAlertLevels #Checks fail without it

View File

@@ -1,5 +1,5 @@
- type: entity
id: CP14DresserBase
id: CP14CabinetBase
parent: BaseStructure
abstract: true
components:
@@ -99,15 +99,15 @@
- type: Rotatable
- type: entity
name: wooden dresser
name: wooden cabinet
parent:
- CP14DresserBase
- CP14CabinetBase
- CP14BaseFlammableSpreading
id: CP14WoodenDresser
description: A regular wooden dresser.
id: CP14WoodenCabinet
description: A regular wooden cabinet.
components:
- type: Sprite
sprite: _CP14/Structures/Storage/Dressers/wood_dresser.rsi
sprite: _CP14/Structures/Storage/Dressers/wood_cabinet.rsi
state: icons
- type: Damageable
damageContainer: Inorganic
@@ -126,6 +126,38 @@
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
parent: CP14CabinetBase
id: C14IronCabinet
name: iron cabinet
description: an iron cabinet. Sturdy, lockable. You can store your valuables here without fear of some burglar taking it all for himself.
components:
- type: Sprite
sprite: _CP14/Structures/Storage/Dressers/iron_cabinet.rsi
state: icons
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: StructuralMetallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 75
behaviors:
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
parent: C14IronCabinet
id: C14IronCabinetCargo
name: money box
description: An armored vault for storing money for trade with the city. City workers will unload their earnings here, or take them from here when you want to buy something.
components:
- type: CP14CargoMoneyBox
- type: entity
name: wooden cupboard
parent:

View File

@@ -0,0 +1,72 @@
- type: entity
id: CP14BasePallets
abstract: true
placement:
mode: SnapgridCenter
components:
- type: Clickable
- type: Sprite
sprite: _CP14/Structures/Furniture/pallets.rsi
snapCardinals: true
drawdepth: FloorTiles
- type: Transform
anchored: true
- type: entity
id: CP14WoodenPallet
parent:
- CP14BasePallets
- CP14BaseFlammable
name: wooden pallet
description: wooden goods stand
components:
- type: Sprite
state: wooden
- type: FootstepModifier
footstepSoundCollection:
collection: FootstepWood
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 500
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 200
behaviors:
- !type:SpawnEntitiesBehavior
spawn:
CP14WoodenPlanks1:
min: 1
max: 1
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: entity
id: CP14WoodenPalletSell
parent: CP14WoodenPallet
name: selling wooden pallet
components:
- type: CP14SellingPalett
- type: Sprite
layers:
- state: wooden
- state: sell
- type: entity
id: CP14WoodenPalletBuy
parent: CP14WoodenPallet
name: buying wooden pallet
components:
- type: CP14BuyingPalett
- type: Sprite
layers:
- state: wooden
- state: buy

View File

@@ -0,0 +1,50 @@
- type: entity
id: CP14CashConverter
parent: BaseStructure
name: cash converter
description: A simple magical device connected by small portals to the empire's central bank. It allows you to convert coins between denominations, and doesn't even charge interest! That's generous.
components:
- type: InteractionOutline
- type: CP14CurrencyConverter
- type: Transform
anchored: true
- type: Sprite
drawdepth: Mobs
noRot: true
offset: 0, 0.2
sprite: _CP14/Structures/Specific/Economy/cash_device.rsi
state: base
- type: Anchorable
delay: 1
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 80
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- trigger:
!type:DamageTrigger
damage: 40
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
#- !type:CP14ThrowStoredCurrencyBehaviour #TODO
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.4,-0.4,0.4,0.4"
density: 190
mask:
- MachineMask
layer:
- MachineLayer

View File

@@ -0,0 +1,22 @@
- type: entity
parent: BaseStructure
id: CP14TravelingShop
name: city trading information board
description: Allows you to track what the city is selling and buying right now.
components:
- type: Sprite
snapCardinals: true
sprite: _CP14/Structures/Furniture/workbench.rsi
state: filler
- type: Icon
sprite: _CP14/Structures/Furniture/workbench.rsi
state: filler
- type: ActivatableUI
key: enum.CP14StoreUiKey.Key
- type: Clickable
- type: InteractionOutline
- type: CP14CargoStore
- type: UserInterface
interfaces:
enum.CP14StoreUiKey.Key:
type: CP14StoreBoundUserInterface

View File

@@ -0,0 +1,19 @@
- type: entity
id: CP14TravelingStoreshipAnchor
name: traveling storeship anchor
placement:
mode: SnapgridCenter
description: the point of adhesion of a traveling storeship to the surface. Keep your eyes to the sky so you don't get crushed!
components:
- type: Clickable
- type: Sprite
sprite: _CP14/Structures/traveling_storeship_anchor.rsi
state: base
drawdepth: FloorTiles
- type: Icon
sprite: _CP14/Structures/traveling_storeship_anchor.rsi
state: base
- type: Transform
anchored: true
- type: CP14TravelingStoreShipFTLTarget
- type: FTLSmashImmune

View File

@@ -31,7 +31,7 @@
- ItemMask
layer:
- SlipLayer
fix2: #For melee like water bucket
fix2: #For melee like water bucket (dont work)
hard: true
shape:
!type:PhysShapeAabb

View File

@@ -20,7 +20,7 @@
minPlayers: 0
stations:
Dev:
stationProto: StandardStationArena
stationProto: CP14BaseExpedition
components:
- type: StationNameSetup
mapNameTemplate: "Dev"

View File

@@ -47,4 +47,11 @@
id: CP14Book
files:
- /Audio/_CP14/Items/book1.ogg
- /Audio/_CP14/Items/book2.ogg
- /Audio/_CP14/Items/book2.ogg
- type: soundCollection
id: CP14Coins
files:
- /Audio/_CP14/Items/coins1.ogg
- /Audio/_CP14/Items/coins2.ogg
- /Audio/_CP14/Items/coins3.ogg

View File

@@ -0,0 +1,14 @@
- type: storePositionBuy
id: CP14AlchemyNormalizer
name: cp14-store-buy-alchemy-normalizer-name
desc: cp14-store-buy-alchemy-normalizer-desc
icon:
sprite: _CP14/Structures/Specific/Alchemy/normalizer.rsi
state: base
price:
min: 400
max: 500
services:
- !type:CP14BuyItemsService
product:
CP14AlchemyNormalizer: 1

View File

@@ -0,0 +1,41 @@
- type: storePositionSell
id: GoldBars
name: cp14-store-sell-goldbar-name
desc: cp14-store-sell-goldbar-desc
icon:
sprite: _CP14/Objects/Materials/gold_bar.rsi
state: bar_3
price:
min: 1000
max: 1000
service: !type:CP14SellStackService
stackId: CP14GoldBar
count: 10
- type: storePositionSell
id: IronBars
name: cp14-store-sell-ironbar-name
desc: cp14-store-sell-ironbar-desc
icon:
sprite: _CP14/Objects/Materials/iron_bar.rsi
state: bar_3
price:
min: 500
max: 500
service: !type:CP14SellStackService
stackId: CP14IronBar
count: 10
- type: storePositionSell
id: CopperBars
name: cp14-store-sell-copperbar-name
desc: cp14-store-sell-copperbar-desc
icon:
sprite: _CP14/Objects/Materials/copper_bar.rsi
state: bar_3
price:
min: 400
max: 500
service: !type:CP14SellStackService
stackId: CP14CopperBar
count: 10

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

View File

@@ -0,0 +1,23 @@
{
"version": 1,
"size": {
"x": 8,
"y": 8
},
"license": "CLA",
"copyright": "Created by TheShuEd (Github) for CrystallPunk14",
"states": [
{
"name": "c"
},
{
"name": "g"
},
{
"name": "p"
},
{
"name": "s"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

View File

@@ -0,0 +1,20 @@
{
"version": 1,
"license": "CLA",
"copyright": "Created by TheShuEd (Github) for CrystallPunk14 and resprite by Jaraten and vladimir.s",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "wooden"
},
{
"name": "buy"
},
{
"name": "sell"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,15 @@
{
"version": 1,
"license": "CLA",
"copyright": "Created by Jaraten and TheShuEd (Github) for CrystallPunk 14",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "base",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,19 @@
{
"version": 1,
"license": "CLA",
"copyright": "Created by TheShuEd for CrystallPunk14",
"size": {
"x": 32,
"y": 96
},
"states": [
{
"name": "icon",
"directions": 4
},
{
"name": "icon-open",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,15 @@
{
"version": 1,
"license": "CLA",
"copyright": "Created by TheShuEd for CrystallPunk14",
"size": {
"x": 64,
"y": 64
},
"states": [
{
"name": "base",
"directions": 4
}
]
}

View File

@@ -101,6 +101,9 @@ CP14GatherableBloodgrass: CP14GatherableBloodFlower
CP14GatherableFlowersRed: CP14GatherableBloodFlower
CP14VialSmallBloodgrassSap: CP14VialSmallBloodFlowerSap
CP14BarrelBloodGrassSap: CP14BarrelBloodFlowerSap
#2024-10-11
CP14WoodenDresser: CP14WoodenCabinet
# <---> CrystallPunk migration zone end