Compare commits

...

28 Commits

Author SHA1 Message Date
Deserty0
3fb169a18a Explode system numerics 2025-10-29 23:19:03 +10:00
Deserty0
8564d878c3 Update CP14FishingSystem.cs 2025-10-29 23:15:46 +10:00
Deserty0
29cbf0560f Cleanup 2025-10-29 23:06:20 +10:00
Ed
2da1977ae1 clean up 2025-10-28 13:46:04 +03:00
Deserty0
f53c68d272 123 2025-10-27 00:22:59 +10:00
Deserty0
44b757a033 Merge remote-tracking branch 'upstream/master' into des-fishing 2025-10-26 22:45:15 +10:00
Deserty0
a1e452de63 comments 2025-10-17 14:46:01 +10:00
Deserty0
f31e82f7b3 123 2025-10-17 14:28:03 +10:00
Deserty0
2fdd3923f5 swaggy swaggy swags 2025-10-05 17:33:13 +10:00
Deserty0
88575e2286 I am become Death, the Code Destroyer 2025-10-05 16:48:09 +10:00
Deserty0
636b2a5514 prediction
needs heavy cleanups
2025-10-04 17:00:47 +10:00
Deserty0
90797f88b6 help me 2025-09-19 22:22:56 +10:00
Deserty0
122193bc7b quick fix 2025-09-12 13:45:21 +10:00
Deserty0
0c7dd03f79 я усталь 2025-09-12 13:44:21 +10:00
Deserty0
59d776ca72 MASSIVE UPDATE NUMBER ONE 2025-09-12 12:41:17 +10:00
Deserty0
782ae7c588 Update CP14FishBaseBehavior.cs 2025-09-11 03:07:27 +10:00
Deserty0
aa16b587fc Merge branch 'des-fishing' of https://github.com/crystallpunk-14/crystall-punk-14 into des-fishing 2025-09-11 01:14:46 +10:00
Deserty0
4de7747fb9 im actualy idk
ну шо ща пиздос
2025-09-11 01:14:20 +10:00
Deserty0
45f4668b11 Merge branch 'master' into des-fishing 2025-09-10 09:39:07 +10:00
Deserty0
5fbb8789df я опять невдупляю почему оно не работает продолжу завтра 2025-09-08 00:06:17 +10:00
Deserty0
c8b0560c7a UI 2025-09-07 04:31:11 +10:00
Deserty0
5d2fb2c4bc 13234546tyu 2025-09-07 01:37:52 +10:00
Deserty0
3759f240b3 123 2025-09-06 04:14:26 +10:00
Deserty0
af2e62ff59 я очень устал босс 2025-09-06 03:03:37 +10:00
Deserty0
307c0dc01b fix meta 2025-09-05 23:55:54 +10:00
Deserty0
0c44159c49 last work before silksong 2025-09-04 23:26:38 +10:00
Deserty0
dcecaded17 slow work 2025-09-04 22:54:20 +10:00
Deserty0
310126c660 init 2025-09-04 02:32:59 +10:00
34 changed files with 1079 additions and 2 deletions

View File

@@ -318,6 +318,7 @@ namespace Content.Client.Options.UI.Tabs
AddButton(CP14ContentKeyFunctions.CP14OpenSkillMenu);
AddButton(CP14ContentKeyFunctions.OpenBelt2);
AddButton(CP14ContentKeyFunctions.SmartEquipBelt2);
AddButton(CP14ContentKeyFunctions.CP14FishingAction);
//CP14 end
foreach (var control in _keyControls.Values)

View File

