From ff01033eaa6eba68f928f2669c4c951fa5ee18b2 Mon Sep 17 00:00:00 2001 From: Apachito Date: Tue, 18 Nov 2025 15:06:59 -0600 Subject: [PATCH] Port Fishing from Goob (#4615) * FISHING REAL * Update fishingspots.yml * Delete fishingspots.yml * Create fishingspots.yml Removed the component that was causing errors * Update Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Structures/Furniture/toilet.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Tiles/lava.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Tiles/liquid_plasma.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Tiles/liquid_plasma.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Tiles/water.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/_Goobstation/Recipes/Construction/Graphs/misc/makeshift_rod.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/Entities/Tiles/water.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/_Goobstation/Actions/fishing.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Update Resources/Prototypes/_Goobstation/Catalog/Fills/Crates/service.yml Co-authored-by: Tobias Berger Signed-off-by: Apachito * Changed the name spaces * Update SharedFishingSystem.cs * Revert "Update SharedFishingSystem.cs" This reverts commit 78451cd5c21ce4eabe11ffe99ea30b24d7b772fe. * Update FishingOverlay.cs * Update FishingOverlay.cs * Revert "Changed the name spaces" This reverts commit 50bac6c4beae0938d49411aac5470ffd6b19f9e3. * Revert "Changed the name spaces" * Fixed name spaces and the sprite system * Update makeshift_rod.yml * Update makeshift_rod.yml * Update Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs Signed-off-by: Tobias Berger * Update Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs Signed-off-by: Tobias Berger * Fixed Name Spaced Changed the names spaces and did the ones I forgot to do initially. * Changed name spaces again * Fixed name spaces real * Update SharedFishingSystem.cs * Update SharedFishingSystem.cs * Update SharedFishingSystem.cs * Update FishingSystem.cs * Update FishingSystem.cs --------- Signed-off-by: Apachito Signed-off-by: Tobias Berger Co-authored-by: Tobias Berger --- .../_Goobstation/Fishing/FishingSystem.cs | 42 ++ .../Fishing/Overlays/FishingOverlay.cs | 137 ++++++ .../_Goobstation/Fishing/FishingSystem.cs | 168 ++++++++ .../Components/ActiveFisherComponent.cs | 28 ++ .../Components/ActiveFishingSpotComponent.cs | 37 ++ .../Fishing/Components/FishComponent.cs | 18 + .../Components/FishingLureComponent.cs | 25 ++ .../Fishing/Components/FishingRodComponent.cs | 71 ++++ .../Components/FishingSpotComponent.cs | 30 ++ .../Fishing/Events/FishingEvents.cs | 31 ++ .../Fishing/Systems/SharedFishingSystem.cs | 390 ++++++++++++++++++ .../Items/Fishing/attributions.yml | 14 + .../Items/Fishing/fishing_rod_cast.ogg | Bin 0 -> 11314 bytes .../Items/Fishing/fishing_rod_reel.ogg | Bin 0 -> 29125 bytes .../en-US/_Goobstation/fishing/fishing.ftl | 5 + .../Entities/Structures/Furniture/toilet.yml | 19 + Resources/Prototypes/Entities/Tiles/lava.yml | 7 + .../Entities/Tiles/liquid_plasma.yml | 19 + Resources/Prototypes/Entities/Tiles/water.yml | 19 + .../_Goobstation/Actions/fishing.yml | 41 ++ .../Catalog/Cargo/cargo_service.yml | 14 + .../Catalog/Fills/Crates/service.yml | 17 + .../Objects/Specific/Fishing/fish.yml | 294 +++++++++++++ .../Objects/Specific/Fishing/fishingspots.yml | 49 +++ .../Objects/Specific/Fishing/loot_tables.yml | 109 +++++ .../Entities/Objects/Tools/fishing_rods.yml | 117 ++++++ .../Graphs/misc/makeshift_rod.yml | 21 + .../Recipes/Construction/misc.yml | 12 + .../Interface/Misc/fish_bar.rsi/icon.png | Bin 0 -> 191 bytes .../Interface/Misc/fish_bar.rsi/meta.json | 14 + .../Specific/Fishing/fish.rsi/alien.png | Bin 0 -> 676 bytes .../Specific/Fishing/fish.rsi/angler.png | Bin 0 -> 1057 bytes .../Specific/Fishing/fish.rsi/bass.png | Bin 0 -> 783 bytes .../Specific/Fishing/fish.rsi/bingle.png | Bin 0 -> 813 bytes .../Specific/Fishing/fish.rsi/blob.png | Bin 0 -> 473 bytes .../Fishing/fish.rsi/blue_fintuna.png | Bin 0 -> 660 bytes .../Specific/Fishing/fish.rsi/boy_fisher.png | Bin 0 -> 638 bytes .../Specific/Fishing/fish.rsi/catfish.png | Bin 0 -> 687 bytes .../Specific/Fishing/fish.rsi/clown.png | Bin 0 -> 748 bytes .../Specific/Fishing/fish.rsi/disk.png | Bin 0 -> 511 bytes .../Objects/Specific/Fishing/fish.rsi/gib.png | Bin 0 -> 575 bytes .../Objects/Specific/Fishing/fish.rsi/ian.png | Bin 0 -> 573 bytes .../Specific/Fishing/fish.rsi/meta.json | 95 +++++ .../Specific/Fishing/fish.rsi/mutant.png | Bin 0 -> 1144 bytes .../Specific/Fishing/fish.rsi/nuker.png | Bin 0 -> 1083 bytes .../Specific/Fishing/fish.rsi/pufferfish.png | Bin 0 -> 1250 bytes .../Specific/Fishing/fish.rsi/silverfish.png | Bin 0 -> 845 bytes .../Fishing/fish.rsi/sun_tropical.png | Bin 0 -> 1037 bytes .../Specific/Fishing/fish.rsi/toxic.png | Bin 0 -> 694 bytes .../Specific/Fishing/fish.rsi/tropical.png | Bin 0 -> 517 bytes .../Fishing/fishing_lure.rsi/icon.png | Bin 0 -> 217 bytes .../Fishing/fishing_lure.rsi/meta.json | 17 + .../Fishing/fishing_lure.rsi/rope.png | Bin 0 -> 135 bytes .../Fishing/fishing_rod.rsi/icon-active.png | Bin 0 -> 736 bytes .../Specific/Fishing/fishing_rod.rsi/icon.png | Bin 0 -> 623 bytes .../fishing_rod.rsi/inhand-left-active.png | Bin 0 -> 288 bytes .../Fishing/fishing_rod.rsi/inhand-left.png | Bin 0 -> 429 bytes .../fishing_rod.rsi/inhand-right-active.png | Bin 0 -> 287 bytes .../Fishing/fishing_rod.rsi/inhand-right.png | Bin 0 -> 435 bytes .../Fishing/fishing_rod.rsi/meta.json | 33 ++ .../Fishing/fishing_spot.rsi/meta.json | 21 + .../Fishing/fishing_spot.rsi/water.png | Bin 0 -> 2341 bytes .../Fishing/golden_rod.rsi/icon-active.png | Bin 0 -> 517 bytes .../Specific/Fishing/golden_rod.rsi/icon.png | Bin 0 -> 463 bytes .../golden_rod.rsi/inhand-left-active.png | Bin 0 -> 243 bytes .../Fishing/golden_rod.rsi/inhand-left.png | Bin 0 -> 312 bytes .../golden_rod.rsi/inhand-right-active.png | Bin 0 -> 246 bytes .../Fishing/golden_rod.rsi/inhand-right.png | Bin 0 -> 317 bytes .../Specific/Fishing/golden_rod.rsi/meta.json | 33 ++ .../Fishing/goon_rod.rsi/icon-active.png | Bin 0 -> 591 bytes .../Specific/Fishing/goon_rod.rsi/icon.png | Bin 0 -> 559 bytes .../goon_rod.rsi/inhand-left-active.png | Bin 0 -> 288 bytes .../Fishing/goon_rod.rsi/inhand-left.png | Bin 0 -> 429 bytes .../goon_rod.rsi/inhand-right-active.png | Bin 0 -> 287 bytes .../Fishing/goon_rod.rsi/inhand-right.png | Bin 0 -> 435 bytes .../Specific/Fishing/goon_rod.rsi/meta.json | 33 ++ .../Fishing/makeshift_lure.rsi/icon.png | Bin 0 -> 218 bytes .../Fishing/makeshift_lure.rsi/meta.json | 17 + .../Fishing/makeshift_lure.rsi/rope.png | Bin 0 -> 146 bytes .../Fishing/makeshift_rod.rsi/icon-active.png | Bin 0 -> 430 bytes .../Fishing/makeshift_rod.rsi/icon.png | Bin 0 -> 332 bytes .../makeshift_rod.rsi/inhand-left-active.png | Bin 0 -> 240 bytes .../Fishing/makeshift_rod.rsi/inhand-left.png | Bin 0 -> 306 bytes .../makeshift_rod.rsi/inhand-right-active.png | Bin 0 -> 241 bytes .../makeshift_rod.rsi/inhand-right.png | Bin 0 -> 309 bytes .../Fishing/makeshift_rod.rsi/meta.json | 33 ++ 86 files changed, 2030 insertions(+) create mode 100644 Content.Client/_Goobstation/Fishing/FishingSystem.cs create mode 100644 Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs create mode 100644 Content.Server/_Goobstation/Fishing/FishingSystem.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/ActiveFisherComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/ActiveFishingSpotComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/FishComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/FishingLureComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/FishingRodComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Components/FishingSpotComponent.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Events/FishingEvents.cs create mode 100644 Content.Shared/_Goobstation/Fishing/Systems/SharedFishingSystem.cs create mode 100644 Resources/Audio/_Goobstation/Items/Fishing/attributions.yml create mode 100644 Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_cast.ogg create mode 100644 Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_reel.ogg create mode 100644 Resources/Locale/en-US/_Goobstation/fishing/fishing.ftl create mode 100644 Resources/Prototypes/_Goobstation/Actions/fishing.yml create mode 100644 Resources/Prototypes/_Goobstation/Catalog/Cargo/cargo_service.yml create mode 100644 Resources/Prototypes/_Goobstation/Catalog/Fills/Crates/service.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fish.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fishingspots.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/loot_tables.yml create mode 100644 Resources/Prototypes/_Goobstation/Entities/Objects/Tools/fishing_rods.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Construction/Graphs/misc/makeshift_rod.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Construction/misc.yml create mode 100644 Resources/Textures/_Goobstation/Interface/Misc/fish_bar.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Interface/Misc/fish_bar.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/alien.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/angler.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/bass.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/bingle.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/blob.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/blue_fintuna.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/boy_fisher.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/catfish.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/clown.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/disk.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/gib.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/ian.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/mutant.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/nuker.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/pufferfish.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/silverfish.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/sun_tropical.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/toxic.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/tropical.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi/rope.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/icon-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-left-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-left.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-right-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-right.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/water.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/icon-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-left-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-left.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-right-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-right.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-left-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-left.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-right-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-right.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/meta.json create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/rope.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-left-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-left.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right-active.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right.png create mode 100644 Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/meta.json diff --git a/Content.Client/_Goobstation/Fishing/FishingSystem.cs b/Content.Client/_Goobstation/Fishing/FishingSystem.cs new file mode 100644 index 0000000000..ba6a840f06 --- /dev/null +++ b/Content.Client/_Goobstation/Fishing/FishingSystem.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client._Goobstation.Fishing.Overlays; +using Content.Shared._Goobstation.Fishing.Components; +using Content.Shared._Goobstation.Fishing.Systems; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Client._Goobstation.Fishing; + +public sealed class FishingSystem : SharedFishingSystem +{ + [Dependency] private readonly IOverlayManager _overlay = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + public override void Initialize() + { + base.Initialize(); + _overlay.AddOverlay(new FishingOverlay(EntityManager, _player)); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlay.RemoveOverlay(); + } + + // Does nothing on client, because can't spawn entities in prediction + protected override void SetupFishingFloat(Entity fishingRod, EntityUid player, EntityCoordinates target) {} + + // Does nothing on client, because can't delete entities in prediction + protected override void ThrowFishReward(EntProtoId fishId, EntityUid fishSpot, EntityUid target) {} + + // Does nothing on client, because NUKE ALL PREDICTION!!!! (UseInHands event sometimes gets declined on Server side, and it desyncs, so we can't predict that sadly. + protected override void CalculateFightingTimings(Entity fisher, ActiveFishingSpotComponent activeSpotComp) {} +} diff --git a/Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs b/Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs new file mode 100644 index 0000000000..55b25d6027 --- /dev/null +++ b/Content.Client/_Goobstation/Fishing/Overlays/FishingOverlay.cs @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using System.Numerics; +using Content.Client.UserInterface.Systems; +using Content.Shared._Goobstation.Fishing.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Client.Player; +using Robust.Shared.Utility; + +namespace Content.Client._Goobstation.Fishing.Overlays; + +public sealed class FishingOverlay : Overlay +{ + private readonly IEntityManager _entManager; + private readonly IPlayerManager _player; + private readonly SharedTransformSystem _transform; + private readonly ProgressColorSystem _progressColor; + private readonly SpriteSystem _sprite; + private readonly Texture _barTexture; + + // Fractional positions for progress bar fill (relative to texture height/width) + private const float StartYFraction = 0.09375f; // 3/32 + private const float EndYFraction = 0.90625f; // 29/32 + private const float BarWidthFraction = 0.2f; // 2/10 + + // Apply a custom scale factor to reduce the size of the progress bar + // We dont want to do this because muh pixel consistency, but i'll keep it here as an option + private const float BarScale = 1f; + + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; + + public FishingOverlay(IEntityManager entManager, IPlayerManager player) + { + _entManager = entManager; + _player = player; + _transform = _entManager.EntitySysManager.GetEntitySystem(); + _progressColor = _entManager.System(); + _sprite = _entManager.System(); + // Load the progress bar texture + var sprite = new SpriteSpecifier.Rsi(new("/Textures/_Goobstation/Interface/Misc/fish_bar.rsi"), "icon"); + _barTexture = _entManager.EntitySysManager.GetEntitySystem().Frame0(sprite); + } + + protected override void Draw(in OverlayDrawArgs args) + { + var handle = args.WorldHandle; + var rotation = args.Viewport.Eye?.Rotation ?? Angle.Zero; + var xformQuery = _entManager.GetEntityQuery(); + + const float scale = 1f; + var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale)); + var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation); + + // Define bounds for culling entities outside the viewport + var bounds = args.WorldAABB.Enlarged(5f); + var localEnt = _player.LocalSession?.AttachedEntity; + + // Calculate the size of the texture in world units + var textureSize = new Vector2(_barTexture.Width, _barTexture.Height) / EyeManager.PixelsPerMeter; + + var scaledTextureSize = textureSize * BarScale; + + // Define the progress bar's width as a fraction of the texture width + var barWidth = scaledTextureSize.X * BarWidthFraction; + + // Iterate through all entities with ActiveFisherComponent + var enumerator = _entManager.AllEntityQueryEnumerator(); + while (enumerator.MoveNext(out var uid, out var comp, out var sprite, out var xform)) + { + // Skip if the entity is not on the current map, has invalid progress, or is not the local player + if (xform.MapID != args.MapId || + comp.TotalProgress == null || + comp.TotalProgress < 0 || + uid != localEnt) + continue; + + // Get the world position of the entity + var worldPosition = _transform.GetWorldPosition(xform, xformQuery); + if (!bounds.Contains(worldPosition)) + continue; + + // Set up the transformation matrix for rendering + var worldMatrix = Matrix3Helpers.CreateTranslation(worldPosition); + var scaledWorld = Matrix3x2.Multiply(scaleMatrix, worldMatrix); + var matty = Matrix3x2.Multiply(rotationMatrix, scaledWorld); + handle.SetTransform(matty); + + // Calculate the position of the progress bar relative to the entity + var position = new Vector2( + _sprite.GetLocalBounds((uid, sprite)).Width / 2f, + -scaledTextureSize.Y / 2f // Center vertically + ); + + // Draw the background texture at the scaled size + handle.DrawTextureRect(_barTexture, new Box2(position, position + scaledTextureSize)); + + // Calculate progress and clamp it to [0, 1] + var progress = Math.Clamp(comp.TotalProgress.Value, 0f, 1f); + + // Calculate the fill height based on progress + var startYPixel = scaledTextureSize.Y * StartYFraction; + var endYPixel = scaledTextureSize.Y * EndYFraction; + var yProgress = (endYPixel - startYPixel) * progress + startYPixel; + + // Define the fill box with the correct width and height + var box = new Box2( + new Vector2((scaledTextureSize.X - barWidth) / 2f, startYPixel), + new Vector2((scaledTextureSize.X + barWidth) / 2f, yProgress) + ); + + // Translate the box to the correct position + box = box.Translated(position); + + // Draw the progress fill + var color = GetProgressColor(progress); + handle.DrawRect(box, color); + } + + // Reset the shader and transform + handle.UseShader(null); + handle.SetTransform(Matrix3x2.Identity); + } + + /// + /// Gets the color for the progress bar based on the progress value. + /// + public Color GetProgressColor(float progress, float alpha = 1f) + { + return _progressColor.GetProgressColor(progress).WithAlpha(alpha); + } +} diff --git a/Content.Server/_Goobstation/Fishing/FishingSystem.cs b/Content.Server/_Goobstation/Fishing/FishingSystem.cs new file mode 100644 index 0000000000..666aacf751 --- /dev/null +++ b/Content.Server/_Goobstation/Fishing/FishingSystem.cs @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Rouden <149893554+Roudenn@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using System.Linq; +using System.Numerics; +using Content.Shared._Goobstation.Fishing.Components; +using Content.Shared._Goobstation.Fishing.Events; +using Content.Shared._Goobstation.Fishing.Systems; +using Content.Shared.EntityTable; +using Content.Shared.Interaction.Events; +using Content.Shared.Item; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Events; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._Goobstation.Fishing; + +public sealed class FishingSystem : SharedFishingSystem +{ + // Here we calculate the start of fishing, because apparently StartCollideEvent + // works janky on clientside so we can't predict when fishing starts. + [Dependency] private readonly IComponentFactory _compFactory = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnFloatCollide); + SubscribeLocalEvent(OnFishingInteract); + } + + #region Event handling + + private void OnFloatCollide(Entity ent, ref StartCollideEvent args) + { + // TODO: make it so this can collide with any unacnchored objects (items, mobs, etc) but not the player casting it (get parent of rod?) + // Fishing spot logic + var attachedEnt = args.OtherEntity; + + if (HasComp(attachedEnt)) + return; + + if (!FishSpotQuery.TryComp(attachedEnt, out var spotComp)) + { + if (args.OtherBody.BodyType == BodyType.Static) + return; + + Anchor(ent, attachedEnt); + return; + } + + // Anchor fishing float on an entity + Anchor(ent, attachedEnt); + + // Currently we don't support multiple loots from this + var fish = spotComp.FishList.GetSpawns(_random.GetRandom(), EntityManager, _proto, new EntityTableContext()).First(); + + // Get fish difficulty + _proto.Index(fish).TryGetComponent(out FishComponent? fishComp, _compFactory); + + // Assign things that depend on the fish + var activeFishSpot = EnsureComp(attachedEnt); + activeFishSpot.Fish = fish; + activeFishSpot.FishDifficulty = fishComp?.FishDifficulty ?? FishComponent.DefaultDifficulty; + + // Assign things that depend on the spot + var time = spotComp.FishDefaultTimer + _random.NextFloat(-spotComp.FishTimerVariety, spotComp.FishTimerVariety); + activeFishSpot.FishingStartTime = Timing.CurTime + TimeSpan.FromSeconds(time); + activeFishSpot.AttachedFishingLure = ent; + + // Declares war on prediction + Dirty(attachedEnt, activeFishSpot); + Dirty(ent); + } + + private void OnFishingInteract(EntityUid uid, FishingRodComponent component, UseInHandEvent args) + { + if (!FisherQuery.TryComp(args.User, out var fisherComp) || fisherComp.TotalProgress == null || args.Handled || !Timing.IsFirstTimePredicted) + return; + + fisherComp.TotalProgress += fisherComp.ProgressPerUse * component.Efficiency; + Dirty(args.User, fisherComp); // That's a bit evil, but we want to keep numbers real. + + args.Handled = true; + } + + private void Anchor(Entity ent, EntityUid attachedEnt) + { + var spotPosition = Xform.GetWorldPosition(attachedEnt); + Xform.SetWorldPosition(ent, spotPosition); + Xform.SetParent(ent, attachedEnt); + _physics.SetLinearVelocity(ent, Vector2.Zero); + _physics.SetAngularVelocity(ent, 0f); + ent.Comp.AttachedEntity = attachedEnt; + RemComp(ent); + RemComp(ent); + } + + #endregion + + protected override void SetupFishingFloat(Entity fishingRod, EntityUid player, EntityCoordinates target) + { + var (uid, component) = fishingRod; + var targetCoords = Xform.ToMapCoordinates(target); + var playerCoords = Xform.GetMapCoordinates(Transform(player)); + + var fishFloat = Spawn(component.FloatPrototype, playerCoords); + component.FishingLure = fishFloat; + Dirty(uid, component); + + // Calculate throw direction + var direction = targetCoords.Position - playerCoords.Position; + if (direction == Vector2.Zero) + direction = Vector2.UnitX; // If the user somehow manages to click directly in the center of themself, just toss it to the right i guess. + + // Yeet + Throwing.TryThrow(fishFloat, direction, 15f, player, 2f, null, true); + + // Set up lure component + var fishLureComp = EnsureComp(fishFloat); + fishLureComp.FishingRod = uid; + Dirty(fishFloat, fishLureComp); + + // Rope visuals + var visuals = EnsureComp(fishFloat); + visuals.Sprite = component.RopeSprite; + visuals.OffsetA = component.RopeLureOffset; + visuals.OffsetB = component.RopeUserOffset; + visuals.Target = GetNetEntity(uid); + } + + protected override void ThrowFishReward(EntProtoId fishId, EntityUid fishSpot, EntityUid target) + { + var position = Transform(fishSpot).Coordinates; + var fish = Spawn(fishId, position); + // Throw da fish back to the player because it looks funny + var direction = Xform.GetWorldPosition(target) - Xform.GetWorldPosition(fish); + var length = direction.Length(); + var distance = Math.Clamp(length, 0.5f, 15f); + direction *= distance / length; + + Throwing.TryThrow(fish, direction, 7f); + } + + protected override void CalculateFightingTimings(Entity fisher, ActiveFishingSpotComponent activeSpotComp) + { + if (Timing.CurTime < fisher.Comp.NextStruggle) + return; + + fisher.Comp.NextStruggle = Timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(0.06f, 0.18f)); + fisher.Comp.TotalProgress -= activeSpotComp.FishDifficulty; + Dirty(fisher); + } +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/ActiveFisherComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/ActiveFisherComponent.cs new file mode 100644 index 0000000000..422921cc72 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/ActiveFisherComponent.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.GameStates; + +namespace Content.Shared._Goobstation.Fishing.Components; + +/// +/// Applied to players that are pulling fish out from water +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveFisherComponent : Component +{ + [DataField, AutoNetworkedField] + public TimeSpan? NextStruggle; + + [DataField, AutoNetworkedField] + public float? TotalProgress; + + [DataField, AutoNetworkedField] + public float ProgressPerUse = 0.05f; + + [DataField, AutoNetworkedField] + public EntityUid FishingRod; +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/ActiveFishingSpotComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/ActiveFishingSpotComponent.cs new file mode 100644 index 0000000000..7adb83e4f7 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/ActiveFishingSpotComponent.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Goobstation.Fishing.Components; + +/// +/// Dynamic component, that is assigned to active fishing spots that are currently waiting for da fish. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ActiveFishingSpotComponent : Component +{ + [ViewVariables, AutoNetworkedField] + public EntityUid? AttachedFishingLure; + + [DataField, AutoNetworkedField] + public TimeSpan? FishingStartTime; + + /// + /// If true, someone is pulling fish out of this spot. + /// + [DataField, AutoNetworkedField] + public bool IsActive; + + [DataField, AutoNetworkedField] + public float FishDifficulty; + + /// + /// Fish that we're currently trying to catch + /// + [DataField] + public EntProtoId? Fish; // not networked because useless for client +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/FishComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/FishComponent.cs new file mode 100644 index 0000000000..ddc59c5b74 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/FishComponent.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +namespace Content.Shared._Goobstation.Fishing.Components; + +/// +/// The fish itself! +/// +[RegisterComponent] +public sealed partial class FishComponent : Component +{ + public const float DefaultDifficulty = 0.021f; + + [DataField("difficulty")] + public float FishDifficulty = DefaultDifficulty; +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/FishingLureComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/FishingLureComponent.cs new file mode 100644 index 0000000000..9d8ea77ca0 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/FishingLureComponent.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.GameStates; + +namespace Content.Shared._Goobstation.Fishing.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class FishingLureComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid FishingRod; + + [DataField, AutoNetworkedField] + public EntityUid? AttachedEntity; + + [ViewVariables] + public TimeSpan NextUpdate; + + [DataField] + public float UpdateInterval = 1f; +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/FishingRodComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/FishingRodComponent.cs new file mode 100644 index 0000000000..94753c3d64 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/FishingRodComponent.cs @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Rouden <149893554+Roudenn@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Utility; +using System.Numerics; + +namespace Content.Shared._Goobstation.Fishing.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class FishingRodComponent : Component +{ + /// + /// Higher value will make every interact more productive. + /// + [DataField] + public float Efficiency = 1f; + + /// + /// At what progress fishing starts. + /// + [DataField] + public float StartingProgress = 0.33f; + + /// + /// How many seconds we wait until fish starts to fight with us + /// + [DataField] + public float StartingStruggleTime = 0.3f; + + /// + /// If lure moves bigger than this distance away from the rod, + /// it will force it to reel instantly. + /// + [DataField] + public float BreakOnDistance = 8f; + + [DataField] + public EntProtoId FloatPrototype = "FishingLure"; + + [DataField] + public SpriteSpecifier RopeSprite = + new SpriteSpecifier.Rsi(new ResPath("_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi"), "rope"); + + [DataField, ViewVariables] + public Vector2 RopeUserOffset = new (0f, 0f); + + [DataField, ViewVariables] + public Vector2 RopeLureOffset = new (0f, 0f); + + [DataField, AutoNetworkedField] + public EntityUid? FishingLure; + + [DataField] + public EntProtoId ThrowLureActionId = "ActionStartFishing"; + + [DataField, AutoNetworkedField] + public EntityUid? ThrowLureActionEntity; + + [DataField] + public EntProtoId PullLureActionId = "ActionStopFishing"; + + [DataField, AutoNetworkedField] + public EntityUid? PullLureActionEntity; +} diff --git a/Content.Shared/_Goobstation/Fishing/Components/FishingSpotComponent.cs b/Content.Shared/_Goobstation/Fishing/Components/FishingSpotComponent.cs new file mode 100644 index 0000000000..fad67292eb --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Components/FishingSpotComponent.cs @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared.EntityTable.EntitySelectors; + +namespace Content.Shared._Goobstation.Fishing.Components; + +[RegisterComponent] +public sealed partial class FishingSpotComponent : Component +{ + /// + /// All possible fishes to catch here + /// + [DataField(required: true)] + public EntityTableSelector FishList; + + /// + /// Default time for fish to occur + /// + [DataField] + public float FishDefaultTimer; + + /// + /// Variety number that FishDefaultTimer can go up or down to randomly + /// + [DataField] + public float FishTimerVariety; +} diff --git a/Content.Shared/_Goobstation/Fishing/Events/FishingEvents.cs b/Content.Shared/_Goobstation/Fishing/Events/FishingEvents.cs new file mode 100644 index 0000000000..a7b9a43647 --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Events/FishingEvents.cs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared.Actions; +using Robust.Shared.Serialization; + +namespace Content.Shared._Goobstation.Fishing.Events; + +public sealed partial class ThrowFishingLureActionEvent : WorldTargetActionEvent; + +public sealed partial class PullFishingLureActionEvent : InstantActionEvent; + +[Serializable, NetSerializable] +public sealed class ActiveFishingSpotComponentState : ComponentState +{ + public readonly float FishDifficulty; + public bool IsActive; + public TimeSpan? FishingStartTime; + public NetEntity? AttachedFishingLure; + + public ActiveFishingSpotComponentState(float fishDifficulty, bool isActive, TimeSpan? fishingStartTime, NetEntity? attachedFishingLure) + { + FishDifficulty = fishDifficulty; + IsActive = isActive; + FishingStartTime = fishingStartTime; + AttachedFishingLure = attachedFishingLure; + } +} diff --git a/Content.Shared/_Goobstation/Fishing/Systems/SharedFishingSystem.cs b/Content.Shared/_Goobstation/Fishing/Systems/SharedFishingSystem.cs new file mode 100644 index 0000000000..3a2c62a7fa --- /dev/null +++ b/Content.Shared/_Goobstation/Fishing/Systems/SharedFishingSystem.cs @@ -0,0 +1,390 @@ +// SPDX-FileCopyrightText: 2025 Aidenkrz +// SPDX-FileCopyrightText: 2025 Aviu00 +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Rouden <149893554+Roudenn@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Roudenn +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Fishing.Components; +using Content.Shared._Goobstation.Fishing.Events; +using Content.Shared.Actions; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Robust.Shared.Map; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Shared._Goobstation.Fishing.Systems; + +/// +/// This handles... da fish +/// +public abstract class SharedFishingSystem : EntitySystem +{ + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] protected readonly INetManager Net = default!; + [Dependency] protected readonly ThrowingSystem Throwing = default!; + [Dependency] protected readonly SharedTransformSystem Xform = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + + protected EntityQuery FisherQuery; + protected EntityQuery ActiveFishSpotQuery; + protected EntityQuery FishSpotQuery; + protected EntityQuery FishRodQuery; + protected EntityQuery FishLureQuery; + + public override void Initialize() + { + base.Initialize(); + + FisherQuery = GetEntityQuery(); + ActiveFishSpotQuery = GetEntityQuery(); + FishSpotQuery = GetEntityQuery(); + FishRodQuery = GetEntityQuery(); + FishLureQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnFishingRodInit); + SubscribeLocalEvent(OnGetActions); + SubscribeLocalEvent(OnThrowFloat); + SubscribeLocalEvent(OnPullFloat); + SubscribeLocalEvent(OnRodParentChanged); + + SubscribeLocalEvent(OnRodTerminating); + SubscribeLocalEvent(OnLureTerminating); + SubscribeLocalEvent(OnSpotTerminating); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + UpdateFishing(); + } + + private void UpdateFishing() + { + if (!Timing.IsFirstTimePredicted) + return; + + var currentTime = Timing.CurTime; + var activeFishers = EntityQueryEnumerator(); + while (activeFishers.MoveNext(out var fisher, out var fisherComp)) + { + // Get fishing rod, then float, then spot... ReCurse. + if (TerminatingOrDeleted(fisherComp.FishingRod) || + !FishRodQuery.TryComp(fisherComp.FishingRod, out var fishingRodComp) || + TerminatingOrDeleted(fishingRodComp.FishingLure) || + !FishLureQuery.TryComp(fishingRodComp.FishingLure, out var fishingFloatComp) || + TerminatingOrDeleted(fishingFloatComp.AttachedEntity) || + !ActiveFishSpotQuery.TryComp(fishingFloatComp.AttachedEntity, out var activeSpotComp)) + continue; + + var fishRod = fisherComp.FishingRod; + var fishSpot = fishingFloatComp.AttachedEntity.Value; + + fisherComp.TotalProgress ??= fishingRodComp.StartingProgress; + fisherComp.NextStruggle ??= Timing.CurTime + TimeSpan.FromSeconds(fishingRodComp.StartingStruggleTime); + + // Fish fighting logic + CalculateFightingTimings((fisher, fisherComp), activeSpotComp); + + switch (fisherComp.TotalProgress) + { + case < 0f: + // It's over + _popup.PopupEntity(Loc.GetString("fishing-progress-fail"), fisher, fisher); + StopFishing((fishRod, fishingRodComp), fisher); + continue; + + case >= 1f: + if (activeSpotComp.Fish != null) + { + ThrowFishReward(activeSpotComp.Fish.Value, fishSpot, fisher); + _popup.PopupEntity(Loc.GetString("fishing-progress-success"), fisher, fisher); + } + + StopFishing((fishRod, fishingRodComp), fisher); + break; + } + } + + var fishingSpots = EntityQueryEnumerator(); + while (fishingSpots.MoveNext(out var activeSpotComp)) + { + if (currentTime < activeSpotComp.FishingStartTime || activeSpotComp.IsActive || activeSpotComp.FishingStartTime == null) + continue; + + // Trigger start of the fishing process + if (TerminatingOrDeleted(activeSpotComp.AttachedFishingLure)) + continue; + + // Get fishing lure, then rod, then player... ReCurse. + if (!FishLureQuery.TryComp(activeSpotComp.AttachedFishingLure, out var fishingFloatComp) || + TerminatingOrDeleted(fishingFloatComp.FishingRod) || + !FishRodQuery.TryComp(fishingFloatComp.FishingRod, out var fishRodComp)) + continue; + + var fishRod = fishingFloatComp.FishingRod; + + if (TerminatingOrDeleted(fishingFloatComp.FishingRod)) + continue; + + var fisher = Transform(fishingFloatComp.FishingRod).ParentUid; + + if (!Exists(fisher) || TerminatingOrDeleted(fisher)) + continue; + + var activeFisher = EnsureComp(fisher); + activeFisher.FishingRod = fishRod; + activeFisher.ProgressPerUse *= fishRodComp.Efficiency; + activeFisher.TotalProgress = fishRodComp.StartingProgress; + activeFisher.NextStruggle = Timing.CurTime + TimeSpan.FromSeconds(fishRodComp.StartingStruggleTime); // Compensate ping for 0.3 seconds + + // Predicted because it works like 99.9% of the time anyway. + _popup.PopupPredicted(Loc.GetString("fishing-progress-start"), fisher, fisher); + activeSpotComp.IsActive = true; + } + + var fishingLures = EntityQueryEnumerator(); + while (fishingLures.MoveNext(out var fishingLure, out var lureComp, out var xform)) + { + if (lureComp.NextUpdate > Timing.CurTime) + continue; + + lureComp.NextUpdate = Timing.CurTime + TimeSpan.FromSeconds(lureComp.UpdateInterval); + + if (TerminatingOrDeleted(lureComp.FishingRod) || + !FishRodQuery.TryComp(lureComp.FishingRod, out var fishingRodComp)) + continue; + + var lurePos = Xform.GetMapCoordinates(fishingLure, xform); + var rodPos = Xform.GetMapCoordinates(lureComp.FishingRod); + var distance = lurePos.Position - rodPos.Position; + var fisher = Transform(lureComp.FishingRod).ParentUid; + + if (!Exists(fisher) || TerminatingOrDeleted(fisher) || + distance.Length() > fishingRodComp.BreakOnDistance || + lurePos.MapId != rodPos.MapId || + !_hands.IsHolding(fisher, lureComp.FishingRod) || + !HasComp(fisher)) + { + var rod = (lureComp.FishingRod, fishingRodComp); + StopFishing(rod, fisher); + ToggleFishingActions(rod, fisher, false); + } + } + } + + /// + /// if AddPulling is true, we ADD Pulling action and REMOVE Throwing action. + /// Basically true if we start, and false if we end. + /// + private void ToggleFishingActions(Entity ent, EntityUid fisher, bool addPulling) + { + if (TerminatingOrDeleted(ent) || !Exists(fisher) || TerminatingOrDeleted(fisher)) + return; + + if (addPulling) + { + _actions.RemoveAction(ent.Comp.ThrowLureActionEntity); + _actions.AddAction(fisher, ref ent.Comp.PullLureActionEntity, ent.Comp.PullLureActionId, ent); + } + else + { + _actions.RemoveAction(ent.Comp.PullLureActionEntity); + _actions.AddAction(fisher, ref ent.Comp.ThrowLureActionEntity, ent.Comp.ThrowLureActionId, ent); + } + } + + protected abstract void CalculateFightingTimings(Entity fisher, ActiveFishingSpotComponent activeSpotComp); + + /// + /// Server-side only, sets up fishing float and throws it + /// + protected abstract void SetupFishingFloat(Entity fishingRod, EntityUid player, EntityCoordinates target); + + /// + /// Server-side only, spawns a fish and throws it to our player! + /// + protected abstract void ThrowFishReward(EntProtoId fishId, EntityUid fishSpot, EntityUid target); + + /// + /// Reels the fishing rod back and stops fishing progress if arguments are passed to it. + /// + private void StopFishing( + Entity fishingRod, + EntityUid? fisher) + { + var nullOrDeleted = + fishingRod.Comp.FishingLure == null || TerminatingOrDeleted(fishingRod.Comp.FishingLure.Value); + + if (!nullOrDeleted && FishLureQuery.TryComp(fishingRod.Comp.FishingLure, out var lureComp) && + !TerminatingOrDeleted(lureComp.AttachedEntity) && + ActiveFishSpotQuery.TryComp(lureComp.AttachedEntity, out var activeSpotComp)) + RemCompDeferred(lureComp.AttachedEntity.Value, activeSpotComp); + + if (!nullOrDeleted && Net.IsServer) + QueueDel(fishingRod.Comp.FishingLure); + + if (Exists(fisher) && !TerminatingOrDeleted(fisher) && FisherQuery.TryComp(fisher, out var fisherComp)) + { + RemCompDeferred(fisher.Value, fisherComp); + + ToggleFishingActions(fishingRod, fisher.Value, false); + } + + fishingRod.Comp.FishingLure = null; + } + + #region Terminating Events + + private void OnRodTerminating(Entity ent, ref EntityTerminatingEvent args) + { + TryStopFishing(ent); + } + + private void OnLureTerminating(Entity ent, ref EntityTerminatingEvent args) + { + TryStopFishing(ent); + } + + private void OnSpotTerminating(Entity ent, ref EntityTerminatingEvent args) + { + TryStopFishing(ent); + } + + #endregion + + #region Deletion Helpers + + /// + /// Stops fishing by taking only the Fishing rod as an argument. + /// + private void TryStopFishing(Entity rod) + { + var player = Transform(rod).ParentUid; + StopFishing(rod, player); + } + + /// + /// Stops fishing by taking only the Fishing lure as an argument. + /// + private void TryStopFishing(Entity lure) + { + if (!FishRodQuery.TryComp(lure.Comp.FishingRod, out var rodComp)) + return; + + TryStopFishing((lure.Comp.FishingRod, rodComp)); + } + + /// + /// Stops fishing by taking only the Active spot as an argument. + /// + private void TryStopFishing(Entity spot) + { + if (!FishLureQuery.TryComp(spot.Comp.AttachedFishingLure, out var lureComp)) + return; + + if (!FishRodQuery.TryComp(lureComp.FishingRod, out var rodComp)) + return; + + TryStopFishing((lureComp.FishingRod, rodComp)); + } + + #endregion + + #region Event Handling + + private void OnThrowFloat(Entity ent, ref ThrowFishingLureActionEvent args) + { + if (args.Handled || !Timing.IsFirstTimePredicted) + return; + + var player = args.Performer; + + if (ent.Comp.FishingLure != null || !Xform.IsValid(args.Target)) + { + args.Handled = true; + return; + } + + SetupFishingFloat(ent, player, args.Target); + ToggleFishingActions(ent, player, true); + args.Handled = true; + } + + private void OnPullFloat(Entity ent, ref PullFishingLureActionEvent args) + { + if (args.Handled || !Timing.IsFirstTimePredicted) + return; + + var player = args.Performer; + var (uid, component) = ent; + + if (component.FishingLure == null) + { + ToggleFishingActions(ent, player, true); + args.Handled = true; + return; + } + + _popup.PopupPredicted(Loc.GetString("fishing-rod-remove-lure", ("ent", Name(uid))), uid, uid); + + if (!FishLureQuery.TryComp(component.FishingLure, out var lureComp)) + return; + + if (lureComp.AttachedEntity != null && Exists(lureComp.AttachedEntity)) + { + // TODO: so this kinda just lets you pull anything right up to you, it should instead just apply an impulse in your direction modfiied by the weight of the player vs the object + // Also we need to autoreel/snap the line if the player gets too far away + // Also we should probably PVS override the lure if the rod is in PVS, and vice versa to stop the joint visuals from popping in/out + var attachedEnt = lureComp.AttachedEntity.Value; + var targetCoords = Xform.GetMapCoordinates(Transform(attachedEnt)); + var playerCoords = Xform.GetMapCoordinates(Transform(player)); + var rand = new System.Random((int) Timing.CurTick.Value); // evil random prediction hack + + // Calculate throw direction + var direction = (playerCoords.Position - targetCoords.Position) * rand.NextFloat(0.2f, 0.85f); + + // Yeet + Throwing.TryThrow(attachedEnt, direction, 4f, player); + } + + StopFishing(ent, player); + ToggleFishingActions(ent, player, false); + args.Handled = true; + } + + private void OnFishingRodInit(Entity ent, ref MapInitEvent args) + { + _actions.AddAction(ent, ref ent.Comp.ThrowLureActionEntity, ent.Comp.ThrowLureActionId); + } + + private void OnRodParentChanged(Entity ent, ref EntParentChangedMessage args) + { + if (TerminatingOrDeleted(ent) || !Exists(args.Transform.ParentUid)) + return; + + // Anything that is an active fisher should be fine. + if (!FisherQuery.HasComp(args.Transform.ParentUid)) + { + StopFishing(ent, args.OldParent); + } + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + if (ent.Comp.FishingLure == null) + args.AddAction(ref ent.Comp.ThrowLureActionEntity, ent.Comp.ThrowLureActionId); + else + args.AddAction(ref ent.Comp.PullLureActionEntity, ent.Comp.PullLureActionId); + } + + #endregion +} diff --git a/Resources/Audio/_Goobstation/Items/Fishing/attributions.yml b/Resources/Audio/_Goobstation/Items/Fishing/attributions.yml new file mode 100644 index 0000000000..2d9605adee --- /dev/null +++ b/Resources/Audio/_Goobstation/Items/Fishing/attributions.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2025 Aidenkrz +# SPDX-FileCopyrightText: 2025 GoobBot +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- files: ["fishing_rod_cast.ogg"] + license: "CC-BY-NC-SA-4.0" + copyright: "Taken from Goon at commit 0c11a46275f493f295d6d05d1aa5fad1d935efb4" + source: "https://github.com/goonstation/goonstation/blob/master/sound/items/fishing_rod_cast.ogg" + +- files: ["fishing_rod_reel.ogg"] + license: "CC-BY-NC-SA-4.0" + copyright: "Taken from Goon at commit 0c11a46275f493f295d6d05d1aa5fad1d935efb4" + source: "https://github.com/goonstation/goonstation/blob/master/sound/items/fishing_rod_cast.ogg" \ No newline at end of file diff --git a/Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_cast.ogg b/Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_cast.ogg new file mode 100644 index 0000000000000000000000000000000000000000..9ae49abb50fef3c2e3b2cfd04cc6c4418c30989d GIT binary patch literal 11314 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_reel.ogg b/Resources/Audio/_Goobstation/Items/Fishing/fishing_rod_reel.ogg new file mode 100644 index 0000000000000000000000000000000000000000..95c543b8f7a0dd914020154f5d5510a376502643 GIT binary patch literal 29125 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 literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/_Goobstation/fishing/fishing.ftl b/Resources/Locale/en-US/_Goobstation/fishing/fishing.ftl new file mode 100644 index 0000000000..14fedcb423 --- /dev/null +++ b/Resources/Locale/en-US/_Goobstation/fishing/fishing.ftl @@ -0,0 +1,5 @@ +fishing-rod-remove-lure = { $ent } is reeling +fishing-progress-success = You pull something up from the spot! +fishing-progress-fail = You missed it... +fishing-progress-lost-rod = You lost control over the { $ent }! +fishing-progress-start = You feel something clinging to the fishing lure! diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 1b5ac30a9c..be2ed43986 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -63,6 +63,18 @@ - MachineMask layer: - None + # Begin Goobstation - fishing + fishing: + shape: + !type:PhysShapeCircle + radius: 0.4 + layer: + - ItemMask + mask: + - HighImpassable + density: 1000 + hard: false + # End Goobstation - fishing - type: PlungerUse - type: Appearance - type: SecretStash @@ -92,6 +104,13 @@ reagents: - ReagentId: Water Quantity: 1 + # Goobstation - fishing in toilet is fun + - type: FishingSpot + fishList: !type:NestedSelector + tableId: ToiletFishingLootTable + fishDefaultTimer: 45.0 + fishTimerVariety: 15.0 + # Goobstation - fishing - type: DrainableSolution solution: tank - type: ReagentTank diff --git a/Resources/Prototypes/Entities/Tiles/lava.yml b/Resources/Prototypes/Entities/Tiles/lava.yml index 4c0b8750bd..f9b8672fea 100644 --- a/Resources/Prototypes/Entities/Tiles/lava.yml +++ b/Resources/Prototypes/Entities/Tiles/lava.yml @@ -21,6 +21,13 @@ multiplier: 3.75 multiplierOnExisting: 0.75 - !type:Ignite + # Begin Goobstation - fishing + - type: FishingSpot + fishList: !type:NestedSelector + tableId: LavaFishingLootTable + fishDefaultTimer: 25.0 + fishTimerVariety: 15.0 + # End Goobstation - fishing - type: Transform anchored: true - type: SyncSprite diff --git a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml index 27858fe429..ee5fb00843 100644 --- a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml +++ b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml @@ -21,6 +21,13 @@ multiplier: 3.75 multiplierOnExisting: 0.75 - !type:Ignite + # Begin Goobstation - fishing + - type: FishingSpot + fishList: !type:NestedSelector + tableId: PlasmaFishingLootTable + fishDefaultTimer: 25.0 + fishTimerVariety: 15.0 + # End Goobstation - fishing - type: Transform anchored: true - type: SyncSprite @@ -51,6 +58,18 @@ - ItemMask density: 1000 hard: false + # Begin Goobstation - fishing + fishing: + shape: + !type:PhysShapeCircle + radius: 0.4 + layer: + - ItemMask + mask: + - HighImpassable + density: 1000 + hard: false + # End Goobstation - fishing - type: Tag tags: - HideContextMenu diff --git a/Resources/Prototypes/Entities/Tiles/water.yml b/Resources/Prototypes/Entities/Tiles/water.yml index 490b543261..8d5fa8ebc0 100644 --- a/Resources/Prototypes/Entities/Tiles/water.yml +++ b/Resources/Prototypes/Entities/Tiles/water.yml @@ -49,6 +49,18 @@ - ItemMask density: 1000 hard: false + # Begin Goobstation - fishing + fishing: + shape: + !type:PhysShapeCircle + radius: 0.4 + layer: + - ItemMask + mask: + - HighImpassable + density: 1000 + hard: false + # End Goobstation - fishing - type: FootstepModifier footstepSoundCollection: collection: FootstepWater @@ -63,5 +75,12 @@ - type: TileEntityEffect effects: - !type:ExtinguishReaction + # Begin Goobstation - fishing + - type: FishingSpot + fishList: !type:NestedSelector + tableId: WaterFishingLootTable + fishDefaultTimer: 25.0 + fishTimerVariety: 15.0 + # End Goobstation - fishing - type: CosmicCorruptible # DeltaV - Cosmic Cult convertTo: FloorCosmicDecay diff --git a/Resources/Prototypes/_Goobstation/Actions/fishing.yml b/Resources/Prototypes/_Goobstation/Actions/fishing.yml new file mode 100644 index 0000000000..0bd25c9204 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Actions/fishing.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2025 Aidenkrz +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: entity + parent: BaseAction + id: ActionStartFishing + name: Throw fishing lure + description: Throw lure from the fishing rod to catch something! + components: + - type: Action + icon: + sprite: _Goobstation/Objects/Specific/Fishing/fishing_lure.rsi + state: icon + sound: + path: /Audio/_Goobstation/Items/Fishing/fishing_rod_cast.ogg + itemIconStyle: BigAction + useDelay: 2.5 + - type: TargetAction + range: 15 + checkCanAccess: false + - type: WorldTargetAction + event: !type:ThrowFishingLureActionEvent + +- type: entity + parent: BaseAction + id: ActionStopFishing + name: Reel fishing rod + description: Reel your fishing rod to pull an object that it attached to, or stop fishing. + components: + - type: Action + icon: + sprite: _Goobstation/Objects/Specific/Fishing/goon_rod.rsi + state: icon + sound: + path: /Audio/_Goobstation/Items/Fishing/fishing_rod_reel.ogg + itemIconStyle: NoItem + - type: InstantAction + event: !type:PullFishingLureActionEvent diff --git a/Resources/Prototypes/_Goobstation/Catalog/Cargo/cargo_service.yml b/Resources/Prototypes/_Goobstation/Catalog/Cargo/cargo_service.yml new file mode 100644 index 0000000000..55e21b9861 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Catalog/Cargo/cargo_service.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: cargoProduct + id: FishingGoodies + icon: + sprite: _Goobstation/Objects/Specific/Fishing/fishing_rod.rsi + state: icon + product: CrateFishingGoodies + cost: 1000 + category: cargoproduct-category-name-service + group: market diff --git a/Resources/Prototypes/_Goobstation/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/_Goobstation/Catalog/Fills/Crates/service.yml new file mode 100644 index 0000000000..f7815231d7 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Catalog/Fills/Crates/service.yml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: entity + id: CrateFishingGoodies + parent: CratePlastic + name: fishing goodies crate + description: A couple of fishing rods, a bear, and a hat. Everything you need to catch all fish on the station! + components: + - type: StorageFill + contents: + - id: FishingRod + - id: FishingRodGoon + - id: ClothingHeadFishCap + - id: DrinkBeerBottleFull diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fish.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fish.yml new file mode 100644 index 0000000000..18fe454322 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fish.yml @@ -0,0 +1,294 @@ +# SPDX-FileCopyrightText: 2025 Evige +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Rouden <149893554+Roudenn@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: entity + id: BaseFish + parent: [SimpleMobBase, BaseItem] + abstract: true + name: fish + description: Water-born creature from the infinite sea... Or toilet. + components: + - type: Fish + difficulty: 0.029 + - type: StaticPrice + price: 250 + - type: MobThresholds + thresholds: + 0: Alive + 1: Critical + 5: Dead + - type: MovementSpeedModifier + baseWalkSpeed: 0.1 + baseSprintSpeed: 0.1 + - type: PassiveDamage + allowedStates: + - Alive + damageCap: 20 + damage: + types: + Asphyxiation: 1 + - type: Respirator + damage: + types: + Asphyxiation: 1.0 + damageRecovery: + types: + Asphyxiation: 0 + - type: Butcherable + spawned: + - id: FoodMeatFish + amount: 2 + - type: Bloodstream + bloodMaxVolume: 20 + bloodReagent: Blood + - type: ZombieImmune + +- type: entity + id: BaseFishRare + parent: BaseFish + abstract: true + name: rare fish + description: Water-born creature from the infinite sea... Or toilet. + components: + - type: Fish + difficulty: 0.038 + - type: StaticPrice + price: 750 + +- type: entity + id: FishAlien + parent: BaseFishRare + name: alien fish + description: Looks pretty... Abductory. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: alien + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishAngler + parent: BaseFish + name: angler fish + description: Scariest thing in existence, after bingles. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: angler + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishBass + parent: BaseFish + name: bass fish + description: Probably the most normal fish in existence. Unless you use it as bass guitar. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: bass + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishBingle + parent: BaseFishRare + name: bingle fish + description: bingle + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: bingle + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishBlob + parent: BaseFish + name: blob fish + description: Unfortuantly, this is not a 5th level bio-hazard threat. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: blob + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishBlueFintuna + parent: BaseFish + name: blue fintuna + description: It's looking straight to you... + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: blue_fintuna + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishBoyFisher + parent: BaseFishRare + name: boy fisher + description: You like kissing fish, don't you? + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: boy_fisher + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishCat + parent: BaseFish + name: cat fish + description: Ironically this looks nothing like a cat. And it doesn't meow. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: catfish + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishTropicalClown + parent: BaseFish + name: clown clown fish + description: That is almost certanly some kind of joke. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: clown + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + - type: Bloodstream + bloodMaxVolume: 20 + bloodReagent: Laughter + +- type: entity + id: FishNukeDisk + parent: BaseFishRare + name: nuke disk fish + description: Erm, Actually, it's a bad idea trying to fit a fish inside a nuclear explosion device. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: disk + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishGib + parent: BaseFishRare + name: gib stick fish + description: "My face when i open a present and get gibstick:" + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: gib + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishMutant + parent: BaseFishRare + name: mutant fish + description: Anomalous fish that looks like it ate some nuclear waste. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: mutant + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishNuker + parent: BaseFishRare + name: nuclear operative fish + description: Predator of the nuke disk fish. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: nuker + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishPuffer + parent: BaseFish + name: puffer fish + description: It is poisonous... in many ways. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: pufferfish + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishSilver + parent: BaseFish + name: silver fish + description: Actually contains silver. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: silverfish + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishTropicalSun + parent: BaseFish + name: tropical sun fish + description: It probably looked at the sun for too long...? + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: sun_tropical + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishToxic + parent: BaseFishRare + name: toxic waste fish + description: "Looking at this fish, you understand how much it was a bad idea to allow nuclear waste to be dumped into rivers." + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: toxic + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + +- type: entity + id: FishTropical + parent: BaseFish + name: clown fish + description: This fish is always searching for something... Or maybe someone? + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: tropical + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + - type: Bloodstream + bloodMaxVolume: 10 + bloodReagent: Laughter + +- type: entity + id: FishIan + parent: BaseFishRare + name: ian fish + description: Water-born ian from the sea. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi + state: ian + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fish.rsi diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fishingspots.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fishingspots.yml new file mode 100644 index 0000000000..36b9406fb7 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/fishingspots.yml @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2025 Aidenkrz +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: entity + id: BaseFishingSpot + abstract: true + name: suspicious waves + description: Something is floating here... + placement: + mode: SnapgridCenter + components: + - type: FishingSpot + fishList: !type:NestedSelector + tableId: WaterFishingLootTable + fishDefaultTimer: 25.0 + fishTimerVariety: 15.0 + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.25,0.25,0.25" + layer: + - ItemMask + mask: + - HighImpassable + density: 1000 + hard: false + - type: Transform + anchored: true + noRot: true + - type: Clickable + - type: InteractionOutline + # TODO: change visuals when it's active + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fishing_spot.rsi + state: water + - type: Icon + sprite: _Goobstation/Objects/Specific/Fishing/fishing_spot.rsi + state: water + +- type: entity + id: FishingSpotWater + parent: BaseFishingSpot diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/loot_tables.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/loot_tables.yml new file mode 100644 index 0000000000..b6b936a77d --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Specific/Fishing/loot_tables.yml @@ -0,0 +1,109 @@ +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +# This should be on every other fishing table +- type: entityTable + id: BasicFishingLootTable + table: !type:GroupSelector + children: + # 75% chance of scrap of some kind + - !type:GroupSelector + weight: 75 + children: + - !type:NestedSelector + tableId: SalvageScrapLowValue + weight: 65 + - !type:NestedSelector + tableId: SalvageScrapHighValue + weight: 35 + # 15% chance of some trash + - !type:NestedSelector + tableId: GenericTrashItems + weight: 15 + # 10% chance of low-value treasure or maintenance tools + - !type:GroupSelector + weight: 10 + children: + - !type:NestedSelector + tableId: SalvageTreasureCommon + - !type:NestedSelector + tableId: MaintToolsTable + +# All types of the rare and unique fishes +- type: entityTable + id: RareFishTable + table: !type:GroupSelector + children: + - id: FishAlien + - id: FishBingle + - id: FishBoyFisher + - id: FishNukeDisk + - id: FishGib + - id: FishMutant + - id: FishNuker + - id: FishToxic + - id: FishIan + +# Fish from water +- type: entityTable + id: WaterFishTable + table: !type:GroupSelector + children: + - id: FishAngler + - id: FishBass + - id: FishBlob + - id: FishBlueFintuna + - id: FishCat + - id: FishTropicalClown + - id: FishPuffer + - id: FishSilver + - id: FishTropicalSun + - id: FishTropical + +# Items from Water +- type: entityTable + id: WaterFishingLootTable + table: !type:GroupSelector + children: + - !type:NestedSelector + tableId: BasicFishingLootTable + weight: 70 + - !type:NestedSelector + tableId: WaterFishTable + weight: 20 + - !type:NestedSelector + tableId: RareFishTable + weight: 10 + +# Items from Lava +- type: entityTable + id: LavaFishingLootTable + table: !type:GroupSelector + children: + # TODO: lava/plasma fish + - !type:NestedSelector + tableId: BasicFishingLootTable + +# Items from Plasma +- type: entityTable + id: PlasmaFishingLootTable + table: !type:GroupSelector + children: + # TODO: lava/plasma fish + - !type:NestedSelector + tableId: BasicFishingLootTable + +# Items from Toilets +- type: entityTable + id: ToiletFishingLootTable + table: !type:GroupSelector + children: + # like water but no rare fishes + - !type:NestedSelector + tableId: BasicFishingLootTable + weight: 80 + - !type:NestedSelector + tableId: WaterFishTable + weight: 20 diff --git a/Resources/Prototypes/_Goobstation/Entities/Objects/Tools/fishing_rods.yml b/Resources/Prototypes/_Goobstation/Entities/Objects/Tools/fishing_rods.yml new file mode 100644 index 0000000000..aa4a6d9f65 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Entities/Objects/Tools/fishing_rods.yml @@ -0,0 +1,117 @@ +# SPDX-FileCopyrightText: 2025 Aidenkrz +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Basic rod +- type: entity + id: FishingRod + parent: BaseItem + name: fishing rod + description: It's time to go fishing! + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/fishing_rod.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fishing_rod.rsi + size: Normal + - type: UseDelay # Just for visuals and to prevent autoclickers + delay: 0.08 # 12,5 CPS at max + - type: MeleeWeapon + wideAnimationRotation: 45 + attackRate: 1.0 + damage: + types: + Piercing: 4 + - type: FishingRod + +# Variation of a normal rod +- type: entity + id: FishingRodGoon + suffix: Goon + parent: FishingRod + name: fishing rod + description: It's time to go fishing! + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/goon_rod.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/goon_rod.rsi + size: Normal + +# Makeshift rod +- type: entity + id: FishingRodMakeshift + parent: FishingRod + name: makeshift fishing rod + description: Probably would be hard to catch a fish using that. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi + size: Normal + - type: FishingRod + floatPrototype: FishingLureMakeshift + efficiency: 0.8 + - type: Construction + graph: FishingRodMakeshift + node: makeshiftRod + +# Golden rod +- type: entity + id: FishingRodGolden + parent: FishingRod + name: golden fishing rod + description: Finally, you caught 250 fishes. Here's your trophey. + components: + - type: Sprite + sprite: _Goobstation/Objects/Specific/Fishing/golden_rod.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/golden_rod.rsi + size: Normal + - type: FishingRod + efficiency: 1.3 + - type: MeleeWeapon + wideAnimationRotation: 45 + attackRate: 1.0 + damage: + types: + Piercing: 10 + +# Normal fishing lure +- type: entity + id: FishingLure + parent: BaseItem + name: fishing lure + description: fish come here + categories: [ HideSpawnMenu ] + components: + - type: Sprite + noRot: true + sprite: _Goobstation/Objects/Specific/Fishing/fishing_lure.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/fishing_lure.rsi + size: Normal + - type: FishingLure + +- type: entity + id: FishingLureMakeshift + parent: FishingLure + name: fishing lure + description: fish come here + categories: [ HideSpawnMenu ] + components: + - type: Sprite + noRot: true + sprite: _Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi + state: icon + - type: Item + sprite: _Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi + size: Normal diff --git a/Resources/Prototypes/_Goobstation/Recipes/Construction/Graphs/misc/makeshift_rod.yml b/Resources/Prototypes/_Goobstation/Recipes/Construction/Graphs/misc/makeshift_rod.yml new file mode 100644 index 0000000000..90d211a7d2 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Recipes/Construction/Graphs/misc/makeshift_rod.yml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: constructionGraph + id: FishingRodMakeshift + start: start + graph: + - node: start + edges: + - to: makeshiftRod + steps: + - material: Cloth + amount: 3 + doAfter: 2 + - material: WoodPlank + amount: 5 + doAfter: 2 + - node: makeshiftRod + entity: FishingRodMakeshift \ No newline at end of file diff --git a/Resources/Prototypes/_Goobstation/Recipes/Construction/misc.yml b/Resources/Prototypes/_Goobstation/Recipes/Construction/misc.yml new file mode 100644 index 0000000000..72a738c521 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Recipes/Construction/misc.yml @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 Roudenn +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: construction + id: FishingRodMakeshift + graph: FishingRodMakeshift + startNode: start + targetNode: makeshiftRod + category: construction-category-misc + objectType: Item diff --git a/Resources/Textures/_Goobstation/Interface/Misc/fish_bar.rsi/icon.png b/Resources/Textures/_Goobstation/Interface/Misc/fish_bar.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..17535a5ca0129c2e0283e1b3c476faa2ec00fdb8 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^96+qV!3-oN^iDnkQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1``2&1HT!n;0^mI(jOs$m^)q!GvLtga)Db|u8zu^Bs!0>w;UocRBv%n*= zn1O-sFbFdq&tH)O6x8r^aSYK2PEJTPx%UP(kjR9J=WmQQZeKp4e;6B65MnkEFbNPz_#2x8e)6d|#sXFw4*-~^ReRPrrY zp^7t<3shNkowO2kkw!{NoxetQqF@&FOx-4S9EjP_x3b1F^WMDg`5VJM{&xs~qmOUB z?JYwMUJe}qv3Kw_c>$9Ac_~lM$S`uIoV5r4Ohj6eZ@v4h28UnX1MtDkc~!F}j`t4} z6Are_k zof5BRuCN*{hUZ;Mm2zSt(g0yWtC}@oHCkTr<hml4MS1ueg!o?UAkryNT~E_FbjK1dEQ8T?$fRJs)@ zVkHMMx_0xtqhS=Xp>v(SEk)ewrd6{h^CXV<55pah6BnI6qc0}c0%McD&DH6JasluF z+1NNgIsp(19jKZ$&u>0+7=IZnUEx*C&dsntLr2Px&+et)0R9J=WmqAQiMHt6_gAZ8x@^&Tcy0B|)3N#+pdLW#eG>x7Fy|mHxK!QLE z@n)NbL$hp9sqt#`V9&LQH%ZW#G*}KzOo|>1iD`k&QdznM0{b=yEM&*SGQ)ds9~7wJ zt4N}UaFMta5=~FRQ@&y95HQL;3tkl~#!2H4@;}iEef9`_oHwTc0 zNG6jq-W2hVz{z-2HbxuR*en7NsIBoeRr>JC$GUFYHh>24iH3+bMf5UN97M|8Pwj09 z+S?Kp?Uq@q>%m|Ufb-`rFg|g=VkzQH5orYKFamV|7+!Pdy>sU6Z3(`;^8*0=Js0`$ zr-#I%As0kpZBs+owoUr+6Vi{LxByb0e?cZarxMg1ft!OP0CaU<_l}b+WlU*<7ASzfaNMb5YO!F!F-TFD&{m zK`>ayJMX?nb@eNJbh$@_!!cRKHYE(QxjcZ&Je$jt8Xh5eYY>3c@W{R(o5d1P-WA&$ z>wxDR7#n*4ARX=U*YRJ(_jm6|Zz#QrMMD~LFKrcz-2Un|ne-f|PQO7WJ-0jRy?Z}P z+g=yJU>#R`FX?0~VMq>O?sxA5+A5Z}bS6FLDs>JlEoHg!X_uUxox!q9EX(BP;0RT0 z?`!Tg@`9)fpnpIotWcLRHugYX?Y%^Ea|@Pb^3B(u5h|3oQ*(363(Wyc)1)!l;0uy@ zT5%);piV@?w%4&N6Wd-VRM_O!cXt`?y{tVKFZ$fp}ygPOz65~vV+4-_Yu)6A*0wKcYiMa&ZcvG&i#kpS2%mh{P2U9} literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/bass.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/bass.png new file mode 100644 index 0000000000000000000000000000000000000000..eb8618fb2d4af8ac339fb086fe0c7981e3bf3670 GIT binary patch literal 783 zcmV+q1MvKbP)Px%$w@>(R9J=WmOV%tVHn4M*Cc}cfV z{knsph8pTWOBmE4nT#2|W7gKT9}VD1zs7WW(Et!dAEM}^Q#R7+#YY3Ua|V*hm|>dg zdIM4Pq3aD3!Ym7-=rg+8J7a`I;_JNt-2LEmy{>kVA4wzUONF4qVK{e(i( zxZN({i7O)dITSz~gZVt=2Vga}x?C#L>}D zVRrTzD=R4gVShh^sunEM8=#0IbMv{r{+in)%MlWZPqbRshMAmo6LvNt06RNJfb%w- zpO0IC2FV0|esI8#xj9Gs>1kf5R*(CCjMC{vqx<^p1?i`;u@T|o;x`kT#+A><)x?CO zkD@4juVUK~>VB_QzX?-QuWWrwr4QCg3It*TL{JoEAZT9zK)AeYF*@2_+mR6u?RO1D z`Dp8yN=2=5rLxV^())qt0|6fR-5>amac_W!8mCgPx%=Sf6CR9J=WmOX15Nf^g}vu~2UBvuZ`fyF`Cn22<7VMr4Ub`jF0xG!+UrMc!v zARi!*HdQ)*g%tS$3ZxTm*n*9`zvQAibr>!xl#pIu9?YwCJ}m4GJy@gr3m z8(XDDNfIk5a}fl2i8x7OJTDNlSzhbfPO6HtvttDaua5lv``6sR@AB-~8@ooO>6A2` zVhfzja(ca^($>xA1+MF;qL75)I(aS$0-rpW01StH8jV!}SBG$a|6fC%=aQ3?4;&x& z0eJD^E#JR?E0HEi48UaaAHLrZtE;#1{U%aoCR)ATCapkx`SS5{3v6t+0RBB?e?xofXch)+6$az7XY>7K*@QM#I#zQNI5ZczQ1OS zRH5BNB>@|q&OwP>r*nGxiQ%wMnvP5BrJSH|Nt#ZL813FlfQJvCo9BfDPEJ0U=SPqJ ztQFU8?=l?ri6RKYb$u8)KmTghG@EybqHW+Z4J!$SwG9gitgQUO&dyVIcAl2pcRB}V z)FMWsK`DuQ_Z|q}UlESui7dNdG#W4(4Ulp|6m1(pb_xPuZ|^V0#10vH$iFxsOv zFFel^)gT0`lU`4KN#p40_z3_ z(Z?ab#5}8QtWurMOcZeY?PXPXP|fDak@vA1PxnbEuG#b%hSP-I?4I8hCZvUFT?XOqLy;a?us||TrI1~gwl{G)Q66Ex3)}xM< z-g8756z(3d`ls$xaP>OhpVxdfOsb7{w M>FVdQ&MBb@0LOR6jQ{`u literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/blue_fintuna.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/blue_fintuna.png new file mode 100644 index 0000000000000000000000000000000000000000..78f40ebaa803b9d33bc569360aea85c167dbe402 GIT binary patch literal 660 zcmV;F0&D$=P)+tWGs#yDmWBD zY!)Gsp%mO=RP<@^7l}rm`L6FAPMUY$ed*NUT?p^o^XvQWckjC|N+lyvAW`6?D^Pnd z)`J=qA zKXs3LkKb?|f%m(_hH}Kmk97hBpXWG>DCoKLKp{c zyNW(QGDq6Dmg9`zmh`Rv2LK4*I3s9^ZQz3)Rfl{l0W_GD1QCe>PhyDzi2|g+KN5EC u6_SI}WdHyG4rN$LW=%~1DgXcg2mk;800000(o>TF0000Px#8&FJCMF0Q**#H0l008FV;@Q>JtOEnb$H#^M00000DlpVg5`b4)*qh*H}ShXa+9AZ7NCrc z5;#%@?y*40A#!N)v+N!#xs5m_I0_8sjkYxwsJI#Cmq6gwTE~ZlvC9h-+(h1v@mI>TU5a@Gh{`HM`51mh-3`%jlp;4g2rTJF@-b8CdE0$C54ZP>D?>^xclo}gU zb6(wqP6r5k80TSjGK1U5IO#hK^;N*NGvrwvKiD+)5wW$Ws2r7r!5p z&%OAYn?mJ|pUSHucXM^QnG4FCUSD8FqwXAT?}<;`fU?Zn{h%>B<8KA-{=b-=^Vz)* z_m|}F8yyP%U)H$!`&8t5ZTtU;wanfRpx^@neScqaDEN@TrttrA{b0!6`u_m|jsG7M zP}~os(E5L|zSjTC0%^`G|1S$@{l64?|1T;m@c;5EwEll!pOpSzRM7i>S>p5mU;JkL Y0jD4vpvC8|!vFvP07*qoM6N<$f;aFoMgRZ+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/catfish.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/catfish.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0e1a6135b362d9c243f3a533aad1f0cdb6065a GIT binary patch literal 687 zcmV;g0#N;lP)Px%X-PyuR9J=Wm9c9RQ5?rVud(frLNGRg8+u@&1cVZ?lx8WE4#ma67R0R}E%_S^ zf(~+ve?W&6F(7o2yGf8hOToc(h|o}7Dk?-M+_h>U1#O1skiN^i^gJ5`7w@~=-OGF5 z-{SB;%^OVRZI4I78XkX2B228z#o z{|uO?$}$olRqlAIIrVSDUHFYANA5>)B|9z^(9GNlJKO7BfQI8p0Q2STfbps2lF4KO zW2%TM+N)kr9VS_P$jsadKo>w2H%t>bHU_{@Da)zl0{8&Ot0J_hKJIy*zdj7&`Xd{F z{exYSBcozKP0)+0C`&Y@m3mEJ>9mlWR>T*P1+i_1-@$i2fLiK4kaK&!*OK7Fvt!L`hS6*UcawYDl9KAM|LEvU^+KV zb@Po#CEocjE_E72!*MW7Q$E|;iZt$10$?`Z_IGSLb5(rxK1t{vK>*>^>k<&#dwYBi zX}{8JzAYz=e8=o8!Ry^Y^&5IJ@4FDU?%bzVwZg0d5W=h10mw{EMMkPsEve2gbT-n# z(SZ2yB@sxfI`F&iz!_BdoA6Hn`g3w*RGhR9DJ+yilSX}r^!fb&^i?EQtXQ!ws$Uvs V7>FFPx%rb$FWR9J=Wl}kueQ5eU6M-+T8&a{vgJ`f-G2!iB*T96siY;r$Xw1^3E;jCsM zv=zc=eP=N)3fcs^j9PeQXox_H7-%&!IwFy?7?aGY2wa>N_uhGM=i#WUnm=6b@EdbqP0K92>jMk$u)IZ41UfL9JU!s(`F5x!?i-?o6se&kxTVEI9 za2WbjnJESG^TJUA+hjePRX(4u^2+_sh38)Ic--vlrSl~K2#3Q!p=D1cpa{Y+3}qMw zwvbdRl^;xgFPhm4iQOb412SKNXEU(#_N5pfALrviyn>D8O8}VdhE)zJCAzL-7zR>G zHs{Q&>}&rxgQwF?VmFD$@QJRS!SRtGqn&_;vT4mXf#S1*aof_eYgI8W*h%;9K~f(mjlsgQ~_kO zSu&Xn+S;1Xwzkk}Ev$li9S}BoOG}GL`Tgf2uUo+E>?~NWot&Hy3Wd%|e}+9UKR=%r e(Le+L1pWeXEb7=2Vc#tP0000k44ofy`glX(f`xCZ!yxB_XG zEN=FrA`F-Km^bprpM7%q$NyRH{)dHyEm^W8#PWZ4n;L6sbl3A=FCLog*t&Jw2GjE= zv>5`~80K?tdir7R`I|r!YAiSV0V(m4AirRS|5(9>?&AAE`&~U<977^n-%jJrZ&r|K z`Kja4UYhj(f4uCT70%5uV%K#u_Xzm@nj`nMJNZrMqdD^*{?QQ^D!BMaMuvZ9$DYIk zFOHXl2>_jKEf z4c~wFOw}&V#cG<{wXEYj~cXeetd?PXN1MNA9hBTx?CI=y-Y@g~qN2;yh96Q(TwWJ{?B9*X`>yf_)voxo qT3#jguY&cux&W(nbswJpVw!zb^M7FX|7pN5XYh3Ob6Mw<&;$S`!uV_e literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/gib.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/gib.png new file mode 100644 index 0000000000000000000000000000000000000000..5371fc77dce14419a0955676035062055725b42e GIT binary patch literal 575 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 B4sieg literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/pufferfish.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/pufferfish.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4133342112926f6de7f11167ee6359d02282c1 GIT binary patch literal 1250 zcmV<81ReW{P)Px(oJmAMR9J=WmtANaRTRg6v)v@RY>Y_@u3?Rv*pH2tG?gjEfQeA3Qr3rjloqXm z>_e$Sh6MZ4hv0jblvQX560|R+rFE+$h!&!59tv&OlB5x<4b65Pn-om9#pz|0EUB)C=Yo>O2*QuF0lrJq{FtonL4! z<;K{qr4W8<0-!jIEoAvJ9LX0`~?0^hb};y&ed4DR@2L zuBpZA0j~#6?u_BCsny40QGUMkt(dNZkv8!-iQB3K0s#Oby-73b>>~ph;ElHw5|dGc z?)8A&ZuH>~^q0Mh9+06|&ddLmz3LU-5H zQQ@|ys&4DcG~fJ4&jlup$D$^)jwy)rCMg9TAH8pL^mwC6-+Vp|L0RGO(or)t53@%9 zuz!ZmsTLcnpH;`%eJ>)FT?OU9gR)Yj3syv0D%?hqx@+o8V6O)j|AO83BGRR2Keng9 z8vUVx0O@qPB)ByNK#>lxqb)zqTFd|q=`qf9b~x9709RGD2;2(RH}7XIWp`|3<6v2* z3dPZZYuB!!sw!TumzUoByR3oD4KSB7p7B_e!%Ig?jky23#&AErWixG0q2f zI?yucjw$&5^J+9rL(??Q3{^WuY|Xs@pG(15hiV%;?4RMi-&+WEDYVM_03`o|A0@$t zBLluon85NYC5b2kw=xivbN>ho>Sw1Wr0uPH^m@M{5&Hykdr+YRl9n;H;$UNb*@$Qh zDHeSKJSZ!$1j{Rsn2aK~D|E(;=x?hxOwgYG{Cryq>S~O4?K8%EdBrf^-KVpw`xc2` zih-?>Ur$7B%W&`MAOJI0C!~7+79EPu2Q~-p-bWY4D%|kLBJAqEWquD2=r$+DI#d%p zC@UOqRL#PZ+l>|QL-rjhm7F!BFBHcbsH!v~d%*`+zIu@>z`fjdAZj8gD`aopHRnuU zj55*&iL888xuE!AOB`lkvf~$zWx9B@Z~-2Gfhi zmK(kq=E$V|JTP;0LTZ0?k0Y=Ur)6kTRaI&HIE2xM+59u~bezPbL3d&@%4pN5IWE+t z&=A-zrINF&o(YTcVmU79IVYs$6>R@-fGDR?IB2%R8KdoTA+^f;$Q7Nmngc~O(9>@^ zA%RJ`1R$0uz=M@ET4lcdQ^osIq`OwXt=^#Czr`^)Ct26UM6eU;QuI@aW$8}xGJQR- z>!%XS(wB2PxNz_~Px&2T4RhR9J=GmO)4qQ5eU6Z`sUU6}DCcX`nrX3}`Njmmw&Wu2u&R-RjcGpzA^% zHN1FBD71rTmqrJa%@!|L^<0 zdEa~Rzo?kOE@m44U-UGL0jxcUH4*JZ0L2WpwO3i6i69QkjmHqDVeZ7~n`t~wQx%ZS zw@Yv~3Rj*t6A22x|U zU_PG@4N=Ts*97o0)ikYcK+CdRh}pp$S_i%$&itqXFbu;EYkI%*$X!2ap9lrv7(-=v zc>rYdplls;YY$#_Pk5gBLBR3UbmRgAnd`dluCK3*x#s3@768*U>q5k$h-Az&yZ}U0 zH6Op#MQc#L3x^ci-sh{cTY0~L5Q;Da(=7mDR z6FFAibk~Q`ivXN|HAYvv7ocyZQUOsc7KPLI#g!HwXUnXW`m2zY{<(76qngWlxNdk_ z2H2m+D~FMtNF=Z<%k%P?rpeOMlAFCeg6oxDRjjxsxm;yz20`qOX4xIhR%IV#KDV&U>|luxm zRiRMue8OnE1UI_Az~47tg0S6fs6xm)Lme`z{IZvIR0jFjb|5@+K!uPM1X0U;ZlPxH z1yUudZbbFo3DY#IK>W6yqsg8eUw^f^BY$Mxt%LxC?|r~93;^HrL7tE@gq*!&exY?A zzgtt?3yBKhNQQC7ty=al=79h*1k|4Cnx@em-|_T2ta&H^3Bm=brOtD0V&~^R0Jpo> zx!h3*G!G3>0|bD{o}3HxX)h@rya+cJng&qy{tf`Pa>~`c3!W-~455CXXhjS@3;zEf X5p|7`jTFi200000NkvXXu0mjf$7qH# literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/sun_tropical.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/sun_tropical.png new file mode 100644 index 0000000000000000000000000000000000000000..2cee2eafc90445d821d3d6bfc6c9fe441b3c4bea GIT binary patch literal 1037 zcmV+o1oHcdP)Px&$4Nv%R9J=WmS0R;RUF4Z2N|QegBDBKyu+p))P<2i0gZ7)mMGDL!Rwybn~O_a zrims-A7*56W{FDx&4N*%+>@^$iOEc4$xx>&t=mW@W5ogmVHMVaP*`$(XwTu^{=rrr zeBt|Y&$;L1d_U*+JO5x;yV}+NEKx}3u`^wUzoV1j@DPA#`fM<3J0SazsteKa`py8x z&U6{h#%5xn0M5o{o<67(93Daf`8zt9o{a*iKC-+WL~*_V=?V@H;cRSH-|5*Xu}}cO zVlLZkw$iNQ*0Nwo*pt7g;=UoYDD3 zNQ;2fn{S;1ps%-wGFJ^Xo@xarfy!Jps(+2A8t2@!+8rs+WCR*&b>p{tQN}L4gR@+- z81)Q(XQ|&9k67w`y*&U-PR(RMzOvU~Wn~qB>yPGncHtqgbxtIfmy!Sj-R*|&kcXFh zzWi5+8{-lBdV7f8{z3iwF%-ey`jRr08Xmc>%1=CVfLEVNM#OwZ$P|c)`%}jE=U$`f z?N72om}h_S<4e@KT^X}AOU|5Bu8l|Qgo~F9fO5#(1q0pf0EX|72Y@H-4gh{03-W7x zMVagS?VRjtQ8Nd4q0!BY&aHe)fNM8{0OItOs{o8#I4heCc}7^ap@cC1yZqC*D(n00 zG#u9Xdu>zwOE;G@zjiZ7TZ^B>#rD?%n>H=;AWA_17;P}VHV2*|y%xscNX7j}4UHl!LJc#@uW^Ui2AOI7D0?9`* z>~gE^6^JTbXGaf`f$nymci1csl3Tyu6Vr2lQg`HtT2KJx(Xd+KTD4o0IxBIX`Y`9% z*^jw2oxXCFxb9Wvu}}a{NfD{lEjCLvQM|S)WI3_CBueZKu{smk)*K3Z5sA3&HAq1u zmX`q3E%#rQR|HH(LPlaE1+Y`)4Pb`YEY-xu!`wSzSB2;wG|;0{NZ=BH00000NkvXX Hu0mjftj6(U literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/toxic.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/toxic.png new file mode 100644 index 0000000000000000000000000000000000000000..1679f0f548afe35c02e9a8e7d0e94e3f4bd739d7 GIT binary patch literal 694 zcmV;n0!jUeP)JAf!0hus~5fhxR$FC{+ zy)j`HvU!yZ)vvm${=cfLo0K}~I?#2X>%cvCVCjG2zd3q&E#Bel4;lN;1Y(jpncr=i zxUJoOdbGNnu73Vb!;`ZFiRCuuXsZE9QcLu)vgjlDU}qzZ&nIy!wRRvTlr>Q>N2mhv zJAeHBLuCqNYVeaU-}AOa8w3n62^zrlr;i)#*^Bm?5M*KjfD244a*KfA01i)QOt&_- zGsw(6WfFkaQnxmeq4mh<+eJBhCX3%SA;I-k zKVTuaM)YJdNu$xIm`EN0SPP;q&eMgyYP!mC@B>fZG9(y0>(8IQri;_d?6a}iOZ(6F z^v~MrBd|c);&;8a8_YvF0ufBGw$jto&?Zzz2W=FAJn8kz+%Ok_M`;jXlffINn<2y| zF#)j66MX3!nql7GxtSp!AY=0_h-bpa?z8Q@4;YoyH^E@FKz`#NZ^4ht>2;ji=3c`% zgj|`A$Fuf9#ER{}%KB1TeYlpfv!X%o!EJK@M+t-^}hh8C=6#!i2ZnLJX6TD z`Nn*0>wgkx3y8LOdln@S^DVT7MC`~yNxGo>?7$ybR+pl?MD^kT000hUSV?A0O#mtY c000O800000007cclK=n!07*qoM6N<$f@M@e{Qv*} literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/tropical.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fish.rsi/tropical.png new file mode 100644 index 0000000000000000000000000000000000000000..78f138983c20d74c9dcefdfa2812fe733e5e100e GIT binary patch literal 517 zcmV+g0{Z=lP)Px$zez+vR9J=Wl(A0ZKoEw%J<$*fL;(~~P=HK@lt_f~8gvDz((nLoDB=eoPmmYb zv=lT*P^5x_O>q=RD5q1Tt8k!5K`M|CLeMV7*fLIRNFd@0{7I{|XLe`)UC$UYGBUn9 z>Vy027-D>QvpwB{nraLUnhyNLxC~-n*wS@*;HPF)kE-o9{|U3xZIA`g+5~pp65|3!u+Jfch4lw`m6|j zFJ~%3|1UB_tmMi2|yv=ob9@6!pD-sX_gFnd|jB5Vh>k0s>T{ z8x&z+=yW;*NqFTm#sJXh!ro7~{(YeG`8OGGd<@4Cnt>nJ`QW5`KKt@JJ##nd(zl^h2Nkn||00000NkvXX Hu0mjfgZ|}p literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi/icon.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_lure.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5d731573e18a460314cf4723004ab1e229f183d0 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}lRaG=Ln2z= zUOvcqL_vV{!uv_x9NcEpmR&kIZNg$(AJL|JB4W0uo=nj_!1+4$UhT2ALb9=Z9cL|G)#Om%gJRXPtorxWh2#01 zTMR;%_zpyM8y?%m;KK6$?|mb$eo56E;g9%V4 Z6vzj}vT)Qa7ybd_d%F6$taD0e0sssUDFOfh literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/icon-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/icon-active.png new file mode 100644 index 0000000000000000000000000000000000000000..835af5a280fb91f799d95be24c796ca4631cac10 GIT binary patch literal 736 zcmV<60w4W}P)Px%nn^@KR9J<@mpw=uVHn4MuQAE#64VxQ5roJg3PM58LAsbuf?LK;QmA{u)v2pc z7jf&*!KGyJ1Fmr?I47D|Xwj#Gxogz;k(i4Q-1I*8{{GL$J@>xw z1hzcWC(AQ^0JJ;>98mL_UI9=~0e|`KyBr@M6OYGL(=y<~2j6^=QmNzx-V^}|!;o)3 zMa^e=Wo2bWot~b0YrYFe7={4q?T55XOiZZN)m7s0xZl|S@PuLDIF97=d9vB8w|01V zC;+W~!U6ShZB0t0lF~GdSS)tK&zBOo@UUTcHSg@~tm@nF3{*xAVB2=png@eHpurh% z;Q*0Hgit7?hKGmme5YXksPEGqbjw4WG1}w{xy}dn>$s|KVLuzzvtmdY>CgR}& zOw*L9sVUCR&gkmuQmIs`A%C|Aorp}+l57w+1V%>gE`eKf7ycah1i+^4p3CJ}Utd?RKz@|h z9t$s<+TY(7K;6hdCERtMzUK2tIB?m7#bWU)-!x5c$dn)7%yAsKG6$+f?x%n&*38IV zJ2^R#LZRR_p~~fQF1RU?+qO+dM@Mt-h5+ewS^(AF-Y!7%?uHen3Wb9A1^feQ`AN$P SFhDZ^0000Px%DM>^@R9J=Wl+SD0U>L{0uXZLkA+q`7AXq~gbY%zM%jn6Q;LW4Q9lQ^E?SD|D zAjMn%fe}1ZCBw_GhYec~V-ze10ihz6dMhLu>$8h-X|%R#jEC(DAur^~`}yX1zIlNS z=T3yH~<8LK?DK;mP)1O zzT;X3>~N*knyhEd-E{+LS*E-Ddj$M`_WAsLZs?p^IfNG6@AI)Ydwb7@&Z(6$V2A&H zctE*aMl>3o8#kqkg}S z(P*^PehYS0FELN2-nxHoT5)|!a1kmaEM$khpppdydT5+0A2y?7l0!d49x6+1@HBGXf~S+ z0Di0i4$SC)9d6YeIBML2PlG+x3|3cnhZo(FT9G1Z7WQOX{)|E z!68UiVC9xC#phT!81L=Yo$SQGkPxuEGWy8Vs`|6LAO74?Yg7F;Mtm9DW3}tw9zWL0 z$z8QtAZYeWrG|BGMcQZI-}$p4QLl}Ef#gm%t>f9ImgjHB&Rhqyl7V4r)+6hhJ*V#6 z+0T5ss_s=#WzAjt@0Lf(4_1rZ_u;VQUTfDP#2O~;bn?(D-i1pZGAm3nlHOD!XWtag e$iVO;mLorat2L@=^V;7a!#rL6T-G@yGywoTuxsG} literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-left.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..de26c62d813257d3f49e8f50b82832b64ad00324 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R5c zr;B4q#hkaZcIO>7;Bn1wBY?Y?sem>u=6`m!>kXwM|&IZOQ4dXSNNAk(Hgt z7Ya4lC|C+LJn>+dq|E5i#{%RC{7gK*e0z7l{rUC&+49LkY6qCVJb3eK`NvPQd^;M9 zepJrWzie`sX-nn&&4o)?b~W5@D$iLrrGa4u(>1pDQ-lNh#R7glY2Y*i<+=dsW= zyZxeF4NvF(w=>)SFYdt_+eRgYPw8u>CjR5txTH+{z^_%)q_gxhst;&nUWjGT{*V}y zV4}isaw3C@JJSRiPKP2)4iCfTa)mWkIahS%-L)2ol3f>Rv%9<@PBt!Ds{Y8{dyR2I zFRmt9Z)bVGK_wuox$K?fp3QG$4f>vce`X}J)FiF@&LxqJt)KozMQ3Vg3S7MT@Xv0M oAKqD)63v;ig5dG+x3|3cnhZo(14`EF9kwvgJG@s* zz;~9Kz~z7(`R6=8*e;bvt1V?_U|=tuWj6iwU(@-$^ZIYs=*~JSby@myVU?U~-1V&9 zi%uG=)Pp#Vmb!2)oFU-0w{^kcJ~;r5l2 zoIgF?Z+ZRw;9q~@bFM!4B73(bVDrMW?Hk;gXY(eWZ!?v2n!ML3^_zjR2vfvJoZ%gyQC;=s_{Qi zmJ+Vu&g9U?(jddhU@62LcwRb-hUo&T(`@zG@E%(MmE`0xc z;kwt|Qx|(mw0~by_y4=-2JVTfKCR~etPZXadUiK92hZi-il5Uv_<0Xkz722l zWBqw=Jtel6YVG)3CU;@$c7eYKL>{U+?Xed(8QskQk>&df>$fl?B$G_8XSX zefX;(Mup*t2g8Yp3=@;dN4~t(}79DmI zWL$cvTGn0fa=uuA(VpTbajm{gHh&iX+>%@-Yd3wdN=_Zq8b^EfZwY%67f-n}#h0m1 z;@LNjxs!As+`7~5FFVdQ I&MBb@09L54a{vGU literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/meta.json b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/meta.json new file mode 100644 index 0000000000..979f05fa92 --- /dev/null +++ b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_rod.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Goonstation, taken at commit 39ddf6bbd54c9f27fe49f5cea1a3119738b09596", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-left-active", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-right-active", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "icon-active" + } + ] +} diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/meta.json b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/meta.json new file mode 100644 index 0000000000..ead6c5ee9c --- /dev/null +++ b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/meta.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "water by Rouge2t7 on Discord", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "water", + "delays": [ + [ + 0.25, + 0.25, + 0.25 + ] + ] + } + ] +} diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/water.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/fishing_spot.rsi/water.png new file mode 100644 index 0000000000000000000000000000000000000000..1d9528ddadcdb42581c4257a71c8f97dbbeab6ad GIT binary patch literal 2341 zcmV+=3EK9FP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2)ao`K~!i%?U-9^ z9A_2B&+Kiycdxx(JL_z0Z<{8mLz=2>8UaO!My*gKF6~34!VB;Q2m}%p4?G~?0fYbv zs=o13L5p}nRaHQxAhl36DNPE!#i|=$I*z^GwY}c^&R%Bu{@?5w&tB}Bs5~)0X|=m^ z`ObIlXNDXc92^`R92^`R9RBZ6y5EQ5vrk;o@STdvzvOl)G#2qwwceEfONw6}`_<=8 zrR0GRnNNK04^lpeC6CZ-tXKZ^ttz=Z3jO{oAOD}ozwpz)Tk?U+?V{*-fa+yael+G6 z$B3T1_>n`)KhOaX7z}zXga7ljCG$rYx7S7C$x-sDUNVdhwVEv|ZSGQNDn+M`c;q$H z6A^ms!p%7Z28W?56;r`}V1QUWNUG{4qfOLowJE<^rjTc`0LlSy51Tvd}-YbKuA zKjN_r)2P;H{_c6od#{&Cxfv}If9=AA0l(|u) zaz>-+`4g6W)&uvr+HHFJOTF31^*tiisZ{b59Su>f?i+%b8Kl|!UrQ;p&lh2tSIL@f@)CamVf zkzCE)Fr79sCXc=Fo%`e_?)}zN+C6utX<^;Go+GPT+@{z>LLNi(a(#zlKxtKIH&-IV z9kPsqn%iy})DWNS^<_%V%}`D>XTa;u2HW(?>+jP`Kl;3s&-!!8@kfI+nV#v<-!7lp z1^K1=RT+H96b{N;@0z#d+x6$bO`Sd|9l-jVcL}(@qORIK3vcFgSsCf+v-324e3n!Z z=~#q~4-g%m;Fyg5{Q3@UytgE$f}wOumcf`eu5HL~yYWd0ThWC5B&H%XF&Pr|ew%jI zHl*?EklkB#9M-nOSC zr(-`b2AU9in-$>_1Y$?yr24{C6fyv39%?N+AR`>8j!w$oSOZ`k$j5|p>DBA<7-P>M zV0UCFI7-|3diM-TN3OlQBr?^IdxZf3M=45hyA!nZXZfpdFUmcEVfwS~{r!ha5Q9L@ zW{NUs-026t{B0a;luS>%loka>qaqzu3WiiF7j&8x23uNKr9vhv(yu`--=1e7w5?aI z4^IG-x6Dzgs8dKZG=IP=($z&?v1UM|x1uQGc|ka{A!bJbWVgi#r-q}Xh#sQ)y`;De ziYLaUeB963*6DO8S8UTAC*m}njLDA?jM2u-CnD*KF0L z-uOIt?{Ui24Jwy((u;+?9)RYPSz4x$nA^qrzbM38#?iD1*(w7(a#xlr9e?YTEeMk1GVvLvj5yz}Z66IjO%9}9A#;F=6hCcSj^ z-zz;MFVE+8IQ`))zV`MbqjY7-oPYrLIqc<`ub%Ib|IjaA&|L0Fk8T|4EFYsOH-47a zkJUrOvk!U?PhfVikITP!RLZv;0FVjx=!0-}1}_Z7un9FRrT`n3P^Fc8?~6i2I9`LJM3rle zL%;OsI5;>s3_|oD)6Dc%BNhEk00000 LNkvXXu0mjfOM{R^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/icon-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/icon-active.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c654e374b7c2043c962446068f4b86950d2949 GIT binary patch literal 517 zcmV+g0{Z=lP)Px$zez+vR9J=Ol|M)VVHn1r8XKA-9PEY&M9@W=a%m65^+KqrsYzNJbBdN)bc*As zF==uMT5M{Nd{GWuG9ig;acJym>Eyi5)6Tmy=w0qU`0o2X-}C-C;J-!dWR-J;vv*Fr zh{Y)3$-QG>@DLSp6wcl`*;46DLFf+HZ{EJS!{gd%*vL`1^X4R@DZ+8^NCb?_c_ae7 z;P&TS;GqhbHs{WDAZ_T@Y9zVv9bVtgEWi!E`ErCw;6TDpp9fY0euqpT0;Z|kTC)# z_Ns_57K@?Z?*jl*snkNLTw~Md4)tCmw^w-nEJMsT(u2HbI-Du^v3U~QF2Z{6cJ6)Gzz?`p?IJ>5*8~4| zYntYMW)yH>)H&e02&c6@$QbDyUDrduxjmdN3`Il%|8wyLF#qPx$iAh93R9J=WmoZ2JQ5?oU8y%V=9P9=OM7Ud|DVKIZTn(Y7rY31^%qd!IIj4}L z#-hn3XtAl`=pM>}OP7$uwKz2Pwdmnp)}@?xQhn16|9ijh{r~TK@PNOj_R%UMg|k=V zxQV4O;hCL7U~m@}(hX;?Mz%}zj$rN`7~gs4@&@;7$MZ(I;ohB-4CfFo96U4w|8Vcj z{osMba8lGrGB(s)4NQV>JRQIf9EcV!YQI0{rAnQV!dnk7tg6kaqZWvS5Ui?AKEJ-P zRH?JkXoLeE!EM{75Q3vl1huwrR)r8uBogTN`v3r0mSa`5NyD_9nv0XYnKS?GMdXEZ zvkRt78m0v$En$DR2qi6{RH@J0kuME+;kvFvUdce%9ptld6mm(No?W4kOCq0*!&tr| zQ|8o0Cbn)8yj(6jwbJ?)bA@dvX$gjDp>=(Z$9EON?m!t7ry9qnVn6t}iP5*!y`A|C zxPJU8JV-fv$TpdWnto$mku002ovPDHLk FV1gaA(C+{M literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-left-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-left-active.png new file mode 100644 index 0000000000000000000000000000000000000000..fe7a0f1c5a95901ba653308ee5b95972dcec2d53 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|)_S@)hE&XX zd+RK3vw{R$!W#j;i=x$KuI&Y!)k+Ujx&%+1|2)S)?Zo#cxd{Rc3^OhVeNOznZQjPe z|71?D|K7GC|Mg6<%Bz0s+b2X{2~fPFo7Tb5HtS?eSN)D3nTLCp@h^~GnzrhS`O~i@ z?TQiWfEF?^oPG7kzUF#5`=h#lch`Pecb+dswyR>*`LwAr8J`ueNF;1lydo0;Bur`< nRNk#=Q`&$0|H0KjeNTQc-I&x7K2bR3J;)eOS3j3^P6;D);Y?#p^5d+{v$32JIcA#nhb$D zZ!AwdeQa9grd{>>?AkBv($o2za#x}D_q#%IyJn$@>lyBDS~p2R=ucFvIA;f^-LpA! zx_Hf`e!TotF}?S(#Fcq!ieLV`n)2dsY)NPQ8#~6c8ef-q)k^X2)X(5quMIRF1VUDK zu3WzCI^T=`f3Czo^<6LfLAR^p;*(pAQ&twvUp8ri^^dKupLjWaV|2~;V%HYjX!-o- zU6nie?vJ;H@(OO;H8pSbt91>%OZV`)E<66eaUIA=g=5?iXO2go-*;;oi0kR<=d#Wz Gp$PyCREpF9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-right-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/inhand-right-active.png new file mode 100644 index 0000000000000000000000000000000000000000..07b0a660fd5f0dc952930e344bc78b352e8d1206 GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Hh8)?hE&XX zd&`@v*+7K#Lb~`vrCmq+?k?(nq4s8lLQ_)n#-DSHgB9`y!G&gsn&;{@8u(6A_`PHaK@X13jRx7zJD`i1U)yq+uOX=Cpk$8Pe_i#tS9{*EU$&6=UaR|Tr~?4KJSQ(zjCW(!H0dqSy@dd zs(u`8dp@sX?;F=8Z+b62d->Z=&pET)$urw`(N8v&V5jutx^=rR94ho?0y+!?Qc^!p z4CnrD8u7J^?|lsngX59;7~2M>ca8p)wibK09oVd L>gTe~DWM4f0?mp4 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/meta.json b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/meta.json new file mode 100644 index 0000000000..10bfa9bbca --- /dev/null +++ b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/golden_rod.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Goonstation, taken at commit 39ddf6bbd54c9f27fe49f5cea1a3119738b09596, modified by rouden_ (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-left-active", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-right-active", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "icon-active" + } + ] +} diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon-active.png new file mode 100644 index 0000000000000000000000000000000000000000..b05d89453bf88122724aa9f04c6d33783b31c2fc GIT binary patch literal 591 zcmV-V0_mw%gns@rqAd7 ze(yJvkADv40!?K_gkFP*2adS_g*)uEIFChv#OD|@feifW`UdxpZ_+pu0Xh68^(dvE zi9j}dY<>}Lw_BRGR6uFxE|-fi5m15?A+pz5B9N=*mJ6uC>Fzlpva?{Sz;AG+cT5w= zRdY4CX#z^EMo)V#uxSEH@TJLd+Up!Ngdu?(eXa>^NI2(@Z{Ys{7;4Gu6oAI@*MU`%W=}{#@^uA%Jp*DEAV>s!>+nb7q8yz!B%lVDuZdsnaPWZyWX~QzH~fcy z7Wg-bKZMuoRTj_#Hxe_mt3L%;!R6un>Ksvk0$IUn>3Ca*)V87k1s-mLWbR>c$j^^- d23UZV;scrjs4k@n2uuI~002ovPDHLkV1jH#0fYbm literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7a014e6f889858c03e9a220bff18c828b95bc815 GIT binary patch literal 559 zcmV+~0?_@5P)Px$=}AOER9J=Olszm1VI0Pv!$4^aCpd{44rw&0!Hp_NB*Y+HNNSKyCW+)MMuRjl zU~b2Yu!=!C8W;>Us7M+maZ>fs2C4Xn;cvRrHdp=dUMulTckjLT{+|EW3;Z|90#CBQ zlm1>)Y4}i|6K&1)##+S;=M7h;Y7;dTAqPi_`>Q&)^|%YU=}$M z5yx=`*D?dezyXU&@Keom#XJM~;AYV?H&7~^seybo2Y`HVfKd;ajhY_yPjDuZ1)h|# zL7H?*Bnv#Ls+x7z7t*iX6+3E{7+?Vh%Itws=A5YkuM(tkMvaNQN{|ju?#Ukr84ag{ z^>MRQvcQu+5TZotmAp!jR8?gHE-Gc6lAES_IK-~wXdGX{yw8JyzE&pbE_g_%G+x3|3cnhZo(FT9G1Z7WQOX{)|E z!68UiVC9xC#phT!81L=Yo$SQGkPxuEGWy8Vs`|6LAO74?Yg7F;Mtm9DW3}tw9zWL0 z$z8QtAZYeWrG|BGMcQZI-}$p4QLl}Ef#gm%t>f9ImgjHB&Rhqyl7V4r)+6hhJ*V#6 z+0T5ss_s=#WzAjt@0Lf(4_1rZ_u;VQUTfDP#2O~;bn?(D-i1pZGAm3nlHOD!XWtag e$iVO;mLorat2L@=^V;7a!#rL6T-G@yGywoTuxsG} literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-left.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..de26c62d813257d3f49e8f50b82832b64ad00324 GIT binary patch literal 429 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R5c zr;B4q#hkaZcIO>7;Bn1wBY?Y?sem>u=6`m!>kXwM|&IZOQ4dXSNNAk(Hgt z7Ya4lC|C+LJn>+dq|E5i#{%RC{7gK*e0z7l{rUC&+49LkY6qCVJb3eK`NvPQd^;M9 zepJrWzie`sX-nn&&4o)?b~W5@D$iLrrGa4u(>1pDQ-lNh#R7glY2Y*i<+=dsW= zyZxeF4NvF(w=>)SFYdt_+eRgYPw8u>CjR5txTH+{z^_%)q_gxhst;&nUWjGT{*V}y zV4}isaw3C@JJSRiPKP2)4iCfTa)mWkIahS%-L)2ol3f>Rv%9<@PBt!Ds{Y8{dyR2I zFRmt9Z)bVGK_wuox$K?fp3QG$4f>vce`X}J)FiF@&LxqJt)KozMQ3Vg3S7MT@Xv0M oAKqD)63v;ig5dG+x3|3cnhZo(14`EF9kwvgJG@s* zz;~9Kz~z7(`R6=8*e;bvt1V?_U|=tuWj6iwU(@-$^ZIYs=*~JSby@myVU?U~-1V&9 zi%uG=)Pp#Vmb!2)oFU-0w{^kcJ~;r5l2 zoIgF?Z+ZRw;9q~@bFM!4B73(bVDrMW?Hk;gXY(eWZ!?v2n!ML3^_zjR2vfvJoZ%gyQC;=s_{Qi zmJ+Vu&g9U?(jddhU@62LcwRb-hUo&T(`@zG@E%(MmE`0xc z;kwt|Qx|(mw0~by_y4=-2JVTfKCR~etPZXadUiK92hZi-il5Uv_<0Xkz722l zWBqw=Jtel6YVG)3CU;@$c7eYKL>{U+?Xed(8QskQk>&df>$fl?B$G_8XSX zefX;(Mup*t2g8Yp3=@;dN4~t(}79DmI zWL$cvTGn0fa=uuA(VpTbajm{gHh&iX+>%@-Yd3wdN=_Zq8b^EfZwY%67f-n}#h0m1 z;@LNjxs!As+`7~5FFVdQ I&MBb@09L54a{vGU literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/meta.json b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/meta.json new file mode 100644 index 0000000000..979f05fa92 --- /dev/null +++ b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/goon_rod.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "license": "CC-BY-NC-SA-3.0", + "copyright": "Goonstation, taken at commit 39ddf6bbd54c9f27fe49f5cea1a3119738b09596", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-left-active", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "inhand-right-active", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "icon-active" + } + ] +} diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/icon.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..042c32df9f02c8f4561f1a3881851b8991919a9e GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}Q#@T9Ln2z= zUNPi4WFT?$ojN2f$zNcKh;&o z{S)ADaB=avT#{Ki@96XXS*Md_q#0u3->cnyVZz7|aB9`u3ErBD$2LBlb5wejI)`F{ z#JLq)Z?XKbeEx>%*FTo`dt)6mwcnjStCw(lUxm%&ScZnf`|6MUXAl&;$j@b?lDR~O RwF~Gz22WQ%mvv4FO#t){R`&n^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/meta.json b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/meta.json new file mode 100644 index 0000000000..8f15d47712 --- /dev/null +++ b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Sprites by arraydeess (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "rope" + } + ] +} diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/rope.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_lure.rsi/rope.png new file mode 100644 index 0000000000000000000000000000000000000000..2662f7bfdc70c2834a346175cb5c3d84989d7e82 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}KAtX)ArY-_ zFK^^zP~dU7n6Px&gg5q$yKh)bgTe~DWM4fYUVI> literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon-active.png new file mode 100644 index 0000000000000000000000000000000000000000..e2a38566c6cde957bddb94857aee2ce77c1d5b26 GIT binary patch literal 430 zcmV;f0a5;mP)Px$Xh}ptR9J=Wm9a|0P!xv$+eD+yQ2GFFIygyj5*KmN!PPrCxz?c_e2+^Vh0;wF z$4m}Rc5|^ymx4&qL8{O}0%|xNBvI>4nxr`yq(6i}&JEvpPHqxlmRbILlmsamUN3OM zdtHYfUV+Xd=x~#hq=}NCm#1s$bsf69J*Uglnsyz_1pLmA_qWlxxe@@dQrXByKuJ)! zR7CZ}!TQ#&a9WMX^_dDNiC>-X92}w925j4gWm&ueK78l`hJbKd4J@zj1$EOjwfE_- zP6_u2XtuxWfX5c#g2xizgijRUhDQqI0S^_(3$7LT2?qd=Ks;!T zzt;@! Y5g>f;dLyqPO#lD@07*qoM6N<$f_1#Q8UO$Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cac4c84e5f43ad1edf6952725bb4e4545902d6 GIT binary patch literal 332 zcmV-S0ki&zP)Px$21!IgR9J=Wmc0%EK@>*M@G2UO7obvzLThUA0HRTOiD|SRK&?`!cma(@qo5;E zsbmylt)E@Y&RvP@mu$A!pL6b=y_R)y`<_dJl&qG+G^1s?;bH8|esy;dzBR zSs2dWKj%fj6%Gy`;p1E>zqv+Yj;zk?-~Yd{ z`1`a1xrbieXSQB`tvW?)YY0bqccfCo(y&ca9%+m3&z*7c8>7$St6p2HmhZ_fmuPwm zaukEX8qI%){cFn<>dx=~y|U)jX6BvFf?I#dgq`l>lXvRp03!p#0mHf9 zc+C7C<^KIv(=Tgn)>0`XuO=DIzrFp#%qgqtR3+X?uj0@=xn5R!5rgH)=W~LCww^mC zwBN4e=JPq;UFohKw)-tjZ+#H(eI(wl?_i!~yE1D2vErB9T_1Ka0o?=y4x4vLRK2Qt z*YNlJ{a>s1yxz&Y(NoZJ*Bt8&sTnrQRx3^Mt9xs;v@2kb^Tyem&UuB_o-F*gf#ZAk zWP>o{kgKFVdQ&MBb@0QJFt AsQ>@~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right-active.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right-active.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb4cd3bd1ad9adac8c25d6960a473289badfccb GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|R(rZQhE&XX zd&`ll*+78xLMH!9%PNuc7rk$}YIY=aI;zgxvprbx&(BB4RG1hV3~iN^o@q|I-+TRc zwf^k8lGpixysxEVw|D+KH)Czfs!F4&0k-bTgjQ)P{?cB!e9d%;eGR9-T*x{5{FhGb z4m}~Dg$xY7mVb2)aHR8=$=+($k66C?{>OCIcb^U%o9Z}Q|6yOl*=Yso8`Qir^aRXL le7n}Y?fCXhQ-QjAo-=z%WG?e5GM5J#;_2$=vd$@?2>>K`VPpUR literal 0 HcmV?d00001 diff --git a/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right.png b/Resources/Textures/_Goobstation/Objects/Specific/Fishing/makeshift_rod.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d70d19811ee4bf96159977bffded5d9e019d5891 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|-g~+@hE&XX zd&^enkb*$l!>e7w%eHL$qM^A~*Y;sqgoTAif(s9y{d?odPCe55=C&wlFflMRSjxJ% zTytF`z2naOuw|#2WN7IPwU)|1Yx3Q4ox*^*e6?tYE%H$M5*!J9elb%XMPp3mNQtn2$j<&dkps@_HU z>aO{}!!-2juEpQBPFVbsb*00#ZxtC^)IbK$IL~}|-pV)f@lXGNxSp