@@ -0,0 +1,86 @@
using System.Diagnostics.CodeAnalysis;
using Content.Client.Hands.Systems;
using Content.Shared._CP14.Fishing;
using Content.Shared._CP14.Fishing.Components;
using Content.Shared._CP14.Input;
using Robust.Client.GameObjects;
using Robust.Shared.Input;
namespace Content.Client._CP14.Fishing;
public sealed class CP14FishingSystem : CP14SharedFishingSystem
{
[Dependency] private readonly InputSystem _input = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14FishingRodComponent, AfterAutoHandleStateEvent>(OnFishingRodState);
}
public override void Update(float dt)
{
base.Update(dt);
var heldUid = _hands.GetActiveHandEntity();
if (!TryComp<CP14FishingRodComponent>(heldUid, out var fishingRodComponent))
return;
UpdatePressedButtons(fishingRodComponent);
}
/// <summary>
/// Handles BUI updates
/// </summary>
private void OnFishingRodState(Entity<CP14FishingRodComponent> entity, ref AfterAutoHandleStateEvent args)
{
if (_userInterface.TryGetOpenUi(entity.Owner, CP14FishingUiKey.Key, out var bui))
{
bui.Update();
}
}
/// <summary>
/// Handles user inputs
/// </summary>
private void UpdatePressedButtons(CP14FishingRodComponent fishingRodComponent)
{
if (fishingRodComponent.CaughtFish is null)
return;
var reelKey = _input.CmdStates.GetState(CP14ContentKeyFunctions.CP14FishingAction) == BoundKeyState.Down;
if (fishingRodComponent.Reeling == reelKey)
return;
fishingRodComponent.Reeling = reelKey;
RaiseNetworkEvent(new CP14FishingReelKeyMessage(reelKey));
}
/// <summary>
/// Used to get fish and rod component by FishingUI
/// </summary>
/// <returns>True when all info can be resolved</returns>
public bool GetInfo([NotNullWhen(true)] out CP14FishingRodComponent? rodComponent,
[NotNullWhen(true)] out CP14FishComponent? fishComponent)
{
rodComponent = null;
fishComponent = null;
var heldUid = _hands.GetActiveHandEntity();
if (!TryComp<CP14FishingRodComponent>(heldUid, out var posRodComponent))
return false;
if (!TryComp<CP14FishComponent>(posRodComponent.CaughtFish, out var posFishComponent))
return false;
rodComponent = posRodComponent;
fishComponent = posFishComponent;
return true;
}
}

View File

@@ -0,0 +1,40 @@
using Content.Shared._CP14.Fishing.Components;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Fishing.UI;
[UsedImplicitly]
public sealed class CP14FishingBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[ViewVariables]
private CP14FishingWindow? _fishingWindow;
public CP14FishingBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
if (!EntMan.TryGetComponent<CP14FishingRodComponent>(Owner, out var rodComponent))
return;
if (!_prototypeManager.Resolve(rodComponent.FishingMinigame, out var fishingMinigame))
return;
_fishingWindow = this.CreateWindow<CP14FishingWindow>();
_fishingWindow.InitVisuals(fishingMinigame);
}
public override void Update()
{
base.Update();
_fishingWindow?.UpdateDraw();
}
}

View File

@@ -0,0 +1,7 @@
<!-- Empty fishing popup -->
<fishingWindow:CP14FishingWindow
xmlns="https://spacestation14.io"
xmlns:fishingWindow="clr-namespace:Content.Client._CP14.Fishing.UI"
Name="FishingWindow" MaxSize="0 0">
<PanelContainer Name="FishingBackground"></PanelContainer>
</fishingWindow:CP14FishingWindow>

View File

@@ -0,0 +1,85 @@
using Content.Client.Resources;
using Content.Shared._CP14.Fishing;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._CP14.Fishing.UI;
[GenerateTypedNameReferences]
public sealed partial class CP14FishingWindow : BaseWindow
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IEntityManager _entity = default!;
private readonly CP14FishingSystem _fishing;
private CP14FishingMinigamePrototype? _fishingMinigame;
private Texture? _floatTexture;
private Texture? _fishTexture;
public CP14FishingWindow()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_fishing = _entity.System<CP14FishingSystem>();
}
public void InitVisuals(CP14FishingMinigamePrototype fishingMinigame)
{
FishingWindow.Visible = false;
// Hashing
_fishingMinigame = fishingMinigame;
// Getting data
var background = _fishingMinigame.Background;
var fish = _fishingMinigame.FishIcon;
var floater = _fishingMinigame.Float;
FishingWindow.MaxSize = background.Size;
FishingWindow.SetSize = background.Size;
FishingBackground.SetSize = background.Size;
var backgroundTexture = _resourceCache.GetTexture(background.Texture);
FishingBackground.PanelOverride = new StyleBoxTexture
{
Texture = backgroundTexture,
};
_floatTexture = _resourceCache.GetTexture(floater.Texture);
_fishTexture = _resourceCache.GetTexture(fish.Texture);
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
if (_fishingMinigame is null || _floatTexture is null || _fishTexture is null)
return;
if (!_fishing.GetInfo(out var rodComponent, out var fishComponent))
return;
var floatBox = CalculateUIBox(_fishingMinigame.Float, rodComponent.FloatPosition);
var fishBox = CalculateUIBox(_fishingMinigame.FishIcon, fishComponent.Position);
handle.DrawTextureRect(_floatTexture, floatBox);
handle.DrawTextureRect(_fishTexture, fishBox);
}
private static UIBox2 CalculateUIBox(FishingMinigameElementData data, float verticalOffset)
{
var left = data.Offset.X;
var top = data.Offset.Y + verticalOffset + data.Size.Y;
var right = data.Offset.X + data.Size.X;
var bottom = data.Offset.Y + verticalOffset;
return new UIBox2(left, top, right, bottom);
}
}

View File

@@ -11,6 +11,7 @@ namespace Content.Client._CP14.Input
human.AddFunction(CP14ContentKeyFunctions.OpenBelt2);
human.AddFunction(CP14ContentKeyFunctions.SmartEquipBelt2);
human.AddFunction(CP14ContentKeyFunctions.CP14OpenSkillMenu);
human.AddFunction(CP14ContentKeyFunctions.CP14FishingAction);
}
}
}

View File

@@ -0,0 +1,122 @@
using System.Linq;
using System.Numerics;
using Content.Shared._CP14.Fishing;
using Content.Shared._CP14.Fishing.Components;
using Content.Shared.EntityTable;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Player;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._CP14.Fishing;
public sealed class CP14FishingSystem : CP14SharedFishingSystem
{
[Dependency] private readonly EntityTableSystem _entityTable = default!;
[Dependency] private readonly MetaDataSystem _meta = default!;
[Dependency] private readonly MapSystem _map = default!;
[Dependency] private readonly PvsOverrideSystem _pvs= default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private MapId? _mapId;
private EntityQuery<CP14FishingPondComponent> _pondQuery;
private EntityQuery<CP14FishComponent> _fishQuery;
public override void Initialize()
{
base.Initialize();
_pondQuery = GetEntityQuery<CP14FishingPondComponent>();
_fishQuery = GetEntityQuery<CP14FishComponent>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
base.Update(frameTime);
var curTime = _gameTiming.CurTime;
var query = EntityQueryEnumerator<CP14FishingRodComponent>();
// Seeding prediction doesnt work
while (query.MoveNext(out var uid, out var fishRod))
{
TryToCatchFish((uid, fishRod), curTime);
}
}
/// <summary>
/// Tries to cath fish
/// </summary>
private bool TryToCatchFish(Entity<CP14FishingRodComponent> rod, TimeSpan curTime)
{
if (rod.Comp.CaughtFish is not null)
return false;
if (rod.Comp.User is null)
return false;
if (rod.Comp.FishingFloat is null)
return false;
if (rod.Comp.Target is null)
return false;
if (curTime < rod.Comp.FishingTime)
return false;
var pond = rod.Comp.Target;
if (!_pondQuery.TryComp(pond, out var pondComp))
return false;
if (pondComp.LootTable is null)
return false;
if (!_proto.Resolve(pondComp.LootTable, out var lootTable))
return false;
var fishes = _entityTable.GetSpawns(lootTable, _random.GetRandom());
var fishId = fishes.First();
EnsurePausedMap();
var fish = PredictedSpawnAtPosition(fishId, new EntityCoordinates(_map.GetMap(_mapId!.Value), Vector2.Zero));
if (!_player.TryGetSessionByEntity(rod.Comp.User.Value, out var session))
return false;
if (!_fishQuery.TryComp(fish, out var fishComp))
return false;
_pvs.AddSessionOverride(fish, session);
rod.Comp.CaughtFish = fish;
fishComp.GetAwayTime = curTime;
fishComp.GetAwayTime += TimeSpan.FromSeconds(_random.NextDouble(rod.Comp.MinAwaitTime, rod.Comp.MaxAwaitTime));
DirtyField(rod, rod.Comp, nameof(CP14FishingRodComponent.CaughtFish));
DirtyField(fish, fishComp, nameof(CP14FishComponent.GetAwayTime));
return true;
}
/// <summary>
/// Ensures that paused map exists
/// </summary>
private void EnsurePausedMap()
{
if (_map.MapExists(_mapId))
return;
var mapUid = _map.CreateMap(out var newMapId);
_meta.SetEntityName(mapUid, Loc.GetString("fishing-paused-map-name"));
_mapId = newMapId;
_map.SetPaused(mapUid, true);
}
}

View File

@@ -0,0 +1,46 @@
using JetBrains.Annotations;
using Robust.Shared.Random;
namespace Content.Shared._CP14.Fishing.Behaviors;
[ImplicitDataDefinitionForInheritors, UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public abstract partial class CP14FishBaseBehavior
{
/// <summary>
/// Calculates fish position
/// </summary>
/// <param name="random"> Robust random interface </param>
/// <param name="fishPos"> Current position of fish </param>
/// <param name="fishDest"> Fish destination </param>
/// <returns> Calculated fish position </returns>
public float TryCalculatePosition(IRobustRandom random, float fishPos, float fishDest)
{
var speed = CalculateSpeed(random);
var nextPos = float.Lerp(fishPos, fishDest, speed);
return nextPos;
}
/// <summary>
/// Formula to calculate fish speed
/// </summary>
public abstract float CalculateSpeed(IRobustRandom random);
/// <summary>
/// Speed of a fish
/// </summary>
[DataField]
public float Speed = 0.25f;
/// <summary>
/// Salt of speed calculations
/// </summary>
[DataField]
public float Difficulty = 2f;
/// <summary>
/// Base time which fish will wait in destination before selecting new
/// </summary>
[DataField]
public TimeSpan BaseWaitTime = TimeSpan.FromSeconds(5);
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.Random;
namespace Content.Shared._CP14.Fishing.Behaviors;
public sealed partial class CP14FishDartBehaviour : CP14FishBaseBehavior
{
public override float CalculateSpeed(IRobustRandom random)
{
return Speed * (0.5f + random.NextFloat() * Difficulty);
}
}

View File

@@ -0,0 +1,64 @@
using System.Numerics;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared._CP14.Fishing;
/// <summary>
/// Prototype of fishing minigame. Starter position of minigame in the bottom
/// </summary>
[Prototype("CP14FishingMinigameStyle")]
public sealed class CP14FishingMinigamePrototype : IPrototype
{
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// Fishing minigame background data
/// </summary>
[DataField(required: true)]
public FishingMinigameElementData Background;
/// <summary>
/// Fishing minigame fish icon data
/// </summary>
[DataField(required: true)]
public FishingMinigameElementData FishIcon;
/// <summary>
/// Fishing minigame progressbar data
/// </summary>
[DataField(required: true)]
public FishingMinigameElementData Progressbar;
/// <summary>
/// Fishing minigame float data
/// </summary>
[DataField(required: true)]
public FishingMinigameElementData Float;
/// <summary>
/// Size of the area where the float and fish will move
/// </summary>
[DataField(required: true)]
public float FishingMinigameSize;
}
[DataDefinition]
public partial struct FishingMinigameElementData
{
/// <summary>
/// Texture path
/// </summary>
[DataField(required: true)] public ResPath Texture;
/// <summary>
/// Size of a texture
/// </summary>
[DataField(required: true)] public Vector2 Size;
/// <summary>
/// Offset from bottom left corner
/// </summary>
[DataField(required: true)] public Vector2 Offset;
}

View File

@@ -0,0 +1,21 @@
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Fishing;
/// <summary>
/// Key for CP14FishingBoundUserInterface
/// </summary>
[Serializable, NetSerializable]
public enum CP14FishingUiKey : byte
{
Key,
}
/// <summary>
/// Event for sending reeling key status
/// </summary>
[Serializable, NetSerializable]
public sealed class CP14FishingReelKeyMessage(bool reeling) : EntityEventArgs
{
public bool Reeling = reeling;
}

View File

@@ -0,0 +1,278 @@
using Content.Shared._CP14.Fishing.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Throwing;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._CP14.Fishing;
public abstract class CP14SharedFishingSystem : EntitySystem
{
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly SharedUserInterfaceSystem _userInterface = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
private EntityQuery<CP14FishComponent> _fishQuery;
public override void Initialize()
{
base.Initialize();
_fishQuery = GetEntityQuery<CP14FishComponent>();
SubscribeLocalEvent<CP14FishingRodComponent, AfterInteractEvent>(OnInteract);
SubscribeLocalEvent<CP14FishingRodComponent, DroppedEvent>(OnDropEvent);
SubscribeNetworkEvent<CP14FishingReelKeyMessage>(OnReelingMessage);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var curTime = _gameTiming.CurTime;
var query = EntityQueryEnumerator<CP14FishingRodComponent>();
// Seeding prediction doesnt work
while (query.MoveNext(out var uid, out var fishRod))
{
if (fishRod.User is null)
continue;
RevalidateFishing((uid, fishRod));
if (fishRod.User is null)
continue;
if (fishRod.FishingFloat is null)
continue;
if (fishRod.Target is null)
continue;
var fish = fishRod.CaughtFish;
if (fishRod.CaughtFish is not null && _fishQuery.TryComp(fish, out var fishComp)) //TODO: remove multiple fish TryComp in next functions
continue;
_random.SetSeed((int)_gameTiming.CurTick.Value + GetNetEntity(uid).Id);
UpdateFishWaitingStatus((uid, fishRod), curTime);
UpdatePositions((uid, fishRod), curTime);
}
}
/// <summary>
/// Handles float and fish positions updates
/// </summary>
/// <remarks> Please burn it down </remarks>
private void UpdatePositions(Entity<CP14FishingRodComponent> rod, TimeSpan curTime)
{
if (rod.Comp.CaughtFish is null)
return;
if (!rod.Comp.FishHooked)
return;
var fish = rod.Comp.CaughtFish;
if (!_fishQuery.TryComp(fish, out var fishComp))
return;
_proto.Resolve(rod.Comp.FishingMinigame, out var minigamePrototype);
if (minigamePrototype is null)
return;
var maxCord = minigamePrototype.FishingMinigameSize;
var floatSpeed = rod.Comp.FloatSpeed;
var floatPosition = rod.Comp.FloatPosition;
if (rod.Comp.Reeling)
{
Math.Clamp(floatPosition + floatSpeed, 0, maxCord);
}
else
{
Math.Clamp(floatPosition - floatSpeed, 0, maxCord);
}
var fishPos = fishComp.Position;
var fishDest = fishComp.Destination;
var fishBaseWaitTime = fishComp.Behavior.BaseWaitTime;
if (Math.Abs(fishPos - fishDest) < 0.1f)
{
UpdateFishDestination((fish.Value, fishComp), curTime, maxCord);
fishComp.SelectPosTime = curTime + fishBaseWaitTime + fishBaseWaitTime * 0.2 * _random.NextFloat(-1, 1);
}
else
{
fishComp.Position = fishComp.Behavior.TryCalculatePosition(_random, fishComp.Position, fishComp.Destination);
}
DirtyField(rod, rod.Comp, nameof(CP14FishingRodComponent.FloatPosition));
DirtyField(fish.Value, fishComp, nameof(CP14FishComponent.Position));
}
/// <summary>
/// Calculates new fish destination
/// </summary>
private void UpdateFishDestination(Entity<CP14FishComponent> fish, TimeSpan curTime, float maxCord)
{
if (curTime < fish.Comp.SelectPosTime)
return;
fish.Comp.Destination = _random.NextFloat(0, maxCord);
DirtyField(fish, fish.Comp, nameof(CP14FishComponent.Destination));
}
/// <summary>
/// Handles if fish got caught or flees
/// </summary>
private void UpdateFishWaitingStatus(Entity<CP14FishingRodComponent> rod, TimeSpan curTime)
{
if (rod.Comp.CaughtFish is null)
return;
if (rod.Comp.FishHooked)
return;
if (rod.Comp.User is null)
return;
var fish = rod.Comp.CaughtFish;
if (!_fishQuery.TryComp(fish, out var fishComp))
return;
if (rod.Comp.Reeling)
{
rod.Comp.FishHooked = true;
_userInterface.TryOpenUi(rod.Owner, CP14FishingUiKey.Key, rod.Comp.User.Value);
DirtyField(rod, rod.Comp, nameof(CP14FishingRodComponent.FishHooked));
return;
}
if (curTime < fishComp.GetAwayTime)
return;
rod.Comp.CaughtFish = null;
DirtyField(rod, rod.Comp, nameof(CP14FishingRodComponent.CaughtFish));
PredictedDel(fish);
}
/// <summary>
/// Validates if user is still in range of fishing float
/// </summary>
private void RevalidateFishing(Entity<CP14FishingRodComponent> rod)
{
if (rod.Comp.FishingFloat is null)
return;
if (_transform.InRange(rod.Owner, rod.Comp.FishingFloat.Value, rod.Comp.MaxFishingDistance * 1.5f))
return;
PredictedDel(rod.Comp.FishingFloat);
rod.Comp.FishHooked = false;
rod.Comp.CaughtFish = null;
rod.Comp.FishingFloat = null;
rod.Comp.Target = null;
rod.Comp.User = null;
DirtyFields(rod,
rod.Comp,
null,
nameof(CP14FishingRodComponent.FishingFloat),
nameof(CP14FishingRodComponent.Target),
nameof(CP14FishingRodComponent.User),
nameof(CP14FishingRodComponent.CaughtFish),
nameof(CP14FishingRodComponent.FishHooked));
}
/// <summary>
/// Sets <see cref="CP14FishingRodComponent.Reeling"/> to user button status
/// </summary>
private void OnReelingMessage(CP14FishingReelKeyMessage msg, EntitySessionEventArgs args)
{
if (args.SenderSession.AttachedEntity is not { } player)
return;
if (!_hands.TryGetActiveItem(player, out var activeItem) ||
!TryComp<CP14FishingRodComponent>(activeItem, out var fishingRodComponent))
return;
fishingRodComponent.Reeling = msg.Reeling;
DirtyField(activeItem.Value, fishingRodComponent, nameof(CP14FishingRodComponent.Reeling));
}
/// <summary>
/// Starts new fishing process when user interacts with pond using fishing rod
/// </summary>
private void OnInteract(Entity<CP14FishingRodComponent> rod, ref AfterInteractEvent args)
{
if (args.Handled)
return;
if (args.Target is not { Valid: true })
return;
if (rod.Comp.FishingFloat is not null)
return;
if (!TryComp<CP14FishingPondComponent>(args.Target, out _))
return;
if (!_interaction.InRangeUnobstructed(rod.Owner, args.Target.Value, rod.Comp.MaxFishingDistance))
return;
args.Handled = true;
rod.Comp.FishingTime = _gameTiming.CurTime;
rod.Comp.FishingTime += TimeSpan.FromSeconds(_random.NextDouble(rod.Comp.MinAwaitTime, rod.Comp.MaxAwaitTime));
rod.Comp.User = args.User;
DirtyFields(rod, rod.Comp, null, nameof(CP14FishingRodComponent.FishingTime), nameof(CP14FishingRodComponent.User));
ThrowFishingFloat(rod, args.Target.Value);
}
/// <summary>
/// Deletes <see cref="CP14FishingRodComponent.User"/> link
/// </summary>
private void OnDropEvent(Entity<CP14FishingRodComponent> rod, ref DroppedEvent args)
{
rod.Comp.User = null;
DirtyField(rod, rod.Comp, nameof(CP14FishingRodComponent.User));
}
/// <summary>
/// Spawns and throws fishing float
/// </summary>
private void ThrowFishingFloat(Entity<CP14FishingRodComponent> rod, EntityUid fishingPond)
{
var rodCoords = Transform(rod).Coordinates;
var targetCoords = Transform(fishingPond).Coordinates;
var fishingFloat = PredictedSpawnAtPosition(rod.Comp.FloatPrototype, rodCoords);
rod.Comp.FishingFloat = fishingFloat;
rod.Comp.Target = fishingPond;
DirtyFields(rod,
rod.Comp,
null,
nameof(CP14FishingRodComponent.FishingFloat),
nameof(CP14FishingRodComponent.Target));
_throwing.TryThrow(fishingFloat, targetCoords, rod.Comp.ThrowPower, recoil: false, doSpin: false);
}
}

View File

@@ -0,0 +1,53 @@
using Content.Shared._CP14.Fishing.Behaviors;
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.Fishing.Components;
/// <summary>
/// Component for fish, that can be caught via fishing
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause, Access(typeof(CP14SharedFishingSystem))]
public sealed partial class CP14FishComponent : Component
{
/// <summary>
/// Fish behaviour that will be used for speed calculations
/// </summary>
[DataField(required: true), ViewVariables]
public CP14FishBaseBehavior Behavior;
/// <summary>
/// Time when fish will select next position
/// </summary>
[AutoNetworkedField, AutoPausedField, ViewVariables]
public TimeSpan SelectPosTime = TimeSpan.Zero;
/// <summary>
/// Time when the fish will get away if it is not hooked
/// </summary>
[AutoNetworkedField, AutoPausedField, ViewVariables]
public TimeSpan GetAwayTime = TimeSpan.Zero;
/// <summary>
/// Fish current position in minigame coordinates
/// </summary>
[AutoNetworkedField, ViewVariables]
public float Position;
/// <summary>
/// Fish destination in minigame coordinates
/// </summary>
[AutoNetworkedField, ViewVariables]
public float Destination;
/// <summary>
/// Minimal time before fish will get away
/// </summary>
[DataField]
public double MinAwaitTime = 5;
/// <summary>
/// Maximum time before fish will get away
/// </summary>
[DataField]
public double MaxAwaitTime = 10;
}

View File

@@ -0,0 +1,18 @@
using Content.Shared.EntityTable;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Fishing.Components;
/// <summary>
/// Component for fishing ponds
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(CP14SharedFishingSystem))]
public sealed partial class CP14FishingPondComponent : Component
{
/// <summary>
/// LootTable of loot that can be caught in this pond. Only first spawn will be caught
/// </summary>
[DataField]
public ProtoId<EntityTablePrototype>? LootTable;
}

View File

@@ -0,0 +1,105 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Fishing.Components;
/// <summary>
/// Allows to fish with this item
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true, true), AutoGenerateComponentPause, Access(typeof(CP14SharedFishingSystem))]
public sealed partial class CP14FishingRodComponent : Component
{
// Vars
/// <summary>
/// Link to a fishing float, attached to rod
/// </summary>
[AutoNetworkedField, ViewVariables]
public EntityUid? FishingFloat;
/// <summary>
/// Link to fishing rod user
/// </summary>
[AutoNetworkedField, ViewVariables]
public EntityUid? User;
/// <summary>
/// Link to caught fish
/// </summary>
[AutoNetworkedField, ViewVariables]
public EntityUid? CaughtFish;
/// <summary>
/// Link to a target fishing pond
/// </summary>
[AutoNetworkedField, ViewVariables]
public EntityUid? Target;
/// <summary>
/// Float position in minigame coordinates
/// </summary>
[AutoNetworkedField, ViewVariables]
public float FloatPosition = 0f;
/// <summary>
/// Time when fish will be caught
/// </summary>
[AutoNetworkedField, AutoPausedField, ViewVariables]
public TimeSpan FishingTime = TimeSpan.Zero;
/// <summary>
/// Does the user pull the fishing line
/// </summary>
[AutoNetworkedField, ViewVariables]
public bool Reeling;
/// <summary>
/// Is fish hooked
/// </summary>
[AutoNetworkedField, ViewVariables]
public bool FishHooked;
// Data definitions
/// <summary>
/// Fishing float prototype
/// </summary>
[DataField]
public EntProtoId FloatPrototype = "CP14DefaultFishingFloat";
/// <summary>
/// Fishing minigame prototype
/// </summary>
[DataField]
public ProtoId<CP14FishingMinigamePrototype> FishingMinigame = "Default";
/// <summary>
/// Speed of a float in minigame coordinates
/// </summary>
[DataField]
public float FloatSpeed = 2f;
/// <summary>
/// Max distance between rod and float
/// </summary>
[DataField]
public float MaxFishingDistance = 5f;
/// <summary>
/// Power with which float will be thrown
/// </summary>
[DataField]
public float ThrowPower = 10f;
/// <summary>
/// Minimal time before fish will be caught
/// </summary>
[DataField]
public double MinAwaitTime = 5;
/// <summary>
/// Maximum time before fish will be caught
/// </summary>
[DataField]
public double MaxAwaitTime = 20;
}

View File

@@ -9,5 +9,6 @@ namespace Content.Shared._CP14.Input
public static readonly BoundKeyFunction OpenBelt2 = "OpenBelt2";
public static readonly BoundKeyFunction SmartEquipBelt2 = "SmartEquipBelt2";
public static readonly BoundKeyFunction CP14OpenSkillMenu = "CP14OpenSkillMenu";
public static readonly BoundKeyFunction CP14FishingAction = "CP14FishingAction";
}
}
}

View File

@@ -0,0 +1 @@
fishing-paused-map-name = Fish map

View File

@@ -0,0 +1 @@
fishing-paused-map-name = Рыбная карта

View File

@@ -96,4 +96,6 @@
- type: Tag
tags:
- FootstepSound
- CP14Mosquito
- CP14Mosquito
- type: CP14Fish
fishBehavior: !type:CP14FishDartBehaviour

View File

@@ -0,0 +1,61 @@
- type: entity
parent: BaseItem
id: CP14FishingRod
name: fishing rod
description: Wooden stick with string attached.
categories: [ ForkFiltered ]
components:
- type: Item
shape:
- 0,0,0,1
- type: Sprite
sprite: _CP14/Objects/Tools/fishing_rod.rsi
state: icon
- type: Damageable
damageContainer: Inorganic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 50
behaviors:
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- !type:CP14ModularDisassembleBehavior
- !type:DoActsBehavior
acts: ["Destruction"]
- type: MeleeWeapon
angle: 45
attackRate: 1
wideAnimationRotation: 135
wideAnimation: CP14WeaponArcSlash
damage:
types:
Blunt: 0.1
soundHit:
collection: MetalThud
cPAnimationLength: 0.25
- type: Clothing
equipDelay: 0.25
unequipDelay: 0.25
quickEquip: false
breakOnMove: false
slots:
- neck
- type: CP14FishingRod
- type: UserInterface
interfaces:
enum.CP14FishingUiKey.Key:
type: CP14FishingBoundUserInterface
- type: entity
parent: BaseItem
id: CP14DefaultFishingFloat
name: Fishing float
description: Little floating ball.
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Objects/Tools/float.rsi
state: float

View File

@@ -71,6 +71,8 @@
- type: TileEntityEffect
effects:
- !type:ExtinguishReaction
- type: CP14FishingPond
lootTable: CP14WaterFishingLootTable
- type: entity
parent: CP14FloorWaterOptimized

View File

@@ -0,0 +1,5 @@
- type: entityTable
id: CP14WaterFishingLootTable
table: !type:GroupSelector
children:
- id: CP14MobMonsterFlem

View File

@@ -0,0 +1,19 @@
- type: CP14FishingMinigameStyle
id: Default
background:
texture: /Textures/_CP14/Interface/Fishing/Default/background.png
size: 78, 298
offset: 0, 0
fishIcon:
texture: /Textures/_CP14/Interface/Fishing/Default/fish_icon.png
size: 22, 18
offset: 26, 12
progressbar:
texture: /Textures/_CP14/Interface/Fishing/Default/progressbar.png
size: 4, 284
offset: 6, 8
float:
texture: /Textures/_CP14/Interface/Fishing/Default/float.png
size: 22, 76
offset: 26, 12
fishingMinigameSize: 281

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

View File

@@ -0,0 +1,22 @@
{
"version": 1,
"size": {
"x": 48,
"y": 48
},
"license": "CC-BY-SA-4.0",
"copyright": "Created by omsoyk (Discord)",
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

View File

@@ -0,0 +1,25 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-4.0",
"copyright": "Created by omsoyk (Discord)",
"states": [
{
"name": "float"
},
{
"name": "animation",
"delays": [
[
0.1,
0.1,
0.1,
0.1
]
]
}
]
}