From c32824dfa0348f56ec9e736e9411eafd9e79233c Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Wed, 11 Feb 2026 22:10:36 +0300 Subject: [PATCH 01/20] =?UTF-8?q?=D0=BA=D0=BE=D1=80=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit добавлено весло и паруса --- .../Medieval/Ships/Oar/ServerOarSystem.cs | 36 ++++ .../Medieval/Ships/Sail/SailComponent.cs | 14 ++ .../Medieval/Ships/Sail/SailSystem.cs | 161 ++++++++++++++++++ .../Medieval/Ships/Wind/ServerWindSystem.cs | 13 ++ .../Medieval/Ships/Oar/OarComponent.cs | 19 +++ .../Imperial/Medieval/Ships/Oar/OarEvent.cs | 12 ++ .../Imperial/Medieval/Ships/Oar/OarSystem.cs | 134 +++++++++++++++ .../Medieval/Ships/Wind/SharedWindSystem.cs | 151 ++++++++++++++++ .../Skills/Systems/SharedSkillsSystem.cs | 7 + .../Imperial/Medieval/Ships/oar.yml | 32 ++++ .../Imperial/Medieval/Ships/Oar.rsi/base.png | Bin 0 -> 1017 bytes .../Imperial/Medieval/Ships/Oar.rsi/meta.json | 15 ++ 12 files changed, 594 insertions(+) create mode 100644 Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Oar/OarEvent.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs create mode 100644 Resources/Prototypes/Imperial/Medieval/Ships/oar.yml create mode 100644 Resources/Textures/Imperial/Medieval/Ships/Oar.rsi/base.png create mode 100644 Resources/Textures/Imperial/Medieval/Ships/Oar.rsi/meta.json diff --git a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs new file mode 100644 index 00000000000..b10a30cf05b --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs @@ -0,0 +1,36 @@ +// using System.Numerics; +// using Content.Server.Administration.Logs; +// using Content.Shared.Database; +// using Content.Shared.DoAfter; +// using Content.Shared.Imperial.Medieval.Ships.Oar; +// using Content.Shared.Imperial.Medieval.Skills; +// using Content.Shared.Interaction; +// using Content.Shared.Popups; +// using Robust.Shared.Map; +// using Robust.Shared.Physics.Components; +// using Robust.Shared.Physics.Systems; +// using Robust.Shared.Serialization; +// +// namespace Content.Server.Imperial.Medieval.Ships.Oar; +// +// /// +// // /// This handles... +// /// +// public sealed class ServerOarSystem : EntitySystem +// { +// [Dependency] private readonly SharedPopupSystem _popup = default!; +// [Dependency] private readonly SharedPhysicsSystem _physics = default!; +// [Dependency] private readonly SharedTransformSystem _transform = default!; +// [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; +// [Dependency] private readonly SharedSkillsSystem _skills = default!; +// [Dependency] private readonly IAdminLogManager _adminLog = default!; +// +// public override void Initialize() +// { +// +// } +// +// +// +// +// } diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs new file mode 100644 index 00000000000..631cf37f887 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Imperial.Medieval.Ships.Sail; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class SailComponent : Component +{ + [DataField("SailSize")] + public int SailSize = 1; + + [DataField("Folded")] + public bool Folded; +} diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs new file mode 100644 index 00000000000..23c5be2cd95 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -0,0 +1,161 @@ +using System.Numerics; +using Content.Shared._RD.Weight.Components; +using Content.Shared._RD.Weight.Systems; +using Content.Shared.DoAfter; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Imperial.Medieval.Ships.Wind; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Popups; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Imperial.Medieval.Ships.Sail; + +/// +/// This handles... +/// +public sealed class SailSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly RDWeightSystem _rdWeight = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + private const float DefaultReloadTimeSeconds = 1f; + private TimeSpan _nextCheckTime; + + private int _windForce; + private int _windAngle; + /// + public override void Initialize() + { + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); + + // Плавные изменения ветра (0–10) + if (_windForce <= 0) + _windForce += _random.Next(0, 2); + else if (_windForce >= 10) + _windForce -= _random.Next(0, 2); + else + _windForce += _random.Next(-1, 2); + + // Угол ветра: -45° до +45° (в градусах) — реалистично для моря + _windAngle += _random.Next(-5, 6); // ±5 градусов за шаг + _windAngle = Math.Clamp(_windAngle, -45, 45); + + // --- Обработка каждого паруса --- + foreach (var sailComponent in EntityManager.EntityQuery()) + { + if (sailComponent.Folded) + continue; + + var sailEntity = sailComponent.Owner; + var boat = _transform.GetParentUid(sailEntity); + if (!EntityManager.TryGetComponent(boat, out PhysicsComponent? boatBody)) + continue; + + // Получаем углы в радианах + float sailAngleRad = (float)_transform.GetWorldRotation(sailEntity).Theta; + float boatAngleRad = (float)_transform.GetWorldRotation(boat).Theta; + float windAngleRad = _windAngle * MathF.PI / 180f; // ветер в радианах + + // Направление ветра как вектор + Vector2 windDirection = new Vector2( + MathF.Cos(windAngleRad), + MathF.Sin(windAngleRad) + ); + + // Нормаль паруса — перпендикуляр к его поверхности (наружу) + // Парус — плоскость, нормаль — направление, в которое он "ловит" ветер + var sailNormalAngleRad = sailAngleRad + MathF.PI / 2f; + var sailNormal = new Vector2( + MathF.Cos(sailNormalAngleRad), + MathF.Sin(sailNormalAngleRad) + ); + + // Угол между ветром и нормалью паруса + var dot = Math.Clamp(Vector2.Dot(windDirection, sailNormal), -1f, 1f); + var angleBetween = MathF.Acos(dot); + + // Эффективность: максимальна, когда ветер перпендикулярен парусу (0°) + // Минимальна, когда ветер параллелен (90°+) + var efficiency = MathF.Cos(angleBetween); + + // Если ветер дует сзади (угол > 90°), всё равно даём слабую тягу + // (реальные паруса могут работать и на "бейсинге") + if (angleBetween > MathF.PI / 2f) + efficiency = MathF.Max(0.05f, efficiency); // 5% тяги сзади + else + efficiency = MathF.Max(0f, efficiency); + + // Сила ветра = базовая сила × площадь × эффективность + var forceMagnitude = _windForce * sailComponent.SailSize * efficiency; + + // Направление силы — вдоль нормали паруса + var forceDirection = sailNormal * forceMagnitude; + + // Крутящий момент: сила × плечо × sin(разница между парусом и кораблём) + // Плечо — расстояние от центра корабля до центра паруса (условное) + var sailOffset = 0.5f; // метры — можно настроить в компоненте + var torqueFactor = MathF.Sin(sailAngleRad - boatAngleRad); // как сильно парус "выступает" вбок + var torque = forceMagnitude * sailOffset * torqueFactor * 0.1f; // масштабируем + + var windEffect = new WindEffect + { + PushForce = forceDirection, + RotationTorque = torque + }; + + Push(sailEntity, sailComponent, windEffect); + } + } + } + + private void Push(EntityUid sail, SailComponent sailComponent, WindEffect windForce) + { + var boat = _transform.GetParentUid(sail); + + var entities = _lookup.GetEntitiesIntersecting(boat); + + if (entities.Count > 1000) + return; + + var weight = _rdWeight.GetTotal(boat); + + foreach (var entity in entities) + { + if (HasComp(entity)) + weight += _rdWeight.GetTotal(entity); + } + + if (weight == 0) + weight = 10; + var impulse = windForce.PushForce; + var angleimpulse = windForce.RotationTorque; + if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) + { + _physics.WakeBody(boat); + _physics.ApplyLinearImpulse(boat, impulse, body: body); + _physics.ApplyAngularImpulse(boat, angleimpulse); + } + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs new file mode 100644 index 00000000000..aad8226fda2 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Imperial.Medieval.Ships.Wind; + +/// +/// This handles... +/// +public sealed class ServerWindSystem : EntitySystem +{ + /// + public override void Initialize() + { + + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs new file mode 100644 index 00000000000..d54ee8f8d47 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs @@ -0,0 +1,19 @@ +using System.Numerics; + +namespace Content.Shared.Imperial.Medieval.Ships.Oar; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class OarComponent : Component +{ + [DataField] + public int Power = 200; + + [DataField] + public int SpeedModifier = 1; + + [DataField] + public Vector2 Direction; +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarEvent.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarEvent.cs new file mode 100644 index 00000000000..581fdef3718 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarEvent.cs @@ -0,0 +1,12 @@ + +using Content.Shared.DoAfter; + +using Robust.Shared.Serialization; + +namespace Content.Shared.Imperial.Medieval.Ships.Oar; + + +[Serializable, NetSerializable] +public sealed partial class OnOarDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs new file mode 100644 index 00000000000..e0f0164ece7 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs @@ -0,0 +1,134 @@ +using System.Numerics; +using Content.Shared._RD.Weight.Components; +using Content.Shared._RD.Weight.Systems; +using Content.Shared.Coordinates; +using Content.Shared.DoAfter; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + + +using Content.Shared.Database; +using Content.Shared.Hands.EntitySystems; + + +namespace Content.Shared.Imperial.Medieval.Ships.Oar; + +/// +/// This handles... +/// +public sealed class OarSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly RDWeightSystem _rdWeight = default!; +public override void Initialize() + { + SubscribeLocalEvent(OnOarAfterInteract); + SubscribeLocalEvent(OnOarDoAfter); + } + + private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) + { + var playerEntity = args.User; + + if (args.Handled || !args.CanReach ) + return; + + bool foundWater = false; + foreach (var entity in _lookup.GetEntitiesInRange(args.ClickLocation, 0.3f)) + { + if (MetaData(entity).EntityPrototype?.ID == "MedievalWaterDeepDarkEntity") + { + foundWater = true; + break; + } + } + if (!foundWater) + return; + + var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new OnOarDoAfterEvent(), + args.Target, + target: args.Used) + { + MovementThreshold = 0.5f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + }; + if (!_doAfter.TryStartDoAfter(sdoAfter)) + return; + + var playerPosition = _transform.GetWorldPosition(playerEntity); + var boatPosition = _transform.ToWorldPosition(args.ClickLocation); + var direction = (playerPosition - boatPosition).Normalized(); + component.Direction = direction; + args.Handled = true; + + } + + private void OnOarDoAfter(EntityUid uid, MetaDataComponent component, OnOarDoAfterEvent args) + { + var item = _hands.GetActiveItem(args.User); + if (args.Cancelled || args.Handled || item == null) + return; + + if (!TryComp(item, out var comp)) + return; + + Push(item.Value, comp.Direction, comp.Power, args.User); + args.Handled = true; + } + + private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) + { + power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; + + var boat = _transform.GetParentUid(player); + + var entities = _lookup.GetEntitiesIntersecting(boat); + + if (entities.Count > 1000) + return; + + var weight = _rdWeight.GetTotal(boat); + + foreach (var entity in entities) + { + if (HasComp(entity)) + weight += _rdWeight.GetTotal(entity); + } + + if (weight == 0) + weight = 10; + var impulse = direction * (power / weight); + var angleimpulse = (power / weight); + if (direction.X < 0) + angleimpulse = -angleimpulse; + + if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) + { + _physics.WakeBody(boat); + _physics.ApplyLinearImpulse(boat, impulse, body: body); + _physics.ApplyAngularImpulse(boat, angleimpulse); + } + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs new file mode 100644 index 00000000000..6f8990921b8 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs @@ -0,0 +1,151 @@ +using System.Numerics; +using Robust.Shared.Maths; + +namespace Content.Shared.Imperial.Medieval.Ships.Wind; + +/// +/// This handles wind force calculations for sails. +/// +public sealed class SharedWindSystem : EntitySystem +{ + /// + public override void Initialize() + { + // Initialization logic here if needed + } + + /// + /// Calculates the aerodynamic forces acting on a sail due to wind. + /// + /// World position of the ship. + /// Current rotation of the ship in radians. + /// Angle of the sail relative to the ship's orientation (radians). + /// Effective area of the sail (m²). + /// Current time for dynamic wind simulation. + /// Density of air (default: 1.2 kg/m³). + /// A containing force, torque, and wind data. + public static WindEffect CalculateWindForce( + Vector2 shipPosition, + float shipRotation, + float sailAngle, + float sailArea, + float time = 0f, + float airDensity = 1.2f) + { + // Get wind vector at ship's position + var windVector = GetWindField(shipPosition, time); + var windStrength = windVector.Length(); + var windDirection = windVector.Normalized(); + + // Absolute sail angle in world coordinates + var absoluteSailAngle = shipRotation + sailAngle; + + // Angle between wind direction and sail normal + var windAngle = MathF.Atan2(windDirection.Y, windDirection.X); + var angleDiff = absoluteSailAngle - windAngle; + var angleOfAttack = NormalizeAngle(angleDiff); + + // Lift and drag coefficients based on angle of attack + var liftCoefficient = MathF.Sin(2f * angleOfAttack); + var dragCoefficient = MathF.Abs(MathF.Cos(angleOfAttack)); + + // Dynamic pressure + var dynamicPressure = 0.5f * airDensity * windStrength * windStrength; + + // Force magnitudes + var liftForce = liftCoefficient * dynamicPressure * sailArea; + var dragForce = dragCoefficient * dynamicPressure * sailArea; + + // Sail orientation vectors + Vector2 sailDirection = new(MathF.Cos(absoluteSailAngle), MathF.Sin(absoluteSailAngle)); // Along sail + Vector2 sailNormal = new(-MathF.Sin(absoluteSailAngle), MathF.Cos(absoluteSailAngle)); // Perpendicular to sail + + // Force components + var liftVector = sailNormal * liftForce; + var dragVector = sailDirection * dragForce; + + // Total force + var totalForce = liftVector + dragVector; + + // Torque calculation + var leverArm = 1f; // Distance from center of mass to sail (can be parameterized) + var torque = CalculateTorque(totalForce, absoluteSailAngle, leverArm); + + return new WindEffect + { + PushForce = totalForce, + RotationTorque = angleDiff, + WindStrength = windStrength, + WindDirection = windDirection + }; + } + + /// + /// Normalizes an angle to the range [-π, π] + /// + private static float NormalizeAngle(float angle) + { + while (angle > MathF.PI) + angle -= MathF.Tau; + while (angle < -MathF.PI) + angle += MathF.Tau; + return angle; + } + + /// + /// Simple procedural wind field simulation + /// + private static Vector2 GetWindField(Vector2 position, float time = 0f) + { + var x = position.X; + var y = position.Y; + + var u = MathF.Sin(x / 2f + time) * MathF.Cos(y / 3f) + 0.5f * MathF.Sin(x / 5f + y / 7f); + var v = MathF.Cos(x / 3f + time) * MathF.Sin(y / 2f) + 0.5f * MathF.Cos(x / 7f - y / 5f); + + return new Vector2(u, v); + } + + /// + /// Calculates rotational torque applied by wind force + /// + private static float CalculateTorque(Vector2 force, float sailAngle, float leverArm) + { + if (force.LengthSquared() < 0.001f) + return 0f; + + var forceDirection = force.Normalized(); + Vector2 leverDirection = new(-MathF.Sin(sailAngle), MathF.Cos(sailAngle)); + + // Cross product in 2D: F × r + var crossProduct = forceDirection.X * leverDirection.Y - forceDirection.Y * leverDirection.X; + + return force.Length() * leverArm * crossProduct; + } +} + +/// +/// Represents the effect of wind on a sail. +/// +public struct WindEffect +{ + /// + /// Net force applied to the ship in world coordinates. + /// + public Vector2 PushForce; + + /// + /// Rotational torque around the ship's center. + /// + public float RotationTorque; + + /// + /// Magnitude of the wind vector at this point. + /// + public float WindStrength; + + /// + /// Direction of the wind (normalized). + /// + public Vector2 WindDirection; +} diff --git a/Content.Shared/Imperial/Medieval/Skills/Systems/SharedSkillsSystem.cs b/Content.Shared/Imperial/Medieval/Skills/Systems/SharedSkillsSystem.cs index 02493f6648a..9250b2de118 100644 --- a/Content.Shared/Imperial/Medieval/Skills/Systems/SharedSkillsSystem.cs +++ b/Content.Shared/Imperial/Medieval/Skills/Systems/SharedSkillsSystem.cs @@ -81,4 +81,11 @@ public static int GetPointsCost(int level) return sum; } + + public int GetSkillLevel(EntityUid uid, string skill) + { + if (!TryComp(uid, out var skillComponent)) + return 1; + return skillComponent.Levels.GetValueOrDefault(skill, 1); + } } diff --git a/Resources/Prototypes/Imperial/Medieval/Ships/oar.yml b/Resources/Prototypes/Imperial/Medieval/Ships/oar.yml new file mode 100644 index 00000000000..961cd197858 --- /dev/null +++ b/Resources/Prototypes/Imperial/Medieval/Ships/oar.yml @@ -0,0 +1,32 @@ +- type: entity + name: Весло + parent: BaseItem + id: MedievalOar + description: Обычное деревянное весло которое всем своим видом вселяет в тебя желание кого то ударить им. + components: + - type: QuestItem + contractName: "любое двуручное оружие" + - type: Sharp + - type: Sprite + sprite: Imperial/Medieval/Ships/Oar.rsi + state: base + - type: MeleeParry + parryChanse: 0.2 + - type: MeleeWeapon + animation: CP14WeaponArcThrust + wideAnimation: CP14WeaponArcSlash + cPSwingBeverage: true + cPAnimationOffset: 2 + clickDamageModifier: 1.32 + wideAnimationRotation: -140 + attackRate: 0.65 + range: 2.75 + angle: 0 + damage: + types: + Piercing: 0 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + soundSwing: + path: /Audio/Imperial/Medieval/spear_swing.ogg + - type: Oar \ No newline at end of file diff --git a/Resources/Textures/Imperial/Medieval/Ships/Oar.rsi/base.png b/Resources/Textures/Imperial/Medieval/Ships/Oar.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..c86bb651eea6f91cc352eaca16ae4b1b29c44d54 GIT binary patch literal 1017 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9F5M?jcysy3fA0|WDF zPZ!6KiaBrR_-BbYiX3wni0wOJv7nkcvZA?x{e+%*!A##ggVj45rKj&~wB{H1z^2Ev z%iMsia9Y=ePnI^9U%q>}fBoxf+d6v=;|VWct@^%t)w@^gR<#>Dx-y;cV36cgP-aX~ zY3N~@Fp`wpqU-NIv$0MOLb>zao&c6EFzxUDg^!1yz?ay5l zac3inyV-#g>>m!zEqGer%}9VL=h7k%4n>9@*&1biMWzzQ9R^8WHK&&PTU`H@Blu{Mk7&Zd^b%GJ^<9eF zSY|bR>y|v$uGx5tDPrsW4Ph73d~Zhtuo&!~r}So>#?A(hgXS}YCUH3^?q0Ldm-o}O zs+tKt4OeS)n0j{b@j4!#9K2?--%X)O<&LipbTdrcuBR!GBOw10p9mFvwHS$#Br zxOs6d=6tca(A8u@x%Tl0r8}$-Ccgi8dU18*{GhkyYeb)zs~_CW^*%jp9g{%Kp5sTQ zb?^8-=sW&x+6*NL#puWnotqwdu3FAw&iJ!;-h@Qyd+{-GzU#h8pQzray|vaPAWWZg zoi2mvktX}6`|SIt9zNA#^zE+OaxQVkYhOjot6yy@3=9cqtJ(SC?q+Q%odps#t2G1N z_r%^jyTamJ&T^faDlZ8gh5g6xEM&dEViH4Hs{FQ-R}vV_YRmDipV443lt`B!VpF8(eFQbcjonBAi#uuBF{*|L-oIi})m`!$Vi~JG-%*za( Lu6{1-oD!M Date: Thu, 12 Feb 2026 08:18:41 +0300 Subject: [PATCH 02/20] =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D0=B5=D0=BC=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit волны в процессе --- .../Medieval/Ships/Wave/WaveComponent.cs | 30 +++++ .../Medieval/Ships/Wave/WaveSystem.cs | 108 ++++++++++++++++++ .../Imperial/Medieval/Ships/Oar/OarSystem.cs | 2 +- 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs new file mode 100644 index 00000000000..7005d0d096a --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Damage; + +namespace Content.Server.Imperial.Medieval.Ships.Wave; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class WaveComponent : Component +{ + /// + /// Damage specifier that is multiplied against the calculated damage amount to determine what damage is applied to the colliding entity. + /// + /// + /// The values of this should add up to 1 or else the damage will be scaled. + /// + [DataField] + public DamageSpecifier DamageTypes = new(); + + /// + /// A list of entities that this meteor has collided with. used to ensure no double collisions occur. + /// + [DataField] + public HashSet HitList = new(); + + [DataField] + public float Strength = 1; + [DataField] + public float Direction = 1;; +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs new file mode 100644 index 00000000000..d800f6ac8dc --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -0,0 +1,108 @@ +using Content.Server.Destructible; +using Content.Shared.Construction.Conditions; +using Content.Shared.Damage; +using Content.Shared.Mobs.Systems; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Events; +using Robust.Shared.Player; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using Robust.Shared.Physics.Components; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.Imperial.Medieval.Ships.Wave; + +public sealed class WaveSystem : EntitySystem +{ + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly DestructibleSystem _destructible = default!; + [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; // <-- КЛЮЧЕВОЙ ИЗМЕНЕНИЕ + + private readonly Random _random = new(); + + public override void Initialize() + { + SubscribeLocalEvent(OnCollide); + } + + private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideEvent args) + { + if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(args.OtherEntity)) + return; + + // Пропускаем, если уже толкали эту сущность + if (component.HitList.Contains(args.OtherEntity)) + return; + + // --- 1. Толкаем сущность через PhysicsSystem --- + if (_entityManager.TryGetComponent(args.OtherEntity, out var physics)) + { + // Усиление толчка: 1.5x от силы волны + var force = component.Strength * 1.5f; + var impulse = component.Direction * force; + + // Правильный способ: через PhysicsSystem + _physics.ApplyImpulse(args.OtherEntity, impulse); + + // Визуальный эффект: "мокрый" + if (_entityManager.TryGetComponent(args.OtherEntity, out var appearance)) + appearance.SetState("wet", true); + } + + // --- 2. Проверка: тайл на корабле? --- + var otherPos = _entityManager.GetComponent(args.OtherEntity).Coordinates; + var tileRef = _mapManager.GetTileRef(otherPos); + + // Проверяем, что это корабельная сетка + if (!IsTileOnShip(tileRef.GridId)) + return; + + // Проверяем, что тайл — не дыра + if (_entityManager.TryGetComponent(tileRef.GridId, out var tileGrid)) + { + var tile = tileGrid.GetTile(tileRef.TilePosition); + + if (IsTileHole(tile)) + return; + + // Шанс дыры: 20% при силе 2, 60% при силе 10 + float chance = Math.Clamp((float)component.Strength / 10f * 0.6f, 0.2f, 0.6f); + if (_random.NextFloat() < chance) + { + MakeTileHole(tileRef.GridId, tileRef.TilePosition); + } + } + + // --- 3. Запоминаем удар --- + component.HitList.Add(args.OtherEntity); + } + + private bool IsTileOnShip(MapGridId gridId) + { + // Упрощённая проверка — замените на вашу логику (например, по компоненту ShipComponent) + return gridId.ToString().StartsWith("ShipGrid_"); + } + + private bool IsTileHole(Tile tile) + { + // Замените на ваш тип дыры, если используете кастомный TileType + return tile.Type == TileType.Hole; + } + + private void MakeTileHole(MapGridId gridId, Vector2i tilePos) + { + if (!_entityManager.TryGetComponent(gridId, out var tileComponent)) + return; + + tileComponent.SetTile(tilePos, new Tile(TileType.Hole)); + + // Опционально: звук + // _audio.PlayPvs("wave_hole.wav", gridId, tilePos); + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs index e0f0164ece7..0df7cb2eb35 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs @@ -32,7 +32,7 @@ public sealed class OarSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly RDWeightSystem _rdWeight = default!; -public override void Initialize() + public override void Initialize() { SubscribeLocalEvent(OnOarAfterInteract); SubscribeLocalEvent(OnOarDoAfter); From f4b1bd6d38a2deb5430ed7f8758806890d44daf1 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Thu, 12 Feb 2026 22:12:03 +0300 Subject: [PATCH 03/20] =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BD=D1=8B=20=D1=83?= =?UTF-8?q?=D1=80=D0=B0=D0=B0=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ура волнам --- .../Ships/Wave/Spawn/SpawnWaveComponent.cs | 10 ++ .../Ships/Wave/Spawn/SpawnWaveSystem.cs | 36 +++++ .../Medieval/Ships/Wave/WaveComponent.cs | 2 +- .../Medieval/Ships/Wave/WaveSystem.cs | 142 +++++++++++------- .../Imperial/Medieval/Ships/wave.yml | 57 +++++++ 5 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs create mode 100644 Resources/Prototypes/Imperial/Medieval/Ships/wave.yml diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs new file mode 100644 index 00000000000..54e3d9b996f --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Imperial.Medieval.Ships.Wave; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class SpawnWaveComponent : Component +{ + +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs new file mode 100644 index 00000000000..83ecc4f9371 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs @@ -0,0 +1,36 @@ +using Content.Shared.Damage; +using Robust.Server.GameObjects; +using Robust.Shared.Map; + +namespace Content.Server.Imperial.Medieval.Ships.Wave; + +/// +/// Спавнит волны, щиииииткод +/// +public sealed class SpawnWaveSystem : EntitySystem +{ + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnInit); + } + + private void OnInit(EntityUid uid, SpawnWaveComponent component, ComponentInit args) + { + var entcoords = _transform.GetMoverCoordinates(uid); + var mapcoords = _transform.GetMapCoordinates(uid); + var grid = _mapManager.CreateGridEntity(mapcoords.MapId); + _transform.SetCoordinates(grid, entcoords); + EnsureComp(grid); + _tileDefinitionManager.TryGetDefinition("FloorWood", out var tileDefinition);// сюда поставить воду + if (tileDefinition == null) + return; + _map.SetTile(grid, new Vector2i(0,0),new Tile(tileDefinition.TileId, 0, 0));// создаёт тайлик воды надо поставить воду вон туда + _entityManager.DeleteEntity(uid); + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs index 7005d0d096a..1253e17b221 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs @@ -26,5 +26,5 @@ public sealed partial class WaveComponent : Component [DataField] public float Strength = 1; [DataField] - public float Direction = 1;; + public float Direction = 1; } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index d800f6ac8dc..20e05f39941 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -1,13 +1,22 @@ +using System.Linq; +using System.Numerics; using Content.Server.Destructible; using Content.Shared.Construction.Conditions; using Content.Shared.Damage; +using Content.Shared.Maps; using Content.Shared.Mobs.Systems; using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Tiles; +using Content.Shared.Trigger.Components; using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Physics; using Robust.Shared.Physics.Events; using Robust.Shared.Player; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Timing; using Robust.Shared.Physics.Components; using Robust.Shared.Random; @@ -18,91 +27,116 @@ namespace Content.Server.Imperial.Medieval.Ships.Wave; public sealed class WaveSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DestructibleSystem _destructible = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; - [Dependency] private readonly PhysicsSystem _physics = default!; // <-- КЛЮЧЕВОЙ ИЗМЕНЕНИЕ + [Dependency] private readonly PhysicsSystem _physics = default!; + [Dependency] private readonly FloorTileSystem _floorTileSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly TileSystem _tile = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + private readonly Random _random = new(); + public (string, ushort)[] Stages = + { + ("FloorWood", (ushort)1), + ("FloorSteel", (ushort)2), + ("Plating", (ushort)3), + ("FloorWhite", (ushort)4) + }; public override void Initialize() { SubscribeLocalEvent(OnCollide); + + foreach (var stage in Stages) + { + _tileDefinitionManager.TryGetDefinition(stage.Item1, out var tileDefinition); + if (tileDefinition == null) + continue; + var full = Stages.Index(); + + int index = 0; + foreach (var hah in full) + { + if (hah.Item2 == stage) + { + index = hah.Item1; + break; + } + } + Stages[index] = (stage.Item1, tileDefinition.TileId); + } } + private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideEvent args) { if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(args.OtherEntity)) return; - - // Пропускаем, если уже толкали эту сущность if (component.HitList.Contains(args.OtherEntity)) return; - // --- 1. Толкаем сущность через PhysicsSystem --- - if (_entityManager.TryGetComponent(args.OtherEntity, out var physics)) - { - // Усиление толчка: 1.5x от силы волны - var force = component.Strength * 1.5f; - var impulse = component.Direction * force; - - // Правильный способ: через PhysicsSystem - _physics.ApplyImpulse(args.OtherEntity, impulse); + var collisionPos = _transform.GetMapCoordinates(args.OurEntity); + var gridEntity = args.OtherEntity; + _popup.PopupEntity(Loc.GetString($"{gridEntity}"), uid); + if (!_entityManager.TryGetComponent(gridEntity, out var mapGridComp)) + return; + _popup.PopupEntity(Loc.GetString($"{gridEntity}"), uid); - // Визуальный эффект: "мокрый" - if (_entityManager.TryGetComponent(args.OtherEntity, out var appearance)) - appearance.SetState("wet", true); - } + var grid = new Entity(gridEntity, mapGridComp); + var tileRef = _map.GetTileRef(grid, collisionPos); - // --- 2. Проверка: тайл на корабле? --- - var otherPos = _entityManager.GetComponent(args.OtherEntity).Coordinates; - var tileRef = _mapManager.GetTileRef(otherPos); + var centerTilePos = tileRef.GridIndices; + const float radiusTiles = 1.5f; - // Проверяем, что это корабельная сетка - if (!IsTileOnShip(tileRef.GridId)) - return; - // Проверяем, что тайл — не дыра - if (_entityManager.TryGetComponent(tileRef.GridId, out var tileGrid)) + var nearbyTiles = new List(); + for (int dx = -2; dx <= 2; dx++) { - var tile = tileGrid.GetTile(tileRef.TilePosition); - - if (IsTileHole(tile)) - return; - - // Шанс дыры: 20% при силе 2, 60% при силе 10 - float chance = Math.Clamp((float)component.Strength / 10f * 0.6f, 0.2f, 0.6f); - if (_random.NextFloat() < chance) + for (int dy = -2; dy <= 2; dy++) { - MakeTileHole(tileRef.GridId, tileRef.TilePosition); + var tilePos = centerTilePos + new Vector2i(dx, dy); + var tile = _map.GetTileRef(grid, tilePos); + if (tile.Tile.IsEmpty) + continue; + + var distance = Vector2.Distance(centerTilePos, tilePos); + if (distance <= radiusTiles) + nearbyTiles.Add(tilePos); } } - // --- 3. Запоминаем удар --- - component.HitList.Add(args.OtherEntity); - } + _random.Shuffle(nearbyTiles); + int tilesToReplace = Math.Min(2, nearbyTiles.Count); - private bool IsTileOnShip(MapGridId gridId) - { - // Упрощённая проверка — замените на вашу логику (например, по компоненту ShipComponent) - return gridId.ToString().StartsWith("ShipGrid_"); - } - - private bool IsTileHole(Tile tile) - { - // Замените на ваш тип дыры, если используете кастомный TileType - return tile.Type == TileType.Hole; - } - private void MakeTileHole(MapGridId gridId, Vector2i tilePos) - { - if (!_entityManager.TryGetComponent(gridId, out var tileComponent)) - return; - tileComponent.SetTile(tilePos, new Tile(TileType.Hole)); + for (int i = 0; i < tilesToReplace; i++) + { + var tilePos = nearbyTiles[i]; + _map.TryGetTile(grid, tilePos, out var tile); - // Опционально: звук - // _audio.PlayPvs("wave_hole.wav", gridId, tilePos); + if (tile.TypeId == Stages[Stages.Length].Item2) + continue; + var index = 0; + foreach (var stage in Stages) + { + if (stage.Item2 == tile.TypeId) + break; + index++; + } + _map.SetTile(grid.Owner, grid , tilePos, new Tile(Stages[index+1].Item2, 0, 0)); + } + if (!TerminatingOrDeleted(args.OtherEntity)) + component.HitList.Add(args.OtherEntity); + _entityManager.DeleteEntity(uid); } } diff --git a/Resources/Prototypes/Imperial/Medieval/Ships/wave.yml b/Resources/Prototypes/Imperial/Medieval/Ships/wave.yml new file mode 100644 index 00000000000..fe3d5178da2 --- /dev/null +++ b/Resources/Prototypes/Imperial/Medieval/Ships/wave.yml @@ -0,0 +1,57 @@ +- type: entity + id: BaseWave + name: meteor + description: You prefer them when they're burning up in the atmosphere. + abstract: true + components: + - type: Sprite + noRot: false + sprite: Objects/Misc/meteor.rsi + - type: Projectile + damage: {} + deleteOnCollide: false + - type: SpawnWave + - type: TimedDespawn + lifetime: 120 + - type: Clickable + - type: Physics + bodyType: Dynamic + bodyStatus: InAir + angularDamping: 0 + linearDamping: 0 + - type: Fixtures + fixtures: + projectile: + shape: + !type:PhysShapeCircle + radius: 0.4 + density: 100 + hard: false + layer: + - MapGrid + mask: + - MapGrid + - Impassable + - type: Damageable + damageContainer: Inorganic + - type: TileFrictionModifier + modifier: 0 + +- type: entity + parent: BaseWave + id: WaveLarge + suffix: Large + components: + - type: Sprite + state: big + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 1200 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak \ No newline at end of file From cc4e761b11cb9ec3f66e0190a82de1b9a9ec8476 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sat, 14 Feb 2026 14:15:15 +0300 Subject: [PATCH 04/20] =?UTF-8?q?=D0=9C=D0=B0=D0=BF=D1=8B=20=D1=82=D0=BE?= =?UTF-8?q?=D0=BF=D1=8F=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit финал фикс волн и мапы топят --- .../Ships/PlayerDrowning/DrownerComponent.cs | 9 ++ .../PlayerDrowning/PlayerDrowningComponent.cs | 11 +++ .../PlayerDrowning/PlayerDrowningSystem.cs | 79 +++++++++++++++++ .../Ships/Wave/Spawn/SpawnWaveComponent.cs | 5 +- .../Ships/Wave/Spawn/SpawnWaveSystem.cs | 8 +- .../Medieval/Ships/Wave/WaveComponent.cs | 3 + .../Medieval/Ships/Wave/WaveSystem.cs | 85 +++++++++++-------- 7 files changed, 161 insertions(+), 39 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/PlayerDrowning/DrownerComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/DrownerComponent.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/DrownerComponent.cs new file mode 100644 index 00000000000..5372d9621df --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/DrownerComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Imperial.Medieval.Ships.PlayerDrowning; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class DrownerComponent : Component +{ +} diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs new file mode 100644 index 00000000000..1f01a73223a --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Imperial.Medieval.Ships.PlayerDrowning; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class PlayerDrowningComponent : Component +{ + [DataField("drownTime")] + public int DrownTime = 15; +} diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs new file mode 100644 index 00000000000..da4a2878c5d --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs @@ -0,0 +1,79 @@ +using Content.Shared.Damage.Components; +using Robust.Shared.Map.Components; +using Robust.Shared.Timing; + +namespace Content.Server.Imperial.Medieval.Ships.PlayerDrowning; + +/// +/// This handles... +/// +public sealed class PlayerDrowningSystem : EntitySystem +{ + private const float DefaultReloadTimeSeconds = 1f; + private const int DrownTimeMax = 15; + private TimeSpan _nextCheckTime; + + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EntityManager _entityManager = default!; + + /// + public override void Initialize() + { + base.Initialize(); + _nextCheckTime = _timing.CurTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); + + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); + + foreach (var component in EntityManager.EntityQuery()) + { + if (!TryComp(component.Owner, out var map)) + return; + + var drowningPlayers = new HashSet>(); + + _lookup.GetEntitiesOnMap(map.MapId, drowningPlayers); + + foreach (var entity in drowningPlayers) + { + var transformComp = entity.Comp; + var uid = entity.Owner; + ProcessDrowning(uid, transformComp); + } + } + } + } + + private void ProcessDrowning(EntityUid uid, TransformComponent transform) + { + if (!TryComp(uid, out var drowner)) + EnsureComp(uid); + else + { + if (TryComp(uid, out var stamina)) + { + if (stamina.Critical) + _entityManager.DeleteEntity(uid); + else + { + stamina.StaminaDamage += 1; + } + } + else if (drowner.DrownTime >= DrownTimeMax) + { + _entityManager.DeleteEntity(uid); + } + drowner.DrownTime += 1; + } + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs index 54e3d9b996f..14f7c13109e 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Imperial.Medieval.Ships.Wave; +namespace Content.Server.Imperial.Medieval.Ships.Wave.Spawn; /// /// This is used for... @@ -6,5 +6,6 @@ namespace Content.Server.Imperial.Medieval.Ships.Wave; [RegisterComponent] public sealed partial class SpawnWaveComponent : Component { - + [DataField] + public bool DeleteOnCollide = true; } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs index 83ecc4f9371..bb6fa428403 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWaveSystem.cs @@ -2,7 +2,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Map; -namespace Content.Server.Imperial.Medieval.Ships.Wave; +namespace Content.Server.Imperial.Medieval.Ships.Wave.Spawn; /// /// Спавнит волны, щиииииткод @@ -25,12 +25,14 @@ private void OnInit(EntityUid uid, SpawnWaveComponent component, ComponentInit a var entcoords = _transform.GetMoverCoordinates(uid); var mapcoords = _transform.GetMapCoordinates(uid); var grid = _mapManager.CreateGridEntity(mapcoords.MapId); - _transform.SetCoordinates(grid, entcoords); - EnsureComp(grid); + var waveComponent = EnsureComp(grid); + waveComponent.DeleteOnCollide = component.DeleteOnCollide; _tileDefinitionManager.TryGetDefinition("FloorWood", out var tileDefinition);// сюда поставить воду if (tileDefinition == null) return; _map.SetTile(grid, new Vector2i(0,0),new Tile(tileDefinition.TileId, 0, 0));// создаёт тайлик воды надо поставить воду вон туда + if (HasComp(grid)) + _transform.SetCoordinates(grid, entcoords); _entityManager.DeleteEntity(uid); } } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs index 1253e17b221..fbf46d35f98 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveComponent.cs @@ -27,4 +27,7 @@ public sealed partial class WaveComponent : Component public float Strength = 1; [DataField] public float Direction = 1; + + [DataField] + public bool DeleteOnCollide = true; } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index 20e05f39941..ef766a09a02 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -1,8 +1,10 @@ using System.Linq; using System.Numerics; +using Content.Server.Administration.Logs; using Content.Server.Destructible; using Content.Shared.Construction.Conditions; using Content.Shared.Damage; +using Content.Shared.Database; using Content.Shared.Maps; using Content.Shared.Mobs.Systems; using Content.Shared.Physics; @@ -41,6 +43,7 @@ public sealed class WaveSystem : EntitySystem [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IAdminLogManager _adminlogs = default!; private readonly Random _random = new(); @@ -51,92 +54,106 @@ public sealed class WaveSystem : EntitySystem ("Plating", (ushort)3), ("FloorWhite", (ushort)4) }; + private const float RadiusTiles = 2f; + private bool _initialized; public override void Initialize() { SubscribeLocalEvent(OnCollide); - foreach (var stage in Stages) + } + private void Startup() + { + if (_initialized) + return; + if (Stages == null || Stages.Length == 0) + { + Logger.Error("Stages is null or empty! Cannot initialize."); + return; + } + + // Используем for вместо foreach для изменения коллекции + for (int i = 0; i < Stages.Length; i++) { - _tileDefinitionManager.TryGetDefinition(stage.Item1, out var tileDefinition); - if (tileDefinition == null) + var stage = Stages[i]; + if (!_tileDefinitionManager.TryGetDefinition(stage.Item1, out var tileDefinition)) continue; - var full = Stages.Index(); - int index = 0; - foreach (var hah in full) - { - if (hah.Item2 == stage) - { - index = hah.Item1; - break; - } - } - Stages[index] = (stage.Item1, tileDefinition.TileId); + Stages[i] = (stage.Item1, tileDefinition.TileId); + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"Тайл айди {tileDefinition.TileId} для '{stage.Item1}'"); } + _initialized = true; } - private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideEvent args) { + if (!_initialized) + Startup(); if (TerminatingOrDeleted(uid) || TerminatingOrDeleted(args.OtherEntity)) return; if (component.HitList.Contains(args.OtherEntity)) return; - + EnsureComp(args.OurEntity); var collisionPos = _transform.GetMapCoordinates(args.OurEntity); var gridEntity = args.OtherEntity; - _popup.PopupEntity(Loc.GetString($"{gridEntity}"), uid); if (!_entityManager.TryGetComponent(gridEntity, out var mapGridComp)) return; - _popup.PopupEntity(Loc.GetString($"{gridEntity}"), uid); + var grid = new Entity(gridEntity, mapGridComp); var tileRef = _map.GetTileRef(grid, collisionPos); + var centerTilePos = _map.MapToGrid(grid, collisionPos); - var centerTilePos = tileRef.GridIndices; - const float radiusTiles = 1.5f; - - + var antiradius = (int)RadiusTiles*-1; var nearbyTiles = new List(); - for (int dx = -2; dx <= 2; dx++) + for (int dx = antiradius; dx <= RadiusTiles; dx++) { - for (int dy = -2; dy <= 2; dy++) + for (int dy = antiradius; dy <= RadiusTiles; dy++) { - var tilePos = centerTilePos + new Vector2i(dx, dy); + var tilePos = centerTilePos + new EntityCoordinates(gridEntity, new Vector2(dx, dy)) ; var tile = _map.GetTileRef(grid, tilePos); + if (tile.Tile.IsEmpty) continue; - var distance = Vector2.Distance(centerTilePos, tilePos); - if (distance <= radiusTiles) - nearbyTiles.Add(tilePos); + var distance = Vector2.Distance(centerTilePos.Position, tilePos.Position); + if (distance <= RadiusTiles) + nearbyTiles.Add(((int)tilePos.X, (int)tilePos.Y)); } } _random.Shuffle(nearbyTiles); - int tilesToReplace = Math.Min(2, nearbyTiles.Count); - - + int tilesToReplace = Math.Min(_random.Next(0,4), nearbyTiles.Count); + _popup.PopupEntity(Loc.GetString($" {tilesToReplace}"), uid); for (int i = 0; i < tilesToReplace; i++) { var tilePos = nearbyTiles[i]; - _map.TryGetTile(grid, tilePos, out var tile); + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"тайл поз {tilePos} грид {grid} {_map.TryGetTile(grid, tilePos, out var tiles)}"); + if (!_map.TryGetTile(grid, tilePos, out var tile)) + continue; + var stagelast = Stages.Length-1; + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"тайл айди {tile.TypeId} длина стейжеса {Stages.Length}"); - if (tile.TypeId == Stages[Stages.Length].Item2) + if (tile.TypeId == Stages[stagelast].Item2) continue; + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"грид2 {i}"); var index = 0; foreach (var stage in Stages) { + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"грид3 {stage}"); if (stage.Item2 == tile.TypeId) break; index++; } + if (index == stagelast+1) + index = 0; _map.SetTile(grid.Owner, grid , tilePos, new Tile(Stages[index+1].Item2, 0, 0)); + _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"индекс {index}"); } if (!TerminatingOrDeleted(args.OtherEntity)) component.HitList.Add(args.OtherEntity); - _entityManager.DeleteEntity(uid); + if (component.DeleteOnCollide) + _entityManager.DeleteEntity(args.OurEntity); } } From ccd239ca4b6bfaba2d412a4510031ff001867975 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sat, 14 Feb 2026 19:42:03 +0300 Subject: [PATCH 05/20] =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BD=D1=8B=20=D0=B8?= =?UTF-8?q?=20=D1=83=D1=82=D0=BE=D0=BF=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20?= =?UTF-8?q?=D1=84=D1=83=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlayerDrowning/PlayerDrowningComponent.cs | 5 ++- .../PlayerDrowning/PlayerDrowningSystem.cs | 41 ++++++++++++------- .../PlayerDrowning/UndrowableComponent.cs | 10 +++++ .../Medieval/Ships/Wave/WaveSystem.cs | 10 +---- 4 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/PlayerDrowning/UndrowableComponent.cs diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs index 1f01a73223a..282e8fea95e 100644 --- a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningComponent.cs @@ -7,5 +7,8 @@ namespace Content.Server.Imperial.Medieval.Ships.PlayerDrowning; public sealed partial class PlayerDrowningComponent : Component { [DataField("drownTime")] - public int DrownTime = 15; + public int DrownTime; + + [DataField("Undrowable")] + public bool Undrowable; } diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs index da4a2878c5d..1f0c1caa786 100644 --- a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs @@ -1,4 +1,6 @@ using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Timing; @@ -16,6 +18,8 @@ public sealed class PlayerDrowningSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedStaminaSystem _staminaSystem = default!; /// public override void Initialize() @@ -37,39 +41,46 @@ public override void Update(float frameTime) foreach (var component in EntityManager.EntityQuery()) { - if (!TryComp(component.Owner, out var map)) + if (!TryComp(component.Owner, out var transform)) return; - - var drowningPlayers = new HashSet>(); - - _lookup.GetEntitiesOnMap(map.MapId, drowningPlayers); - - foreach (var entity in drowningPlayers) + var childs = new List(); + var childEnum = transform.ChildEnumerator; + while (childEnum.MoveNext(out var child)) { - var transformComp = entity.Comp; - var uid = entity.Owner; - ProcessDrowning(uid, transformComp); + ProcessDrowning(child); } + } } } - private void ProcessDrowning(EntityUid uid, TransformComponent transform) + private void ProcessDrowning(EntityUid uid) { + if (HasComp(uid)) + return; + if (!TryComp(uid, out var drowner)) EnsureComp(uid); else { + if (drowner.Undrowable) + return; + if (HasComp(uid)) + return; + if (TryComp(uid, out var stamina)) { if (stamina.Critical) - _entityManager.DeleteEntity(uid); - else { - stamina.StaminaDamage += 1; + _entityManager.DeleteEntity(uid); + return; } + + if (_staminaSystem.TryTakeStamina(uid, 25, ignoreResist: true)) + return; + } - else if (drowner.DrownTime >= DrownTimeMax) + if (drowner.DrownTime >= DrownTimeMax) { _entityManager.DeleteEntity(uid); } diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/UndrowableComponent.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/UndrowableComponent.cs new file mode 100644 index 00000000000..8deb8fd34d2 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/UndrowableComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Imperial.Medieval.Ships.PlayerDrowning; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class UndrowableComponent : Component +{ + +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index ef766a09a02..fb279ed4aa2 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -67,10 +67,8 @@ private void Startup() if (_initialized) return; if (Stages == null || Stages.Length == 0) - { - Logger.Error("Stages is null or empty! Cannot initialize."); return; - } + // Используем for вместо foreach для изменения коллекции for (int i = 0; i < Stages.Length; i++) @@ -80,7 +78,6 @@ private void Startup() continue; Stages[i] = (stage.Item1, tileDefinition.TileId); - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"Тайл айди {tileDefinition.TileId} для '{stage.Item1}'"); } _initialized = true; } @@ -129,19 +126,15 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE for (int i = 0; i < tilesToReplace; i++) { var tilePos = nearbyTiles[i]; - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"тайл поз {tilePos} грид {grid} {_map.TryGetTile(grid, tilePos, out var tiles)}"); if (!_map.TryGetTile(grid, tilePos, out var tile)) continue; var stagelast = Stages.Length-1; - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"тайл айди {tile.TypeId} длина стейжеса {Stages.Length}"); if (tile.TypeId == Stages[stagelast].Item2) continue; - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"грид2 {i}"); var index = 0; foreach (var stage in Stages) { - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"грид3 {stage}"); if (stage.Item2 == tile.TypeId) break; index++; @@ -149,7 +142,6 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE if (index == stagelast+1) index = 0; _map.SetTile(grid.Owner, grid , tilePos, new Tile(Stages[index+1].Item2, 0, 0)); - _adminlogs.Add(LogType.Trigger, LogImpact.Extreme, $"индекс {index}"); } if (!TerminatingOrDeleted(args.OtherEntity)) component.HitList.Add(args.OtherEntity); From 8f2613b015b6790a921c24a6694a1c82e944c5cb Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:49:30 +0300 Subject: [PATCH 06/20] =?UTF-8?q?=D0=B2=D1=8B=D0=B3=D1=80=D1=83=D0=B6?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=20=D0=BA=D1=83=D1=87=D1=83=20=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit надо бы это делать по чаще.... --- .../Medieval/Ships/Helm/HelmSystem.cs | 76 +++++ .../Ships/Helm/SteeringOarComponent.cs | 10 + .../Medieval/Ships/Oar/ServerOarSystem.cs | 166 ++++++++--- .../PlayerDrowning/PlayerDrowningSystem.cs | 23 +- .../Medieval/Ships/Sail/SailSystem.cs | 175 ++++++----- .../ShipDrowning/ShipDrowningComponent.cs | 14 + .../Ships/ShipDrowning/ShipDrowningSystem.cs | 86 ++++++ .../Ships/SummonShip/SummonShipComponent.cs | 17 ++ .../Ships/SummonShip/SummonShipSystem.cs | 63 ++++ .../Medieval/Ships/Wave/WaveSystem.cs | 7 +- .../Ships/WeatherVane/WeatherVaneComponent.cs | 10 + .../Administration/Ships/ShipsCCVars.cs | 22 ++ .../Imperial/Medieval/Ships/Oar/OarSystem.cs | 271 +++++++++--------- .../Medieval/Ships/Sail/SailComponent.cs | 8 +- .../Medieval/Ships/Sail/SailUseEvent.cs | 11 + .../Medieval/Ships/Sail/SharedSailSystem.cs | 70 +++++ .../Medieval/Ships/Wind/SharedWindSystem.cs | 133 --------- .../Rotatable/RotatableComponent.cs | 6 +- .../Prototypes/Imperial/Medieval/decor.yml | 7 +- 19 files changed, 780 insertions(+), 395 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Helm/SteeringOarComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/WeatherVane/WeatherVaneComponent.cs create mode 100644 Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs rename {Content.Server => Content.Shared}/Imperial/Medieval/Ships/Sail/SailComponent.cs (60%) create mode 100644 Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs b/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs new file mode 100644 index 00000000000..f248a39cf0b --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs @@ -0,0 +1,76 @@ +using System.Numerics; +using Content.Server.Administration.Logs; +using Content.Shared._RD.Weight.Systems; +using Content.Shared.Database; +using Content.Shared.DoAfter; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Popups; +using NetCord; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Imperial.Medieval.Ships.Helm; + +/// +/// This handles... +/// +public sealed class HelmSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly RDWeightSystem _rdWeight = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + + public void RotateShip(EntityUid boat, EntityUid helm, float helmForce) + { + _physics.WakeBody(boat); + var helmAngle = _transform.GetWorldRotation(helm); + var entities = _lookup.GetEntitiesIntersecting(boat); + EntityUid? steeringOar = null; + foreach (var e in entities) + { + if (HasComp(e)) + { + steeringOar = e; + break; + } + } + if (steeringOar == null) + return; + var steeringOarAngle = _transform.GetWorldRotation(steeringOar.Value); + var diff = (float)steeringOarAngle*180 - (float)helmAngle*180; + diff *= -1; + if (helmForce > 0) + { + _physics.ApplyAngularImpulse(boat, diff); + return; + } + if (helmForce < 0) + { + _physics.ApplyAngularImpulse(boat, -diff); + return; + } + } + + public float CheckForce(EntityUid boat, EntityUid helm) + { + var boatAngle = (float)_transform.GetWorldRotation(boat)*180; + var helmcos = MathF.Cos(boatAngle); + var helmsin = MathF.Sin(boatAngle); + var helmVector = _physics.GetMapLinearVelocity(boat); + var helmForce = Math.Abs(helmVector.X) + Math.Abs(helmVector.Y); + _adminLog.Add(LogType.Action, LogImpact.Extreme, $"хельма {helmForce}"); + return helmForce; + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Helm/SteeringOarComponent.cs b/Content.Server/Imperial/Medieval/Ships/Helm/SteeringOarComponent.cs new file mode 100644 index 00000000000..16c657879f7 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Helm/SteeringOarComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Imperial.Medieval.Ships.Helm; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class SteeringOarComponent : Component +{ + +} diff --git a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs index b10a30cf05b..3266e03e801 100644 --- a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs @@ -1,36 +1,130 @@ -// using System.Numerics; -// using Content.Server.Administration.Logs; -// using Content.Shared.Database; -// using Content.Shared.DoAfter; -// using Content.Shared.Imperial.Medieval.Ships.Oar; -// using Content.Shared.Imperial.Medieval.Skills; -// using Content.Shared.Interaction; -// using Content.Shared.Popups; -// using Robust.Shared.Map; -// using Robust.Shared.Physics.Components; -// using Robust.Shared.Physics.Systems; -// using Robust.Shared.Serialization; -// -// namespace Content.Server.Imperial.Medieval.Ships.Oar; -// -// /// -// // /// This handles... -// /// -// public sealed class ServerOarSystem : EntitySystem -// { -// [Dependency] private readonly SharedPopupSystem _popup = default!; -// [Dependency] private readonly SharedPhysicsSystem _physics = default!; -// [Dependency] private readonly SharedTransformSystem _transform = default!; -// [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; -// [Dependency] private readonly SharedSkillsSystem _skills = default!; -// [Dependency] private readonly IAdminLogManager _adminLog = default!; -// -// public override void Initialize() -// { -// -// } -// -// -// -// -// } +using System.Numerics; +using Content.Shared._RD.Weight.Components; +using Content.Shared._RD.Weight.Systems; +using Content.Shared.Coordinates; +using Content.Shared.DoAfter; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + + +using Content.Shared.Database; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Imperial.Medieval.Ships.Oar; + + +namespace Content.Server.Imperial.Medieval.Ships.Oar; + +/// +/// This handles... +/// +public sealed class OarSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly RDWeightSystem _rdWeight = default!; + public override void Initialize() + { + SubscribeLocalEvent(OnOarAfterInteract); + SubscribeLocalEvent(OnOarDoAfter); + } + + private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) + { + var playerEntity = args.User; + + if (args.Handled || !args.CanReach ) + return; + + var boat = _transform.GetParentUid(playerEntity); + + if (boat == args.ClickLocation.EntityId) + return; + + var clickEntity = args.ClickLocation.EntityId; + if (boat == _transform.GetParentUid(clickEntity)) + return; + + var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new OnOarDoAfterEvent(), + args.Used, + args.Target, + args.Used) + { + MovementThreshold = 0.5f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + }; + _doAfter.TryStartDoAfter(sdoAfter); + var playerPosition = _transform.GetWorldPosition(playerEntity); + var boatPosition = _transform.ToWorldPosition(args.ClickLocation); + var direction = (playerPosition - boatPosition).Normalized(); + component.Direction = direction; + } + + private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) + { + var item = _hands.GetActiveItem(args.User); + if (args.Cancelled || args.Handled || item == null) + return; + + if (!TryComp(item, out var comp)) + return; + + Push(item.Value, comp.Direction, comp.Power, args.User); + args.Handled = true; + args.Repeat = true; + } + + private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) + { + power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; + + var boat = _transform.GetParentUid(player); + + var entities = _lookup.GetEntitiesIntersecting(boat); + + if (entities.Count > 1000) + return; + + var weight = _rdWeight.GetTotal(boat); + + foreach (var entity in entities) + { + if (HasComp(entity)) + weight += _rdWeight.GetTotal(entity); + } + + if (weight == 0) + weight = 10; + var impulse = direction * (power / weight); + var angleimpulse = (power / weight); + if (direction.X < 0) + angleimpulse = -angleimpulse; + + if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) + { + _physics.WakeBody(boat); + _physics.ApplyLinearImpulse(boat, impulse, body: body); + _physics.ApplyAngularImpulse(boat, angleimpulse, body: body); + } + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs index 1f0c1caa786..176bd6e6818 100644 --- a/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/PlayerDrowning/PlayerDrowningSystem.cs @@ -42,15 +42,26 @@ public override void Update(float frameTime) foreach (var component in EntityManager.EntityQuery()) { if (!TryComp(component.Owner, out var transform)) - return; - var childs = new List(); - var childEnum = transform.ChildEnumerator; - while (childEnum.MoveNext(out var child)) + continue; + var childBasement = transform.ChildEnumerator; + while (childBasement.MoveNext(out var childUid)) + EnsureComp(childUid); + } + + foreach (var component in EntityManager.EntityQuery()) + { + if (HasComp(_transform.GetParentUid(component.Owner))) { - ProcessDrowning(child); + ProcessDrowning(component.Owner); + continue; } + if (component.DrownTime > 0) + component.DrownTime -= 1; + else + RemComp(component.Owner); } + } } @@ -76,7 +87,7 @@ private void ProcessDrowning(EntityUid uid) return; } - if (_staminaSystem.TryTakeStamina(uid, 25, ignoreResist: true)) + if (_staminaSystem.TryTakeStamina(uid, 10, ignoreResist: true)) return; } diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index 23c5be2cd95..c6b8065f952 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -1,15 +1,25 @@ using System.Numerics; +using Content.Server.Administration.Logs; +using Content.Server.Imperial.Medieval.Ships.Helm; +using Content.Server.Imperial.Medieval.Ships.ShipDrowning; +using Content.Server.Imperial.Medieval.Ships.WeatherVane; using Content.Shared._RD.Weight.Components; using Content.Shared._RD.Weight.Systems; +using Content.Shared.Changeling; +using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Hands.EntitySystems; +using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Ships.Sail; using Content.Shared.Imperial.Medieval.Ships.Wind; using Content.Shared.Imperial.Medieval.Skills; using Content.Shared.Popups; +using Robust.Shared.Configuration; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Random; using Robust.Shared.Timing; +using Robust.Shared.Maths; namespace Content.Server.Imperial.Medieval.Ships.Sail; @@ -29,12 +39,13 @@ public sealed class SailSystem : EntitySystem [Dependency] private readonly RDWeightSystem _rdWeight = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly HelmSystem _helm = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; - private const float DefaultReloadTimeSeconds = 1f; private TimeSpan _nextCheckTime; - private int _windForce; - private int _windAngle; + /// public override void Initialize() { @@ -48,21 +59,13 @@ public override void Update(float frameTime) if (curTime > _nextCheckTime) { - _nextCheckTime = curTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); - - // Плавные изменения ветра (0–10) - if (_windForce <= 0) - _windForce += _random.Next(0, 2); - else if (_windForce >= 10) - _windForce -= _random.Next(0, 2); - else - _windForce += _random.Next(-1, 2); + _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WindChangeTime)); - // Угол ветра: -45° до +45° (в градусах) — реалистично для моря - _windAngle += _random.Next(-5, 6); // ±5 градусов за шаг - _windAngle = Math.Clamp(_windAngle, -45, 45); + RandomiseVind(); - // --- Обработка каждого паруса --- + var ships = new List(); + var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); + var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); foreach (var sailComponent in EntityManager.EntityQuery()) { if (sailComponent.Folded) @@ -70,77 +73,57 @@ public override void Update(float frameTime) var sailEntity = sailComponent.Owner; var boat = _transform.GetParentUid(sailEntity); - if (!EntityManager.TryGetComponent(boat, out PhysicsComponent? boatBody)) - continue; + EnsureComp(boat); + var boatAngle = new Angle(); + var entities = _lookup.GetEntitiesIntersecting(boat); + foreach (var entity in entities) + { + if (HasComp(entity)) + boatAngle = _transform.GetWorldRotation(entity); + + } + + var sailAngle = (float)_transform.GetWorldRotation(sailEntity)*180; - // Получаем углы в радианах - float sailAngleRad = (float)_transform.GetWorldRotation(sailEntity).Theta; - float boatAngleRad = (float)_transform.GetWorldRotation(boat).Theta; - float windAngleRad = _windAngle * MathF.PI / 180f; // ветер в радианах - - // Направление ветра как вектор - Vector2 windDirection = new Vector2( - MathF.Cos(windAngleRad), - MathF.Sin(windAngleRad) - ); - - // Нормаль паруса — перпендикуляр к его поверхности (наружу) - // Парус — плоскость, нормаль — направление, в которое он "ловит" ветер - var sailNormalAngleRad = sailAngleRad + MathF.PI / 2f; - var sailNormal = new Vector2( - MathF.Cos(sailNormalAngleRad), - MathF.Sin(sailNormalAngleRad) - ); - - // Угол между ветром и нормалью паруса - var dot = Math.Clamp(Vector2.Dot(windDirection, sailNormal), -1f, 1f); - var angleBetween = MathF.Acos(dot); - - // Эффективность: максимальна, когда ветер перпендикулярен парусу (0°) - // Минимальна, когда ветер параллелен (90°+) - var efficiency = MathF.Cos(angleBetween); - - // Если ветер дует сзади (угол > 90°), всё равно даём слабую тягу - // (реальные паруса могут работать и на "бейсинге") - if (angleBetween > MathF.PI / 2f) - efficiency = MathF.Max(0.05f, efficiency); // 5% тяги сзади - else - efficiency = MathF.Max(0f, efficiency); - - // Сила ветра = базовая сила × площадь × эффективность - var forceMagnitude = _windForce * sailComponent.SailSize * efficiency; - - // Направление силы — вдоль нормали паруса - var forceDirection = sailNormal * forceMagnitude; - - // Крутящий момент: сила × плечо × sin(разница между парусом и кораблём) - // Плечо — расстояние от центра корабля до центра паруса (условное) - var sailOffset = 0.5f; // метры — можно настроить в компоненте - var torqueFactor = MathF.Sin(sailAngleRad - boatAngleRad); // как сильно парус "выступает" вбок - var torque = forceMagnitude * sailOffset * torqueFactor * 0.1f; // масштабируем - - var windEffect = new WindEffect + while ( Math.Abs(sailAngle) > 360) { - PushForce = forceDirection, - RotationTorque = torque - }; + if (sailAngle > 0) + sailAngle -= 360; + else + sailAngle += 360; + } - Push(sailEntity, sailComponent, windEffect); + while ( Math.Abs(sailAngle) > 360) + { + if (sailAngle > 0) + sailAngle -= 360; + else + sailAngle += 360; + } + + var diffAngle = sailAngle - windAngle; + var force = windForce * MathF.Cos(diffAngle/180) * sailComponent.SailSize; + + Push(sailEntity, force, boatAngle , push: sailComponent.Push, helm: sailComponent.Helm); + if (!ships.Contains(boat)) + ships.Add(boat); } } } - private void Push(EntityUid sail, SailComponent sailComponent, WindEffect windForce) + private void Push(EntityUid sail, float windForce, Angle torque , bool push = true, bool helm = false) { var boat = _transform.GetParentUid(sail); + var boatAngle = _transform.GetWorldRotation(boat); + + var weight = _rdWeight.GetTotal(boat); + var entities = _lookup.GetEntitiesIntersecting(boat); - if (entities.Count > 1000) + if (entities.Count > 1000000) return; - var weight = _rdWeight.GetTotal(boat); - foreach (var entity in entities) { if (HasComp(entity)) @@ -149,13 +132,47 @@ private void Push(EntityUid sail, SailComponent sailComponent, WindEffect windFo if (weight == 0) weight = 10; - var impulse = windForce.PushForce; - var angleimpulse = windForce.RotationTorque; - if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) + torque += Angle.FromDegrees(90); + if (windForce < 0) + windForce *= (float)0.5; + var impulse = torque.ToVec() * windForce; + if (!push) { - _physics.WakeBody(boat); - _physics.ApplyLinearImpulse(boat, impulse, body: body); - _physics.ApplyAngularImpulse(boat, angleimpulse); + _transform.SetWorldRotation(sail, Angle.FromDegrees(_cfg.GetCVar(ShipsCCVars.WindRotation))); } + + if (helm) + _helm.RotateShip(boat,sail, _helm.CheckForce(boat, sail)); + + if (_helm.CheckForce(boat, sail) > _cfg.GetCVar(ShipsCCVars.ShipsMaxSpeed)) + return; + + _physics.ApplyLinearImpulse(boat, impulse); + } + + private void RandomiseVind() + { + + + + // ветерок сила + var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); + if (windForce <= 0) + windForce += _random.Next(0, 2); + else if (windForce >= 10) + windForce -= _random.Next(0, 2); + else + windForce += _random.Next(-1, 2); + _cfg.SetCVar(ShipsCCVars.WindPower, windForce); + + // ветерок направление + var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); + windAngle += _random.Next(-1, 2)*5; // ±5 градусов за шаг + if (Math.Abs(windAngle) > 360) + windAngle = 0; + else if (windAngle < 0) + windAngle += 360; + + _cfg.SetCVar(ShipsCCVars.WindRotation, windAngle); } } diff --git a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs new file mode 100644 index 00000000000..6345057a131 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Imperial.Medieval.Ships.ShipDrowning; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class ShipDrowningComponent : Component +{ + [DataField("DrownLevel")] + public int DrownLevel; + + [DataField("DrownMaxLevel")] + public float DrownMaxLevel = 10000000000; +} diff --git a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs new file mode 100644 index 00000000000..8a09ad07dfd --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs @@ -0,0 +1,86 @@ +using System.Linq; +using Content.Server.Administration.Commands; +using Content.Server.Imperial.Medieval.Ships.PlayerDrowning; +using Content.Server.Imperial.Medieval.Ships.Wave; +using Content.Shared.Damage; +using Robust.Shared.Map.Components; +using Robust.Shared.Timing; + +namespace Content.Server.Imperial.Medieval.Ships.ShipDrowning; + +/// +/// This handles... +/// +public sealed class ShipDrowningSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EntityManager _entityManager = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly WaveSystem _wave = default!; + + private const float DefaultReloadTimeSeconds = 10f; + public const string ConductorContainer = "Conductor"; + + private TimeSpan _nextCheckTime; + /// + public override void Initialize() + { + base.Initialize(); + _nextCheckTime = _timing.CurTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(DefaultReloadTimeSeconds); + + foreach (var component in EntityManager.EntityQuery()) + { + var ship = component.Owner; + + if (component.DrownLevel > component.DrownMaxLevel) + { + if (component.DrownLevel > component.DrownMaxLevel * 10) + { + _entityManager.DeleteEntity(ship); + return; + } + EnsureComp(ship); + return; + } + if (HasComp(ship)) + RemComp(ship); + + if (!TryComp(ship, out var mapGrid)) + return; + var allTilesEnumerator = _map.GetAllTilesEnumerator(ship, mapGrid); + + var brokenTilesCount = 0; + var allTilesCount = 0; + var brokenlevel = 0; + + while (allTilesEnumerator.MoveNext(out var tile)) + { + allTilesCount++; + brokenlevel = 0; + foreach (var stage in _wave.Stages) + { + if (stage.Item2 == tile.Value.Tile.TypeId) + brokenTilesCount+= brokenlevel; + brokenlevel++; + } + } + + component.DrownLevel += brokenTilesCount; + component.DrownMaxLevel = allTilesCount * 100; + } + + } + + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipComponent.cs b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipComponent.cs new file mode 100644 index 00000000000..3c384b578c3 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Imperial.Medieval.Ships.SummonShip; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class SummonShipComponent : Component +{ + [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer))] + public string File = "/Ships/Victoria.yml"; + + [DataField] + public float Delay = 2; +} diff --git a/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs new file mode 100644 index 00000000000..d0ea8da6673 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs @@ -0,0 +1,63 @@ +using System.IO; +using Content.Server.Administration.Logs; +using Content.Shared.Database; +using Content.Shared.Interaction.Events; +using Content.Shared.Timing; +using Robust.Shared.ContentPack; +using Robust.Shared.EntitySerialization.Systems; +using Robust.Shared.Map; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Content.Server.Imperial.Medieval.Ships.SummonShip; + +/// +/// This handles... +/// +public sealed class SummonShipSystem : EntitySystem +{ + + [Dependency] private readonly MapLoaderSystem _mapLoader = default!; + [Dependency] private readonly IMapManager _mapMan = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly UseDelaySystem _delay = default!; + + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnUse); + } + + private void OnUse(EntityUid uid, SummonShipComponent comp, UseInHandEvent args) + { + if (args.Handled) + return; + args.Handled = true; + var time = TimeSpan.FromSeconds(comp.Delay); + _delay.SetLength(uid, time); + Timer.Spawn(time, () => Use(uid, comp)); + } + + private void Use(EntityUid uid, SummonShipComponent comp) + { + var mapId = _transform.GetMapId(uid); + var worldPos = _transform.GetWorldPosition(uid); + + var path = new ResPath(comp.File); + + if (!_mapLoader.TryLoadGrid(mapId, path, out var grid, offset: worldPos)) + { + _adminLog.Add(LogType.Action, LogImpact.High, $"Ошибка Загрузки грида из {path} сущность загрузки {uid}"); + return; + } + + EntityManager.QueueDeleteEntity(uid); + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index fb279ed4aa2..59d098d24ec 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -54,7 +54,7 @@ public sealed class WaveSystem : EntitySystem ("Plating", (ushort)3), ("FloorWhite", (ushort)4) }; - private const float RadiusTiles = 2f; + private const float RadiusTiles = 3f; private bool _initialized; public override void Initialize() @@ -96,13 +96,16 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE if (!_entityManager.TryGetComponent(gridEntity, out var mapGridComp)) return; - var grid = new Entity(gridEntity, mapGridComp); + var tileRef = _map.GetTileRef(grid, collisionPos); + var centerTilePos = _map.MapToGrid(grid, collisionPos); var antiradius = (int)RadiusTiles*-1; + var nearbyTiles = new List(); + for (int dx = antiradius; dx <= RadiusTiles; dx++) { for (int dy = antiradius; dy <= RadiusTiles; dy++) diff --git a/Content.Server/Imperial/Medieval/Ships/WeatherVane/WeatherVaneComponent.cs b/Content.Server/Imperial/Medieval/Ships/WeatherVane/WeatherVaneComponent.cs new file mode 100644 index 00000000000..f7fed230b40 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/WeatherVane/WeatherVaneComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Imperial.Medieval.Ships.WeatherVane; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class WeatherVaneComponent : Component +{ + +} diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs new file mode 100644 index 00000000000..6ec2ba1036e --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -0,0 +1,22 @@ +using Robust.Shared; +using Robust.Shared.Configuration; +namespace Content.Shared.Imperial.Medieval.Administration.Ships; + +/// +/// Цвары для корабликов, тут лежит ветер максимальная скорость кораблей и подобное +/// +[CVarDefs] +public sealed class ShipsCCVars : CVars +{ + public static readonly CVarDef ShipsMaxSpeed = + CVarDef.Create("ships.maxspeed", 20f, CVar.REPLICATED | CVar.SERVER); + + public static readonly CVarDef WindChangeTime = + CVarDef.Create("ships.windchangetime", 1f, CVar.REPLICATED | CVar.SERVER); + + public static readonly CVarDef WindPower = + CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); + public static readonly CVarDef WindRotation = + CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); +} + diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs index 0df7cb2eb35..bbd88a65065 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs @@ -1,134 +1,137 @@ -using System.Numerics; -using Content.Shared._RD.Weight.Components; -using Content.Shared._RD.Weight.Systems; -using Content.Shared.Coordinates; -using Content.Shared.DoAfter; -using Content.Shared.Imperial.Medieval.Skills; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Popups; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; - - -using Content.Shared.Database; -using Content.Shared.Hands.EntitySystems; - - -namespace Content.Shared.Imperial.Medieval.Ships.Oar; - -/// -/// This handles... -/// -public sealed class OarSystem : EntitySystem -{ - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedSkillsSystem _skills = default!; - [Dependency] private readonly EntityManager _entManager = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly RDWeightSystem _rdWeight = default!; - public override void Initialize() - { - SubscribeLocalEvent(OnOarAfterInteract); - SubscribeLocalEvent(OnOarDoAfter); - } - - private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) - { - var playerEntity = args.User; - - if (args.Handled || !args.CanReach ) - return; - - bool foundWater = false; - foreach (var entity in _lookup.GetEntitiesInRange(args.ClickLocation, 0.3f)) - { - if (MetaData(entity).EntityPrototype?.ID == "MedievalWaterDeepDarkEntity") - { - foundWater = true; - break; - } - } - if (!foundWater) - return; - - var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; - var sdoAfter = new DoAfterArgs(EntityManager, - playerEntity, - time, - new OnOarDoAfterEvent(), - args.Target, - target: args.Used) - { - MovementThreshold = 0.5f, - BreakOnMove = true, - CancelDuplicate = true, - DistanceThreshold = 2, - BreakOnDamage = true, - RequireCanInteract = false, - BreakOnDropItem = true, - BreakOnHandChange = true, - }; - if (!_doAfter.TryStartDoAfter(sdoAfter)) - return; - - var playerPosition = _transform.GetWorldPosition(playerEntity); - var boatPosition = _transform.ToWorldPosition(args.ClickLocation); - var direction = (playerPosition - boatPosition).Normalized(); - component.Direction = direction; - args.Handled = true; - - } - - private void OnOarDoAfter(EntityUid uid, MetaDataComponent component, OnOarDoAfterEvent args) - { - var item = _hands.GetActiveItem(args.User); - if (args.Cancelled || args.Handled || item == null) - return; - - if (!TryComp(item, out var comp)) - return; - - Push(item.Value, comp.Direction, comp.Power, args.User); - args.Handled = true; - } - - private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) - { - power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; - - var boat = _transform.GetParentUid(player); - - var entities = _lookup.GetEntitiesIntersecting(boat); - - if (entities.Count > 1000) - return; - - var weight = _rdWeight.GetTotal(boat); - - foreach (var entity in entities) - { - if (HasComp(entity)) - weight += _rdWeight.GetTotal(entity); - } - - if (weight == 0) - weight = 10; - var impulse = direction * (power / weight); - var angleimpulse = (power / weight); - if (direction.X < 0) - angleimpulse = -angleimpulse; - - if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) - { - _physics.WakeBody(boat); - _physics.ApplyLinearImpulse(boat, impulse, body: body); - _physics.ApplyAngularImpulse(boat, angleimpulse); - } - } -} +// using System.Numerics; +// using Content.Shared._RD.Weight.Components; +// using Content.Shared._RD.Weight.Systems; +// using Content.Shared.Coordinates; +// using Content.Shared.DoAfter; +// using Content.Shared.Imperial.Medieval.Skills; +// using Content.Shared.Interaction; +// using Content.Shared.Interaction.Events; +// using Content.Shared.Popups; +// using Robust.Shared.Physics; +// using Robust.Shared.Physics.Components; +// using Robust.Shared.Physics.Systems; +// +// +// using Content.Shared.Database; +// using Content.Shared.Hands.EntitySystems; +// +// +// namespace Content.Shared.Imperial.Medieval.Ships.Oar; +// +// /// +// /// По идее это должно быть тут, но млять грёбаный делей +// /// +// public sealed class OarSystem : EntitySystem +// { +// [Dependency] private readonly SharedPopupSystem _popup = default!; +// [Dependency] private readonly SharedPhysicsSystem _physics = default!; +// [Dependency] private readonly SharedTransformSystem _transform = default!; +// [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; +// [Dependency] private readonly SharedSkillsSystem _skills = default!; +// [Dependency] private readonly EntityManager _entManager = default!; +// [Dependency] private readonly EntityLookupSystem _lookup = default!; +// [Dependency] private readonly SharedHandsSystem _hands = default!; +// [Dependency] private readonly RDWeightSystem _rdWeight = default!; +// public override void Initialize() +// { +// SubscribeLocalEvent(OnOarAfterInteract); +// SubscribeLocalEvent(OnOarDoAfter); +// } +// +// private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) +// { +// var playerEntity = args.User; +// +// if (args.Handled || !args.CanReach ) +// return; +// +// +// var boat = _transform.GetParentUid(playerEntity); +// +// +// if (boat == args.ClickLocation.EntityId) +// return; +// +// var clickEntity = args.ClickLocation.EntityId; +// if (boat == _transform.GetParentUid(clickEntity)) +// return; +// +// var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; +// var sdoAfter = new DoAfterArgs(EntityManager, +// playerEntity, +// time, +// new OnOarDoAfterEvent(), +// args.Target, +// target: args.Used) +// { +// MovementThreshold = 0.5f, +// BreakOnMove = true, +// CancelDuplicate = true, +// DistanceThreshold = 2, +// BreakOnDamage = true, +// RequireCanInteract = false, +// BreakOnDropItem = true, +// BreakOnHandChange = true, +// }; +// +// +// _popup.PopupClient(Loc.GetString($"{playerEntity} {time} {args.Target} {args.Used}"), component.Owner, args.User); +// _doAfter.TryStartDoAfter(sdoAfter); +// +// +// +// +// var playerPosition = _transform.GetWorldPosition(playerEntity); +// var boatPosition = _transform.ToWorldPosition(args.ClickLocation); +// var direction = (playerPosition - boatPosition).Normalized(); +// component.Direction = direction; +// } +// +// private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) +// { +// var item = _hands.GetActiveItem(args.User); +// if (args.Cancelled || args.Handled || item == null) +// return; +// +// if (!TryComp(item, out var comp)) +// return; +// +// Push(item.Value, comp.Direction, comp.Power, args.User); +// args.Handled = true; +// args.Repeat = true; +// } +// +// private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) +// { +// power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; +// +// var boat = _transform.GetParentUid(player); +// +// var entities = _lookup.GetEntitiesIntersecting(boat); +// +// if (entities.Count > 1000) +// return; +// +// var weight = _rdWeight.GetTotal(boat); +// +// foreach (var entity in entities) +// { +// if (HasComp(entity)) +// weight += _rdWeight.GetTotal(entity); +// } +// +// if (weight == 0) +// weight = 10; +// var impulse = direction * (power / weight); +// var angleimpulse = (power / weight); +// if (direction.X < 0) +// angleimpulse = -angleimpulse; +// +// if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) +// { +// _physics.WakeBody(boat); +// _physics.ApplyLinearImpulse(boat, impulse, body: body); +// _physics.ApplyAngularImpulse(boat, angleimpulse, body: body); +// } +// } +// } diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Sail/SailComponent.cs similarity index 60% rename from Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs rename to Content.Shared/Imperial/Medieval/Ships/Sail/SailComponent.cs index 631cf37f887..4c5331dee8a 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailComponent.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Sail/SailComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Imperial.Medieval.Ships.Sail; +namespace Content.Shared.Imperial.Medieval.Ships.Sail; /// /// This is used for... @@ -11,4 +11,10 @@ public sealed partial class SailComponent : Component [DataField("Folded")] public bool Folded; + + [DataField("Helm")] + public bool Helm; + + [DataField("Push")] + public bool Push = true; } diff --git a/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs b/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs new file mode 100644 index 00000000000..08edcb90019 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs @@ -0,0 +1,11 @@ +using Content.Shared.DoAfter; + +using Robust.Shared.Serialization; + +namespace Content.Shared.Imperial.Medieval.Ships.Sail; + + +[Serializable, NetSerializable] +public sealed partial class SailUseEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs new file mode 100644 index 00000000000..bf6d8b23206 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs @@ -0,0 +1,70 @@ +using Content.Shared.DoAfter; +using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Ships.Oar; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Interaction; +using Robust.Shared.Configuration; +using Robust.Shared.Random; + +namespace Content.Shared.Imperial.Medieval.Ships.Sail; + +/// +/// вращяет парус по ветру +/// +public sealed class SharedSailSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnUse); + } + + private void OnActivate(EntityUid uid, SailComponent component, ActivateInWorldEvent args) + { + UseDelay(args.User, args.Target); + } + + private void OnInteractUsing(EntityUid uid, SailComponent component, InteractUsingEvent args) + { + UseDelay(args.User, args.Target); + } + + private void UseDelay(EntityUid playerEntity, EntityUid targetEntity) + { + + var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new SailUseEvent(), + targetEntity, + playerEntity) + { + MovementThreshold = 0.5f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + }; + _doAfter.TryStartDoAfter(sdoAfter); + } + + private void OnUse(EntityUid uid, SailComponent component, SailUseEvent args) + { + Rotate(uid); + } + + private void Rotate(EntityUid uid) + { + _transform.SetWorldRotation(uid, _cfg.GetCVar(ShipsCCVars.WindRotation)/180); + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs index 6f8990921b8..40e9eea46d4 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Wind/SharedWindSystem.cs @@ -13,139 +13,6 @@ public override void Initialize() { // Initialization logic here if needed } - - /// - /// Calculates the aerodynamic forces acting on a sail due to wind. - /// - /// World position of the ship. - /// Current rotation of the ship in radians. - /// Angle of the sail relative to the ship's orientation (radians). - /// Effective area of the sail (m²). - /// Current time for dynamic wind simulation. - /// Density of air (default: 1.2 kg/m³). - /// A containing force, torque, and wind data. - public static WindEffect CalculateWindForce( - Vector2 shipPosition, - float shipRotation, - float sailAngle, - float sailArea, - float time = 0f, - float airDensity = 1.2f) - { - // Get wind vector at ship's position - var windVector = GetWindField(shipPosition, time); - var windStrength = windVector.Length(); - var windDirection = windVector.Normalized(); - - // Absolute sail angle in world coordinates - var absoluteSailAngle = shipRotation + sailAngle; - - // Angle between wind direction and sail normal - var windAngle = MathF.Atan2(windDirection.Y, windDirection.X); - var angleDiff = absoluteSailAngle - windAngle; - var angleOfAttack = NormalizeAngle(angleDiff); - - // Lift and drag coefficients based on angle of attack - var liftCoefficient = MathF.Sin(2f * angleOfAttack); - var dragCoefficient = MathF.Abs(MathF.Cos(angleOfAttack)); - - // Dynamic pressure - var dynamicPressure = 0.5f * airDensity * windStrength * windStrength; - - // Force magnitudes - var liftForce = liftCoefficient * dynamicPressure * sailArea; - var dragForce = dragCoefficient * dynamicPressure * sailArea; - - // Sail orientation vectors - Vector2 sailDirection = new(MathF.Cos(absoluteSailAngle), MathF.Sin(absoluteSailAngle)); // Along sail - Vector2 sailNormal = new(-MathF.Sin(absoluteSailAngle), MathF.Cos(absoluteSailAngle)); // Perpendicular to sail - - // Force components - var liftVector = sailNormal * liftForce; - var dragVector = sailDirection * dragForce; - - // Total force - var totalForce = liftVector + dragVector; - - // Torque calculation - var leverArm = 1f; // Distance from center of mass to sail (can be parameterized) - var torque = CalculateTorque(totalForce, absoluteSailAngle, leverArm); - - return new WindEffect - { - PushForce = totalForce, - RotationTorque = angleDiff, - WindStrength = windStrength, - WindDirection = windDirection - }; - } - - /// - /// Normalizes an angle to the range [-π, π] - /// - private static float NormalizeAngle(float angle) - { - while (angle > MathF.PI) - angle -= MathF.Tau; - while (angle < -MathF.PI) - angle += MathF.Tau; - return angle; - } - - /// - /// Simple procedural wind field simulation - /// - private static Vector2 GetWindField(Vector2 position, float time = 0f) - { - var x = position.X; - var y = position.Y; - - var u = MathF.Sin(x / 2f + time) * MathF.Cos(y / 3f) + 0.5f * MathF.Sin(x / 5f + y / 7f); - var v = MathF.Cos(x / 3f + time) * MathF.Sin(y / 2f) + 0.5f * MathF.Cos(x / 7f - y / 5f); - - return new Vector2(u, v); - } - - /// - /// Calculates rotational torque applied by wind force - /// - private static float CalculateTorque(Vector2 force, float sailAngle, float leverArm) - { - if (force.LengthSquared() < 0.001f) - return 0f; - - var forceDirection = force.Normalized(); - Vector2 leverDirection = new(-MathF.Sin(sailAngle), MathF.Cos(sailAngle)); - - // Cross product in 2D: F × r - var crossProduct = forceDirection.X * leverDirection.Y - forceDirection.Y * leverDirection.X; - - return force.Length() * leverArm * crossProduct; - } } -/// -/// Represents the effect of wind on a sail. -/// -public struct WindEffect -{ - /// - /// Net force applied to the ship in world coordinates. - /// - public Vector2 PushForce; - - /// - /// Rotational torque around the ship's center. - /// - public float RotationTorque; - /// - /// Magnitude of the wind vector at this point. - /// - public float WindStrength; - - /// - /// Direction of the wind (normalized). - /// - public Vector2 WindDirection; -} diff --git a/Content.Shared/Rotatable/RotatableComponent.cs b/Content.Shared/Rotatable/RotatableComponent.cs index 336c484d8c0..c13cc1425f0 100644 --- a/Content.Shared/Rotatable/RotatableComponent.cs +++ b/Content.Shared/Rotatable/RotatableComponent.cs @@ -11,18 +11,18 @@ public sealed partial class RotatableComponent : Component /// /// If true, this entity can be rotated even while anchored. /// - [DataField, AutoNetworkedField] + [DataField("RotateWhileAnchored"), AutoNetworkedField] public bool RotateWhileAnchored; /// /// If true, will rotate entity in players direction when pulled /// - [DataField, AutoNetworkedField] + [DataField("RotateWhilePulling"), AutoNetworkedField] public bool RotateWhilePulling = true; /// /// The angular value to change when using the rotate verbs. /// - [DataField, AutoNetworkedField] + [DataField("Increment"), AutoNetworkedField] public Angle Increment = Angle.FromDegrees(90); } diff --git a/Resources/Prototypes/Imperial/Medieval/decor.yml b/Resources/Prototypes/Imperial/Medieval/decor.yml index 9dab0528e5e..54cc2edf8b4 100644 --- a/Resources/Prototypes/Imperial/Medieval/decor.yml +++ b/Resources/Prototypes/Imperial/Medieval/decor.yml @@ -5022,7 +5022,7 @@ fixtures: fix1: #vertical shape: - !type:PhysShapeAabb + !type:PhysShapeAabb bounds: "-0.49,-0.15,0.49,0.15" density: 1000 mask: @@ -5385,6 +5385,11 @@ layer: - WallLayer - type: InteractionOutline + - type: Sail + SailSize: 5 + - type: Rotatable + RotateWhileAnchored: true + Increment: 5 - type: entity name: small light From d6ba313b727faf22daa13b01ee39d60e9678a53c Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:37:58 +0300 Subject: [PATCH 07/20] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit фиксы --- .../ShipDrowning/ClientShipDrowningSystem.cs | 3 +++ .../Medieval/Ships/Sail/SailSystem.cs | 1 + .../ShipDrowning/ShipDrowningComponent.cs | 14 ------------- .../Ships/ShipDrowning/ShipDrowningSystem.cs | 2 +- .../Medieval/Ships/Storm/StormSystem.cs | 13 ++++++++++++ .../ShipDrowning/ShipDrowningComponent.cs | 21 +++++++++++++++++++ 6 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 Content.Client/Imperial/Medieval/ShipDrowning/ClientShipDrowningSystem.cs delete mode 100644 Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs diff --git a/Content.Client/Imperial/Medieval/ShipDrowning/ClientShipDrowningSystem.cs b/Content.Client/Imperial/Medieval/ShipDrowning/ClientShipDrowningSystem.cs new file mode 100644 index 00000000000..3aa059d0fc5 --- /dev/null +++ b/Content.Client/Imperial/Medieval/ShipDrowning/ClientShipDrowningSystem.cs @@ -0,0 +1,3 @@ +// тут мог бы быть ваш код) +// но я криворукий бэкэндер который в душе не чает как работать нормально с визуалом поэтому тут пусто +// TODO : сделать нормальное отображение потопления, а не тот костыль который я сделаю diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index c6b8065f952..c28c5ee8eb7 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Imperial.Medieval.Administration.Ships; using Content.Shared.Imperial.Medieval.Ships.Sail; +using Content.Shared.Imperial.Medieval.Ships.ShipDrowning; using Content.Shared.Imperial.Medieval.Ships.Wind; using Content.Shared.Imperial.Medieval.Skills; using Content.Shared.Popups; diff --git a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs deleted file mode 100644 index 6345057a131..00000000000 --- a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Content.Server.Imperial.Medieval.Ships.ShipDrowning; - -/// -/// This is used for... -/// -[RegisterComponent] -public sealed partial class ShipDrowningComponent : Component -{ - [DataField("DrownLevel")] - public int DrownLevel; - - [DataField("DrownMaxLevel")] - public float DrownMaxLevel = 10000000000; -} diff --git a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs index 8a09ad07dfd..7afc40dc8f5 100644 --- a/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Imperial.Medieval.Ships.PlayerDrowning; using Content.Server.Imperial.Medieval.Ships.Wave; using Content.Shared.Damage; +using Content.Shared.Imperial.Medieval.Ships.ShipDrowning; using Robust.Shared.Map.Components; using Robust.Shared.Timing; @@ -19,7 +20,6 @@ public sealed class ShipDrowningSystem : EntitySystem [Dependency] private readonly WaveSystem _wave = default!; private const float DefaultReloadTimeSeconds = 10f; - public const string ConductorContainer = "Conductor"; private TimeSpan _nextCheckTime; /// diff --git a/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs b/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs new file mode 100644 index 00000000000..61496dd6eae --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Imperial.Medieval.Ships.Storm; + +/// +/// This handles... +/// +public sealed class StormSystem : EntitySystem +{ + /// + public override void Initialize() + { + + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs b/Content.Shared/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs new file mode 100644 index 00000000000..b2e79164a36 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/ShipDrowning/ShipDrowningComponent.cs @@ -0,0 +1,21 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Imperial.Medieval.Ships.ShipDrowning; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class ShipDrowningComponent : Component +{ + /// + /// Уровень затоплености + /// + [DataField("DrownLevel")] + public int DrownLevel; + /// + /// Максимальный уровень затоплености + /// + [DataField("DrownMaxLevel")] + public float DrownMaxLevel = 10000000000; +} From 599b143e4d30f6f779046f3eb0454f611d6e7ee5 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Fri, 6 Mar 2026 20:09:13 +0300 Subject: [PATCH 08/20] =?UTF-8?q?=D0=BB=D0=B5=D0=B9=D1=81=D1=8F=20=D0=B2?= =?UTF-8?q?=D0=B0=D1=80=D0=B5=D0=B2=D0=BE=20=D1=84=D0=B8=D0=BA=D1=81=D1=8B?= =?UTF-8?q?=20=D0=B8=20=D1=80=D0=B0=D0=B7=D0=B2=D1=91=D1=80=D1=82=D1=8B?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B0=D1=80=D1=83=D1=81?= =?UTF-8?q?=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ну на самом деле это никто не читает а писать фикс я задолбался, а писать развёрнуто мне лень --- .../Medieval/Ships/Oar/ServerOarSystem.cs | 1 + .../Medieval/Ships/Sail/SailSystem.cs | 21 ++++++-- .../Medieval/Ships/Sail/SailUseEvent.cs | 4 ++ .../Medieval/Ships/Sail/SharedSailSystem.cs | 52 +++++++++++++++++++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs index 3266e03e801..ee563508411 100644 --- a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs @@ -72,6 +72,7 @@ private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInte RequireCanInteract = false, BreakOnDropItem = true, BreakOnHandChange = true, + NeedHand = true, }; _doAfter.TryStartDoAfter(sdoAfter); var playerPosition = _transform.GetWorldPosition(playerEntity); diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index c28c5ee8eb7..a0d72b4778f 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -50,6 +50,7 @@ public sealed class SailSystem : EntitySystem /// public override void Initialize() { + SubscribeLocalEvent(OnFold); } public override void Update(float frameTime) @@ -153,9 +154,6 @@ private void Push(EntityUid sail, float windForce, Angle torque , bool push = tr private void RandomiseVind() { - - - // ветерок сила var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); if (windForce <= 0) @@ -176,4 +174,21 @@ private void RandomiseVind() _cfg.SetCVar(ShipsCCVars.WindRotation, windAngle); } + + + private void OnFold(EntityUid uid, SailComponent component, SailFoldEvent args) + { + if (args.Cancelled) + return; + + var rot = _transform.GetWorldRotation(uid); + var coords = _transform.GetMapCoordinates(uid); + Del(uid); + if (component.Folded) + Spawn("MedievalDecorShipSailReady", coords, rotation: rot); + else + { + Spawn("MedievalDecorShipSailShipup", coords, rotation: rot); + } + } } diff --git a/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs b/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs index 08edcb90019..783ba2e69e9 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Sail/SailUseEvent.cs @@ -9,3 +9,7 @@ namespace Content.Shared.Imperial.Medieval.Ships.Sail; public sealed partial class SailUseEvent : SimpleDoAfterEvent { } + +public sealed partial class SailFoldEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs index bf6d8b23206..de85335a9d6 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Sail/SharedSailSystem.cs @@ -1,10 +1,15 @@ +using Content.Shared.Database; using Content.Shared.DoAfter; +using Content.Shared.Ghost; using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Follower; using Content.Shared.Imperial.Medieval.Ships.Oar; using Content.Shared.Imperial.Medieval.Skills; using Content.Shared.Interaction; +using Content.Shared.Verbs; using Robust.Shared.Configuration; using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Shared.Imperial.Medieval.Ships.Sail; @@ -20,6 +25,7 @@ public sealed class SharedSailSystem : EntitySystem /// public override void Initialize() { + SubscribeLocalEvent>(OnGetAlternativeVerbs); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnUse); @@ -60,6 +66,9 @@ private void UseDelay(EntityUid playerEntity, EntityUid targetEntity) private void OnUse(EntityUid uid, SailComponent component, SailUseEvent args) { + if (args.Cancelled) + return; + Rotate(uid); } @@ -67,4 +76,47 @@ private void Rotate(EntityUid uid) { _transform.SetWorldRotation(uid, _cfg.GetCVar(ShipsCCVars.WindRotation)/180); } + + private void OnGetAlternativeVerbs(GetVerbsEvent ev) + { + if (ev.User == ev.Target || IsClientSide(ev.Target)) + return; + + if (!TryComp(ev.Target, out var sail)) + return; + var text = "Сложить"; + if (sail.Folded) + text = "Разложить"; + var verb = new AlternativeVerb() + { + Priority = 10, + Act = () => TryFold(ev.User, ev.Target), + Impact = LogImpact.Low, + Text = Loc.GetString(text), + }; + ev.Verbs.Add(verb); + } + + private void TryFold(EntityUid playerEntity, EntityUid targetEntity) + { + var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.15f - _skills.GetSkillLevel(playerEntity, "Intelligence") * 0.15f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new SailFoldEvent(), + targetEntity, + playerEntity) + { + MovementThreshold = 0.5f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + NeedHand = true, + }; + _doAfter.TryStartDoAfter(sdoAfter); + } } From b666e10bd6c27305d1ea15cdd62b6d32dd0666f5 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sat, 7 Mar 2026 17:34:10 +0300 Subject: [PATCH 09/20] =?UTF-8?q?=D1=8F=D0=BA=D0=BE=D1=80=D1=8F=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit якоря --- .../Ships/Anchor/AnchorSwithComponent.cs | 10 ++++++ .../Ships/Anchor/AnchorSwithSystem.cs | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs new file mode 100644 index 00000000000..8183ba80778 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Imperial.Medieval.Ships.Anchor; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class AnchorSwithComponent : Component +{ + +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs new file mode 100644 index 00000000000..0c7f59ca2de --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs @@ -0,0 +1,32 @@ +using Content.Shared.Imperial.Medieval.Ships.Sail; +using Content.Shared.Interaction; + +namespace Content.Shared.Imperial.Medieval.Ships.Anchor; + +/// +/// This handles... +/// +public sealed class AnchorSwithSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnInteractUsing); + } + + private void OnInteractUsing(SailComponent component, InteractUsingEvent args) + { + Use(args.User, args.Target); + } + + private void OnActivate(AnchorSwithComponent component, ActivateInWorldEvent args) + { + Use(args.User, args.Target); + } + + private void Use(EntityUid player, EntityUid target) + { + + } +} From 6bc1330afd05b2e3306b40390cea0ee2504ca6e8 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:05:05 +0300 Subject: [PATCH 10/20] =?UTF-8?q?=D0=AF=D0=BA=D0=BE=D1=80=D1=8F=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B3=D0=BE=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Anchor/ServerMedievalAnchorSystem.cs | 47 ++++++++++++++ .../Ships/Anchor/AnchorSwithSystem.cs | 32 ---------- ...omponent.cs => MedievalAnchorComponent.cs} | 5 +- .../Ships/Anchor/MedievalAnchorSystem.cs | 63 +++++++++++++++++++ .../Medieval/Ships/Anchor/UseAnchorEvent.cs | 17 +++++ 5 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs delete mode 100644 Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs rename Content.Shared/Imperial/Medieval/Ships/Anchor/{AnchorSwithComponent.cs => MedievalAnchorComponent.cs} (53%) create mode 100644 Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorSystem.cs create mode 100644 Content.Shared/Imperial/Medieval/Ships/Anchor/UseAnchorEvent.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs b/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs new file mode 100644 index 00000000000..eb5f1321964 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs @@ -0,0 +1,47 @@ +using Content.Server.Shuttles.Components; +using Content.Server.Shuttles.Systems; +using Content.Shared.Imperial.Medieval.Ships.Anchor; + +namespace Content.Server.Imperial.Medieval.Ships.Anchor; + +/// +/// This handles... +/// +public sealed class ServerMedievalAnchorSystem : EntitySystem +{ + [Dependency] private readonly ShuttleSystem _shuttleSystem = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnUseAnchor); + } + + private void OnUseAnchor(EntityUid uid, MedievalAnchorComponent component, UseAnchorEvent args) + { + if (args.Target == null || args.Cancelled) + return; + + var target = component.Owner; + + var enabled = component.Enabled; + + ShuttleComponent? shuttleComponent = default; + + var transform = Transform(target); + var grid = transform.GridUid; + if (!grid.HasValue || !transform.Anchored || !Resolve(grid.Value, ref shuttleComponent)) + return; + + if (!enabled) + { + _shuttleSystem.Disable(grid.Value); + } + else + { + _shuttleSystem.Enable(grid.Value); + } + + shuttleComponent.Enabled = !enabled; + component.Enabled = !enabled; + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs deleted file mode 100644 index 0c7f59ca2de..00000000000 --- a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithSystem.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Shared.Imperial.Medieval.Ships.Sail; -using Content.Shared.Interaction; - -namespace Content.Shared.Imperial.Medieval.Ships.Anchor; - -/// -/// This handles... -/// -public sealed class AnchorSwithSystem : EntitySystem -{ - /// - public override void Initialize() - { - SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnInteractUsing); - } - - private void OnInteractUsing(SailComponent component, InteractUsingEvent args) - { - Use(args.User, args.Target); - } - - private void OnActivate(AnchorSwithComponent component, ActivateInWorldEvent args) - { - Use(args.User, args.Target); - } - - private void Use(EntityUid player, EntityUid target) - { - - } -} diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorComponent.cs similarity index 53% rename from Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs rename to Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorComponent.cs index 8183ba80778..42503d067c1 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Anchor/AnchorSwithComponent.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorComponent.cs @@ -4,7 +4,8 @@ namespace Content.Shared.Imperial.Medieval.Ships.Anchor; /// This is used for... /// [RegisterComponent] -public sealed partial class AnchorSwithComponent : Component +public sealed partial class MedievalAnchorComponent : Component { - + [DataField("Enabled")] + public bool Enabled; } diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorSystem.cs new file mode 100644 index 00000000000..cec5bc2a831 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Anchor/MedievalAnchorSystem.cs @@ -0,0 +1,63 @@ +using Content.Shared.Imperial.Medieval.Ships.Sail; +using Content.Shared.Interaction; +using Content.Shared.Construction.Components; +using Content.Shared.DoAfter; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Popups; + + +namespace Content.Shared.Imperial.Medieval.Ships.Anchor; + +/// +/// система для поднятия и опускания якоря +/// +public sealed class MedievalAnchorSystem : EntitySystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnInteractUsing); + } + + private void OnInteractUsing(EntityUid uid, MedievalAnchorComponent component, InteractUsingEvent args) + { + Use(args.User, args.Target); + } + + private void OnActivate(EntityUid uid, MedievalAnchorComponent component, ActivateInWorldEvent args) + { + Use(args.User, args.Target); + } + + private void Use(EntityUid playerEntity, EntityUid target) + { + + + var time = 7 -_skills.GetSkillLevel(playerEntity, "Strength") * 0.3f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new UseAnchorEvent(), + target, + target: playerEntity) + { + MovementThreshold = 0.5f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + NeedHand = true, + }; + + + _doAfter.TryStartDoAfter(sdoAfter); + + } +} diff --git a/Content.Shared/Imperial/Medieval/Ships/Anchor/UseAnchorEvent.cs b/Content.Shared/Imperial/Medieval/Ships/Anchor/UseAnchorEvent.cs new file mode 100644 index 00000000000..e6fa7120542 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Anchor/UseAnchorEvent.cs @@ -0,0 +1,17 @@ +using Content.Shared.DoAfter; + +using Robust.Shared.Serialization; + + +namespace Content.Shared.Imperial.Medieval.Ships.Anchor; + +/// +/// Ивент для поднятия якоря +/// +[Serializable, NetSerializable] +public sealed partial class UseAnchorEvent : SimpleDoAfterEvent +{ +} + + + From 4bb2cf9135bdb43413d9c8af56329e41e1fca052 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sun, 8 Mar 2026 22:07:16 +0300 Subject: [PATCH 11/20] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D1=8F=D0=B5=D0=BC=20=D0=BF=D0=BE=D0=BF=D0=B0=D0=BF=20=D0=B2?= =?UTF-8?q?=D1=91=D1=81=D0=BB=D0=B0=D0=BC=20=D0=B8=20=D1=88=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Anchor/ServerMedievalAnchorSystem.cs | 2 +- .../Medieval/Ships/Oar/ServerOarSystem.cs | 8 ++ .../Medieval/Ships/Sail/SailSystem.cs | 27 +------ .../Medieval/Ships/Wind/ServerWindSystem.cs | 74 ++++++++++++++++++- .../Administration/Ships/ShipsCCVars.cs | 4 + .../Medieval/Ships/Sea/SeaComponent.cs | 11 +++ 6 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs b/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs index eb5f1321964..9805e4d15ba 100644 --- a/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Anchor/ServerMedievalAnchorSystem.cs @@ -43,5 +43,5 @@ private void OnUseAnchor(EntityUid uid, MedievalAnchorComponent component, UseAn shuttleComponent.Enabled = !enabled; component.Enabled = !enabled; - } + } } diff --git a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs index ee563508411..94656be7289 100644 --- a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs @@ -79,6 +79,14 @@ private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInte var boatPosition = _transform.ToWorldPosition(args.ClickLocation); var direction = (playerPosition - boatPosition).Normalized(); component.Direction = direction; + var pushAngle = direction.ToAngle(); + var boatAngle = _transform.GetWorldRotation(boat); + + var text = "по курсу"; + if (Math.Abs(pushAngle-boatAngle)*180 > 90) + text = "против курса"; + + _popup.PopupClient($"Ты гребёшь {text} лодки", playerEntity); } private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index a0d72b4778f..fdd79a7dbf7 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -61,9 +61,7 @@ public override void Update(float frameTime) if (curTime > _nextCheckTime) { - _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WindChangeTime)); - - RandomiseVind(); + _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WindDelay)); var ships = new List(); var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); @@ -152,29 +150,6 @@ private void Push(EntityUid sail, float windForce, Angle torque , bool push = tr _physics.ApplyLinearImpulse(boat, impulse); } - private void RandomiseVind() - { - // ветерок сила - var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); - if (windForce <= 0) - windForce += _random.Next(0, 2); - else if (windForce >= 10) - windForce -= _random.Next(0, 2); - else - windForce += _random.Next(-1, 2); - _cfg.SetCVar(ShipsCCVars.WindPower, windForce); - - // ветерок направление - var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); - windAngle += _random.Next(-1, 2)*5; // ±5 градусов за шаг - if (Math.Abs(windAngle) > 360) - windAngle = 0; - else if (windAngle < 0) - windAngle += 360; - - _cfg.SetCVar(ShipsCCVars.WindRotation, windAngle); - } - private void OnFold(EntityUid uid, SailComponent component, SailFoldEvent args) { diff --git a/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs index aad8226fda2..146f5ab8618 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs @@ -1,3 +1,11 @@ +using Content.Server.Shuttles.Components; +using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Ships.Sea; +using Robust.Shared.Configuration; +using Robust.Shared.Map; +using Robust.Shared.Random; +using Robust.Shared.Timing; + namespace Content.Server.Imperial.Medieval.Ships.Wind; /// @@ -5,9 +13,73 @@ namespace Content.Server.Imperial.Medieval.Ships.Wind; /// public sealed class ServerWindSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + private TimeSpan _nextCheckTime; /// public override void Initialize() { - + + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WindChangeTime)); + RandomiseVind(); + } + } + + + private void RandomiseVind() + { + // ветерок сила + var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); + var countShips = FindShips(); + + if (windForce <= 0+countShips) + windForce += _random.Next(0, 2); + else if (windForce >= 2 + countShips || countShips >= 10) + windForce -= _random.Next(0, 2); + else + windForce += _random.Next(-1, 2); + _cfg.SetCVar(ShipsCCVars.WindPower, windForce); + + // ветерок направление + var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); + windAngle += _random.Next(-1, 2)*5; // ±5 градусов за шаг + if (Math.Abs(windAngle) > 360) + windAngle = 0; + else if (windAngle < 0) + windAngle += 360; + + _cfg.SetCVar(ShipsCCVars.WindRotation, windAngle); + } + + private int FindShips() + { + var count = 0; + foreach (var seaComp in EntityManager.EntityQuery()) + { + if (seaComp.Disabled) + continue; + var mapUid = seaComp.Owner; + MapId mapId = new MapId(mapUid.Id); + + var ships = new HashSet>(); + _lookup.GetEntitiesOnMap(mapId, ships); + count += ships.Count; + + } + return count; } } diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index 6ec2ba1036e..2fe8297f7da 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -14,8 +14,12 @@ public sealed class ShipsCCVars : CVars public static readonly CVarDef WindChangeTime = CVarDef.Create("ships.windchangetime", 1f, CVar.REPLICATED | CVar.SERVER); + public static readonly CVarDef WindDelay = + CVarDef.Create("ships.windDelay", 1f, CVar.REPLICATED | CVar.SERVER); + public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); + public static readonly CVarDef WindRotation = CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); } diff --git a/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs new file mode 100644 index 00000000000..1ceecdf2585 --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Imperial.Medieval.Ships.Sea; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class SeaComponent : Component +{ + [DataField("Disabled")] + public bool Disabled; +} From 54339d389cca3a2374fc50e1a96a4722aefef991 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Wed, 11 Mar 2026 08:10:40 +0300 Subject: [PATCH 12/20] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=8B=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B2=D0=B5=D1=81=D0=BB=D0=B0,=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B2=D0=BE=D0=BB=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Medieval/Ships/Oar/ServerOarSystem.cs | 104 ++++---- .../Medieval/Ships/Sail/SailSystem.cs | 4 +- .../Medieval/Ships/Storm/StormSystem.cs | 2 +- .../Ships/SummonShip/SummonShipSystem.cs | 2 +- .../Ships/Wave/Spawn/SpawnWindWaveSystem.cs | 89 +++++++ .../Medieval/Ships/Wave/WaveSystem.cs | 37 ++- .../Medieval/Ships/Wind/ServerWindSystem.cs | 2 +- .../Administration/Ships/ShipsCCVars.cs | 13 +- .../Medieval/Ships/Oar/OarComponent.cs | 2 +- .../Imperial/Medieval/Ships/Oar/OarSystem.cs | 227 +++++++----------- 10 files changed, 279 insertions(+), 203 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs index 94656be7289..ffa1ae86c51 100644 --- a/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Oar/ServerOarSystem.cs @@ -15,6 +15,8 @@ using Content.Shared.Database; using Content.Shared.Hands.EntitySystems; using Content.Shared.Imperial.Medieval.Ships.Oar; +using Content.Shared.Interaction.Components; +using Content.Shared.Movement.Components; namespace Content.Server.Imperial.Medieval.Ships.Oar; @@ -35,62 +37,17 @@ public sealed class OarSystem : EntitySystem [Dependency] private readonly RDWeightSystem _rdWeight = default!; public override void Initialize() { - SubscribeLocalEvent(OnOarAfterInteract); SubscribeLocalEvent(OnOarDoAfter); } - private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) + private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) { - var playerEntity = args.User; - - if (args.Handled || !args.CanReach ) - return; - - var boat = _transform.GetParentUid(playerEntity); - - if (boat == args.ClickLocation.EntityId) - return; - - var clickEntity = args.ClickLocation.EntityId; - if (boat == _transform.GetParentUid(clickEntity)) - return; - - var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; - var sdoAfter = new DoAfterArgs(EntityManager, - playerEntity, - time, - new OnOarDoAfterEvent(), - args.Used, - args.Target, - args.Used) + if (args.Cancelled) { - MovementThreshold = 0.5f, - BreakOnMove = true, - CancelDuplicate = true, - DistanceThreshold = 2, - BreakOnDamage = true, - RequireCanInteract = false, - BreakOnDropItem = true, - BreakOnHandChange = true, - NeedHand = true, - }; - _doAfter.TryStartDoAfter(sdoAfter); - var playerPosition = _transform.GetWorldPosition(playerEntity); - var boatPosition = _transform.ToWorldPosition(args.ClickLocation); - var direction = (playerPosition - boatPosition).Normalized(); - component.Direction = direction; - var pushAngle = direction.ToAngle(); - var boatAngle = _transform.GetWorldRotation(boat); - - var text = "по курсу"; - if (Math.Abs(pushAngle-boatAngle)*180 > 90) - text = "против курса"; - - _popup.PopupClient($"Ты гребёшь {text} лодки", playerEntity); - } + RemComp(args.User); + RemComp(args.User); + } - private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) - { var item = _hands.GetActiveItem(args.User); if (args.Cancelled || args.Handled || item == null) return; @@ -103,12 +60,16 @@ private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfte args.Repeat = true; } - private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) + private void Push(EntityUid item, Angle direction, float power, EntityUid player) { - power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; + power += power * (-10 + _skills.GetSkillLevel(player, "Strength")) * 0.1f; var boat = _transform.GetParentUid(player); + var boatAngle = _transform.GetWorldRotation(boat); + + var playerAngle = _transform.GetWorldRotation(player); + var entities = _lookup.GetEntitiesIntersecting(boat); if (entities.Count > 1000) @@ -124,16 +85,47 @@ private void Push(EntityUid item, Vector2 direction, float power, EntityUid play if (weight == 0) weight = 10; - var impulse = direction * (power / weight); + + var directionVec = Vector2.Zero; + while (direction > 2) + { + direction -= 2; + } + + + direction += Angle.FromDegrees(45); + // от 0 до 0.5 (1,0) + // от 0.5 до 1 (0,1) + // от 1 до 1,5 (0,-1) + // от 1,5 до 0 (-1,0) + if (direction > 1) + { + if (direction > 1.5) + directionVec = new Vector2(-1,0); + else + { + directionVec = new Vector2(0,-1); + } + } + else + { + if (direction > 0.5) + directionVec = new Vector2(0,1); + else + { + directionVec = new Vector2(1,0); + } + } + + directionVec = playerAngle.RotateVec(directionVec); + var impulse = directionVec * (power / weight); var angleimpulse = (power / weight); - if (direction.X < 0) - angleimpulse = -angleimpulse; if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) { _physics.WakeBody(boat); _physics.ApplyLinearImpulse(boat, impulse, body: body); - _physics.ApplyAngularImpulse(boat, angleimpulse, body: body); + // _physics.ApplyAngularImpulse(boat, angleimpulse, body: body); } } } diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index fdd79a7dbf7..3fe822d3974 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -65,7 +65,7 @@ public override void Update(float frameTime) var ships = new List(); var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); - var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); + var windForce = _cfg.GetCVar(ShipsCCVars.StormLevel); foreach (var sailComponent in EntityManager.EntityQuery()) { if (sailComponent.Folded) @@ -102,7 +102,7 @@ public override void Update(float frameTime) } var diffAngle = sailAngle - windAngle; - var force = windForce * MathF.Cos(diffAngle/180) * sailComponent.SailSize; + var force = windForce * MathF.Cos(diffAngle/180) * sailComponent.SailSize * _cfg.GetCVar(ShipsCCVars.WindPower); Push(sailEntity, force, boatAngle , push: sailComponent.Push, helm: sailComponent.Helm); if (!ships.Contains(boat)) diff --git a/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs b/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs index 61496dd6eae..935021994a6 100644 --- a/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Storm/StormSystem.cs @@ -8,6 +8,6 @@ public sealed class StormSystem : EntitySystem /// public override void Initialize() { - + } } diff --git a/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs index d0ea8da6673..4ffffda320d 100644 --- a/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/SummonShip/SummonShipSystem.cs @@ -57,7 +57,7 @@ private void Use(EntityUid uid, SummonShipComponent comp) _adminLog.Add(LogType.Action, LogImpact.High, $"Ошибка Загрузки грида из {path} сущность загрузки {uid}"); return; } - + EntityManager.QueueDeleteEntity(uid); } } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs new file mode 100644 index 00000000000..ffd6043761e --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs @@ -0,0 +1,89 @@ +using System.Numerics; +using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Ships.Sea; +using Content.Shared.Imperial.Medieval.Ships.ShipDrowning; +using Content.Shared.Nutrition.Components; +using Robust.Shared.Configuration; +using Robust.Shared.Map; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server.Imperial.Medieval.Ships.Wave.Spawn; + +/// +/// Призыв волн раз в какое то время +/// +public sealed class SpawnWindWaveSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly WaveSystem _wave = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private TimeSpan _nextCheckTime; + /// + public override void Initialize() + { + + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WaveDelay)); + + foreach (var seaComponent in EntityManager.EntityQuery()) + { + if (seaComponent.Disabled) + continue; + var sea = seaComponent.Owner; + var seaMapId = _transform.GetMapId(sea); + var ships = new HashSet>(); + _lookup.GetEntitiesOnMap(seaMapId, ships); + foreach (var shipcomp in ships) + { + var ship = shipcomp.Owner; + + var waveCount = _random.Next(1, 5); + var waveCoords = new EntityCoordinates(ship, GenerateWave()); + Vector2 force; + for (var i = 0; i < waveCount; i++) + { + waveCoords = new EntityCoordinates(ship, GenerateWave()); + force = waveCoords.Position.Normalized()*_cfg.GetCVar(ShipsCCVars.WaveForce)*-1; + _wave.SpawnWave(waveCoords,seaMapId, force); + } + + } + + + } + + } + } + private Vector2 GenerateWave(float radius = 0) + { + if (radius == 0) + radius = _cfg.GetCVar(ShipsCCVars.WaveSpawnRange); + // ----- 1. Случайные числа u, v ∈ [0,1) ----- + var u = _random.NextDouble(); + var v = _random.NextDouble(); + + // ----- 2. Полярные координаты (равномерно по площади) ----- + var rho = radius * Math.Sqrt(u); // расстояние от центра + var phi = 2.0 * Math.PI * v; // угол в радианах + + // ----- 3. Преобразуем в декартовы координаты ----- + var x = (float)(rho * Math.Cos(phi)); + var y = (float)(rho * Math.Sin(phi)); + + return new Vector2(x, y); + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index 59d098d24ec..5bfc8c3dc47 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Construction.Conditions; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.Imperial.Medieval.Additions; using Content.Shared.Maps; using Content.Shared.Mobs.Systems; using Content.Shared.Physics; @@ -69,8 +70,6 @@ private void Startup() if (Stages == null || Stages.Length == 0) return; - - // Используем for вместо foreach для изменения коллекции for (int i = 0; i < Stages.Length; i++) { var stage = Stages[i]; @@ -133,7 +132,7 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE continue; var stagelast = Stages.Length-1; - if (tile.TypeId == Stages[stagelast].Item2) + if (tile.TypeId == Stages[stagelast].Item2 || tile.IsEmpty) continue; var index = 0; foreach (var stage in Stages) @@ -151,4 +150,36 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE if (component.DeleteOnCollide) _entityManager.DeleteEntity(args.OurEntity); } + /// + /// призывает грид на куазанных координатах относительно какой то сущности и даёт ей силу + /// coords кординаты это сущность от которой считать и вектор смещения + /// mapId айди мапы + /// force вектор силы который мы прикладываем если надо + /// deleteOnCollide при столкновении удаляем + /// lifetime = 0 не будет удалять сущность по истечению таймера + /// + public void SpawnWave(EntityCoordinates coords, MapId mapId, Vector2 force = new Vector2(), bool deleteOnCollide = true, float lifetime = 60) + { + var grid = _mapManager.CreateGridEntity(mapId); + var waveComponent = EnsureComp(grid); + waveComponent.DeleteOnCollide = deleteOnCollide; + _tileDefinitionManager.TryGetDefinition("FloorWood", out var tileDefinition);// сюда поставить воду + if (tileDefinition == null) + return; + _map.SetTile(grid, new Vector2i(0,0),new Tile(tileDefinition.TileId, 0, 0));// создаёт тайлик воды надо поставить воду вон туда + if (HasComp(grid)) + { + _transform.SetCoordinates(grid, coords); + _physics.WakeBody(grid); + _physics.ApplyLinearImpulse(grid, force); + if (lifetime > 0) + { + var despawnComponent = EnsureComp(grid); + despawnComponent.Lifetime = lifetime; + despawnComponent.OriginalLifeTime = lifetime; + } + } + + + } } diff --git a/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs index 146f5ab8618..9a6c7027153 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wind/ServerWindSystem.cs @@ -43,7 +43,7 @@ public override void Update(float frameTime) private void RandomiseVind() { // ветерок сила - var windForce = _cfg.GetCVar(ShipsCCVars.WindPower); + var windForce = _cfg.GetCVar(ShipsCCVars.StormLevel); var countShips = FindShips(); if (windForce <= 0+countShips) diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index 2fe8297f7da..673f9f2cc00 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -15,12 +15,23 @@ public sealed class ShipsCCVars : CVars CVarDef.Create("ships.windchangetime", 1f, CVar.REPLICATED | CVar.SERVER); public static readonly CVarDef WindDelay = - CVarDef.Create("ships.windDelay", 1f, CVar.REPLICATED | CVar.SERVER); + CVarDef.Create("ships.winddelay", 1f, CVar.REPLICATED | CVar.SERVER); + public static readonly CVarDef WaveDelay = + CVarDef.Create("ships.wavedelay", 1f, CVar.REPLICATED | CVar.SERVER); public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); public static readonly CVarDef WindRotation = CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); + + public static readonly CVarDef StormLevel = + CVarDef.Create("ships.stormlevel", 1f, CVar.REPLICATED | CVar.SERVER); + + public static readonly CVarDef WaveForce = + CVarDef.Create("ships.waveforce", 1f, CVar.REPLICATED | CVar.SERVER); + + public static readonly CVarDef WaveSpawnRange = + CVarDef.Create("ships.wavespawnrange", 40f, CVar.REPLICATED | CVar.SERVER); } diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs index d54ee8f8d47..7574ed06be6 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarComponent.cs @@ -15,5 +15,5 @@ public sealed partial class OarComponent : Component public int SpeedModifier = 1; [DataField] - public Vector2 Direction; + public Angle Direction; } diff --git a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs index bbd88a65065..94fb5babda2 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Oar/OarSystem.cs @@ -1,137 +1,90 @@ -// using System.Numerics; -// using Content.Shared._RD.Weight.Components; -// using Content.Shared._RD.Weight.Systems; -// using Content.Shared.Coordinates; -// using Content.Shared.DoAfter; -// using Content.Shared.Imperial.Medieval.Skills; -// using Content.Shared.Interaction; -// using Content.Shared.Interaction.Events; -// using Content.Shared.Popups; -// using Robust.Shared.Physics; -// using Robust.Shared.Physics.Components; -// using Robust.Shared.Physics.Systems; -// -// -// using Content.Shared.Database; -// using Content.Shared.Hands.EntitySystems; -// -// -// namespace Content.Shared.Imperial.Medieval.Ships.Oar; -// -// /// -// /// По идее это должно быть тут, но млять грёбаный делей -// /// -// public sealed class OarSystem : EntitySystem -// { -// [Dependency] private readonly SharedPopupSystem _popup = default!; -// [Dependency] private readonly SharedPhysicsSystem _physics = default!; -// [Dependency] private readonly SharedTransformSystem _transform = default!; -// [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; -// [Dependency] private readonly SharedSkillsSystem _skills = default!; -// [Dependency] private readonly EntityManager _entManager = default!; -// [Dependency] private readonly EntityLookupSystem _lookup = default!; -// [Dependency] private readonly SharedHandsSystem _hands = default!; -// [Dependency] private readonly RDWeightSystem _rdWeight = default!; -// public override void Initialize() -// { -// SubscribeLocalEvent(OnOarAfterInteract); -// SubscribeLocalEvent(OnOarDoAfter); -// } -// -// private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) -// { -// var playerEntity = args.User; -// -// if (args.Handled || !args.CanReach ) -// return; -// -// -// var boat = _transform.GetParentUid(playerEntity); -// -// -// if (boat == args.ClickLocation.EntityId) -// return; -// -// var clickEntity = args.ClickLocation.EntityId; -// if (boat == _transform.GetParentUid(clickEntity)) -// return; -// -// var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; -// var sdoAfter = new DoAfterArgs(EntityManager, -// playerEntity, -// time, -// new OnOarDoAfterEvent(), -// args.Target, -// target: args.Used) -// { -// MovementThreshold = 0.5f, -// BreakOnMove = true, -// CancelDuplicate = true, -// DistanceThreshold = 2, -// BreakOnDamage = true, -// RequireCanInteract = false, -// BreakOnDropItem = true, -// BreakOnHandChange = true, -// }; -// -// -// _popup.PopupClient(Loc.GetString($"{playerEntity} {time} {args.Target} {args.Used}"), component.Owner, args.User); -// _doAfter.TryStartDoAfter(sdoAfter); -// -// -// -// -// var playerPosition = _transform.GetWorldPosition(playerEntity); -// var boatPosition = _transform.ToWorldPosition(args.ClickLocation); -// var direction = (playerPosition - boatPosition).Normalized(); -// component.Direction = direction; -// } -// -// private void OnOarDoAfter(EntityUid uid, OarComponent component, ref OnOarDoAfterEvent args) -// { -// var item = _hands.GetActiveItem(args.User); -// if (args.Cancelled || args.Handled || item == null) -// return; -// -// if (!TryComp(item, out var comp)) -// return; -// -// Push(item.Value, comp.Direction, comp.Power, args.User); -// args.Handled = true; -// args.Repeat = true; -// } -// -// private void Push(EntityUid item, Vector2 direction, float power, EntityUid player) -// { -// power += power * (10 - _skills.GetSkillLevel(player, "Strength")) * 0.1f; -// -// var boat = _transform.GetParentUid(player); -// -// var entities = _lookup.GetEntitiesIntersecting(boat); -// -// if (entities.Count > 1000) -// return; -// -// var weight = _rdWeight.GetTotal(boat); -// -// foreach (var entity in entities) -// { -// if (HasComp(entity)) -// weight += _rdWeight.GetTotal(entity); -// } -// -// if (weight == 0) -// weight = 10; -// var impulse = direction * (power / weight); -// var angleimpulse = (power / weight); -// if (direction.X < 0) -// angleimpulse = -angleimpulse; -// -// if (EntityManager.TryGetComponent(boat, out PhysicsComponent? body)) -// { -// _physics.WakeBody(boat); -// _physics.ApplyLinearImpulse(boat, impulse, body: body); -// _physics.ApplyAngularImpulse(boat, angleimpulse, body: body); -// } -// } -// } +using System.Numerics; +using Content.Shared._RD.Weight.Components; +using Content.Shared._RD.Weight.Systems; +using Content.Shared.Coordinates; +using Content.Shared.DoAfter; +using Content.Shared.Imperial.Medieval.Skills; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; + + +using Content.Shared.Database; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction.Components; +using Content.Shared.Movement.Components; +using Robust.Shared.Map; + + +namespace Content.Shared.Imperial.Medieval.Ships.Oar; + +/// +/// По идее это должно быть тут, но млять грёбаный делей +/// +public sealed class OarSystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedSkillsSystem _skills = default!; + [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly RDWeightSystem _rdWeight = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + public override void Initialize() + { + SubscribeLocalEvent(OnOarAfterInteract); + } + + private void OnOarAfterInteract(EntityUid uid, OarComponent component, AfterInteractEvent args) + { + var playerEntity = args.User; + + if (args.Handled || !args.CanReach ) + return; + + var boat = _transform.GetParentUid(playerEntity); + + if (boat == args.ClickLocation.EntityId) + return; + + var clickEntity = args.ClickLocation.EntityId; + if (boat == _transform.GetParentUid(clickEntity)) + return; + + var time = 7 -_skills.GetSkillLevel(playerEntity, "Agility") * 0.3f; + var sdoAfter = new DoAfterArgs(EntityManager, + playerEntity, + time, + new OnOarDoAfterEvent(), + args.Used, + args.Target, + args.Used) + { + MovementThreshold = 0.1f, + BreakOnMove = true, + CancelDuplicate = true, + DistanceThreshold = 2, + BreakOnDamage = true, + RequireCanInteract = false, + BreakOnDropItem = true, + BreakOnHandChange = true, + NeedHand = true, + }; + _doAfter.TryStartDoAfter(sdoAfter); + var playerPosition = _transform.GetWorldPosition(playerEntity); + var boatPosition = _transform.ToWorldPosition(args.ClickLocation); + var direction = (playerPosition - boatPosition).ToAngle(); + component.Direction = direction; + + _popup.PopupClient($"Ты гребёшь в сторону взгляда", playerEntity); + EnsureComp(playerEntity); + EnsureComp(playerEntity); + + } +} From 5f85677568a215ea4d763c40d69294a3e496f9f9 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:52:25 +0300 Subject: [PATCH 13/20] =?UTF-8?q?=D0=BF=D1=80=D0=B8=D0=B2=D0=B5=D1=82=20?= =?UTF-8?q?=D0=BA=D1=83=D1=87=D0=B0=20=D1=86=D0=B2=D0=B0=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ships/Wave/Spawn/SpawnWindWaveSystem.cs | 24 ++++++++++++----- .../Medieval/Ships/Wave/WaveSystem.cs | 15 ++++++----- .../Administration/Ships/ShipsCCVars.cs | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs index ffd6043761e..528cd129de1 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs @@ -51,7 +51,7 @@ public override void Update(float frameTime) { var ship = shipcomp.Owner; - var waveCount = _random.Next(1, 5); + var waveCount = _random.Next(0, (int)_cfg.GetCVar(ShipsCCVars.StormLevel)); var waveCoords = new EntityCoordinates(ship, GenerateWave()); Vector2 force; for (var i = 0; i < waveCount; i++) @@ -68,22 +68,32 @@ public override void Update(float frameTime) } } - private Vector2 GenerateWave(float radius = 0) + private Vector2 GenerateWave(float radius = 0, float targetAngle = 0, float halfAngle = 3.0235f) { + if (halfAngle == 3.0235f) // я прифигею если вы рандомно сможете получить это число + halfAngle = _cfg.GetCVar(ShipsCCVars.WaveSpawnAngle)*_cfg.GetCVar(ShipsCCVars.StormLevel); + + halfAngle /= 180; + + if (targetAngle == 0) + targetAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); if (radius == 0) radius = _cfg.GetCVar(ShipsCCVars.WaveSpawnRange); - // ----- 1. Случайные числа u, v ∈ [0,1) ----- + var u = _random.NextDouble(); var v = _random.NextDouble(); - // ----- 2. Полярные координаты (равномерно по площади) ----- - var rho = radius * Math.Sqrt(u); // расстояние от центра - var phi = 2.0 * Math.PI * v; // угол в радианах + var rho = radius * Math.Sqrt(u); + + var phiMin = targetAngle - halfAngle; + var phiMax = targetAngle + halfAngle; + var phi = phiMin + (phiMax - phiMin) * v; + - // ----- 3. Преобразуем в декартовы координаты ----- var x = (float)(rho * Math.Cos(phi)); var y = (float)(rho * Math.Sin(phi)); return new Vector2(x, y); } + } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index 5bfc8c3dc47..b0e79dca496 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Imperial.Medieval.Additions; +using Content.Shared.Imperial.Medieval.Administration.Ships; using Content.Shared.Maps; using Content.Shared.Mobs.Systems; using Content.Shared.Physics; @@ -15,6 +16,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Physics; using Robust.Shared.Physics.Events; using Robust.Shared.Player; @@ -45,6 +47,7 @@ public sealed class WaveSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IAdminLogManager _adminlogs = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; private readonly Random _random = new(); @@ -55,7 +58,6 @@ public sealed class WaveSystem : EntitySystem ("Plating", (ushort)3), ("FloorWhite", (ushort)4) }; - private const float RadiusTiles = 3f; private bool _initialized; public override void Initialize() @@ -101,13 +103,15 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE var centerTilePos = _map.MapToGrid(grid, collisionPos); - var antiradius = (int)RadiusTiles*-1; + var radiusTiles = _cfg.GetCVar(ShipsCCVars.WaveRadiusTiles) + _cfg.GetCVar(ShipsCCVars.StormLevel); + + var antiradius = (int)radiusTiles*-1; var nearbyTiles = new List(); - for (int dx = antiradius; dx <= RadiusTiles; dx++) + for (int dx = antiradius; dx <= radiusTiles; dx++) { - for (int dy = antiradius; dy <= RadiusTiles; dy++) + for (int dy = antiradius; dy <= radiusTiles; dy++) { var tilePos = centerTilePos + new EntityCoordinates(gridEntity, new Vector2(dx, dy)) ; var tile = _map.GetTileRef(grid, tilePos); @@ -116,7 +120,7 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE continue; var distance = Vector2.Distance(centerTilePos.Position, tilePos.Position); - if (distance <= RadiusTiles) + if (distance <= radiusTiles) nearbyTiles.Add(((int)tilePos.X, (int)tilePos.Y)); } } @@ -124,7 +128,6 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE _random.Shuffle(nearbyTiles); int tilesToReplace = Math.Min(_random.Next(0,4), nearbyTiles.Count); - _popup.PopupEntity(Loc.GetString($" {tilesToReplace}"), uid); for (int i = 0; i < tilesToReplace; i++) { var tilePos = nearbyTiles[i]; diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index 673f9f2cc00..a7712e89a0e 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -8,30 +8,42 @@ namespace Content.Shared.Imperial.Medieval.Administration.Ships; [CVarDefs] public sealed class ShipsCCVars : CVars { + // максимальная скорость public static readonly CVarDef ShipsMaxSpeed = CVarDef.Create("ships.maxspeed", 20f, CVar.REPLICATED | CVar.SERVER); - + // как часто меняется ветер public static readonly CVarDef WindChangeTime = CVarDef.Create("ships.windchangetime", 1f, CVar.REPLICATED | CVar.SERVER); - + // как часто ветер дует public static readonly CVarDef WindDelay = CVarDef.Create("ships.winddelay", 1f, CVar.REPLICATED | CVar.SERVER); + // как часто появляются волны public static readonly CVarDef WaveDelay = CVarDef.Create("ships.wavedelay", 1f, CVar.REPLICATED | CVar.SERVER); - +// сила с которой ветер толкает public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); - +// угол поворота ветра public static readonly CVarDef WindRotation = CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); - +// уровень шторма public static readonly CVarDef StormLevel = CVarDef.Create("ships.stormlevel", 1f, CVar.REPLICATED | CVar.SERVER); - +// скорость с которой волна спавнится public static readonly CVarDef WaveForce = CVarDef.Create("ships.waveforce", 1f, CVar.REPLICATED | CVar.SERVER); - +// радиус спавна волн public static readonly CVarDef WaveSpawnRange = CVarDef.Create("ships.wavespawnrange", 40f, CVar.REPLICATED | CVar.SERVER); +// угол разброса волн + public static readonly CVarDef WaveSpawnAngle = + CVarDef.Create("ships.wavespawnangle", 10f, CVar.REPLICATED | CVar.SERVER); + // в каком радиусе ломает волна + public static readonly CVarDef WaveRadiusTiles = + CVarDef.Create("ships.waveradiustiles", 3f, CVar.REPLICATED | CVar.SERVER); +// какое максимальное количество тайлов может сломать волна + public static readonly CVarDef WaveMaxBreakCount = + CVarDef.Create("ships.wavemaxbreakcount", 3f, CVar.REPLICATED | CVar.SERVER); + } From a1781fba31918fbc2394125e12cc30f3cc2ec920 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Thu, 12 Mar 2026 22:00:25 +0300 Subject: [PATCH 14/20] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20?= =?UTF-8?q?=D1=86=D0=B2=D0=B0=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ShipTeleporting/ShipTeleportSystem.cs | 13 +++++++++ .../Medieval/Ships/Wave/WaveSystem.cs | 28 +++++++++++++++++-- .../Administration/Ships/ShipsCCVars.cs | 21 ++++++++------ 3 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs new file mode 100644 index 00000000000..f09fa773645 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Imperial.Medieval.Ships.ShipTeleporting; + +/// +/// This handles... +/// +public sealed class ShipTeleportSystem : EntitySystem +{ + /// + public override void Initialize() + { + + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index b0e79dca496..3cd9ab76f81 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Physics; using Content.Shared.Popups; +using Content.Shared.Tag; using Content.Shared.Tiles; using Content.Shared.Trigger.Components; using Robust.Server.GameObjects; @@ -48,6 +49,7 @@ public sealed class WaveSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IAdminLogManager _adminlogs = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly TagSystem _tags = default!; private readonly Random _random = new(); @@ -91,6 +93,8 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE return; if (component.HitList.Contains(args.OtherEntity)) return; + if (_cfg.GetCVar(ShipsCCVars.WaveMinToBreakLevel) > _cfg.GetCVar(ShipsCCVars.StormLevel)) + _entityManager.DeleteEntity(args.OurEntity); EnsureComp(args.OurEntity); var collisionPos = _transform.GetMapCoordinates(args.OurEntity); var gridEntity = args.OtherEntity; @@ -109,6 +113,7 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE var nearbyTiles = new List(); + for (int dx = antiradius; dx <= radiusTiles; dx++) { for (int dy = antiradius; dy <= radiusTiles; dy++) @@ -118,6 +123,19 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE if (tile.Tile.IsEmpty) continue; + var wallcheck = new HashSet(); + _lookup.GetEntitiesInTile(tile, wallcheck); + var stop = false; + foreach (var wall in wallcheck) + { + if (_tags.HasTag(wall, "Wall")) + { + stop = true; + } + + } + if (stop) + continue; var distance = Vector2.Distance(centerTilePos.Position, tilePos.Position); if (distance <= radiusTiles) @@ -127,7 +145,7 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE _random.Shuffle(nearbyTiles); - int tilesToReplace = Math.Min(_random.Next(0,4), nearbyTiles.Count); + int tilesToReplace = Math.Min(_random.Next(0,_cfg.GetCVar(ShipsCCVars.WaveMaxBreakCount)), nearbyTiles.Count); for (int i = 0; i < tilesToReplace; i++) { var tilePos = nearbyTiles[i]; @@ -163,7 +181,13 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE /// public void SpawnWave(EntityCoordinates coords, MapId mapId, Vector2 force = new Vector2(), bool deleteOnCollide = true, float lifetime = 60) { + if (!_map.TryGetMap(mapId, out var mapEntity)) + { + return; + } + var grid = _mapManager.CreateGridEntity(mapId); + _transform.AttachToGridOrMap(grid); var waveComponent = EnsureComp(grid); waveComponent.DeleteOnCollide = deleteOnCollide; _tileDefinitionManager.TryGetDefinition("FloorWood", out var tileDefinition);// сюда поставить воду @@ -182,7 +206,5 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE despawnComponent.OriginalLifeTime = lifetime; } } - - } } diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index a7712e89a0e..06e3b9f326e 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -20,30 +20,33 @@ public sealed class ShipsCCVars : CVars // как часто появляются волны public static readonly CVarDef WaveDelay = CVarDef.Create("ships.wavedelay", 1f, CVar.REPLICATED | CVar.SERVER); -// сила с которой ветер толкает + // сила с которой ветер толкает public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); -// угол поворота ветра + // угол поворота ветра public static readonly CVarDef WindRotation = CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); -// уровень шторма + // уровень шторма public static readonly CVarDef StormLevel = CVarDef.Create("ships.stormlevel", 1f, CVar.REPLICATED | CVar.SERVER); -// скорость с которой волна спавнится + // скорость с которой волна спавнится public static readonly CVarDef WaveForce = CVarDef.Create("ships.waveforce", 1f, CVar.REPLICATED | CVar.SERVER); -// радиус спавна волн + // радиус спавна волн public static readonly CVarDef WaveSpawnRange = CVarDef.Create("ships.wavespawnrange", 40f, CVar.REPLICATED | CVar.SERVER); -// угол разброса волн + // угол разброса волн public static readonly CVarDef WaveSpawnAngle = CVarDef.Create("ships.wavespawnangle", 10f, CVar.REPLICATED | CVar.SERVER); // в каком радиусе ломает волна public static readonly CVarDef WaveRadiusTiles = CVarDef.Create("ships.waveradiustiles", 3f, CVar.REPLICATED | CVar.SERVER); -// какое максимальное количество тайлов может сломать волна - public static readonly CVarDef WaveMaxBreakCount = - CVarDef.Create("ships.wavemaxbreakcount", 3f, CVar.REPLICATED | CVar.SERVER); + // какое максимальное количество тайлов может сломать волна + public static readonly CVarDef WaveMaxBreakCount = + CVarDef.Create("ships.wavemaxbreakcount", 3, CVar.REPLICATED | CVar.SERVER); + // Минимальный уровень для поломки лодки (Шторма если кто не понял) + public static readonly CVarDef WaveMinToBreakLevel = + CVarDef.Create("ships.wavemintobreaklevel", 2, CVar.REPLICATED | CVar.SERVER); } From 696da023fff109d120464161543eaef09e72d4e2 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Fri, 13 Mar 2026 08:29:09 +0300 Subject: [PATCH 15/20] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D0=B8=D0=BC=20?= =?UTF-8?q?=D0=B2=D0=BE=D0=BB=D0=BD=D1=8B,=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D1=8F=D0=B5=D0=BC=20=D0=BF=D0=B5=D1=80=D0=B2=D0=B8?= =?UTF-8?q?=D1=87=D0=BD=D1=8B=D0=B9=20=D1=82=D0=B5=D0=BB=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sea/Generation/SeasGenerationSystem.cs | 13 +++++ .../ShipTeleporting/ShipTeleportSystem.cs | 53 ++++++++++++++++++- .../Ships/Wave/Spawn/SpawnWindWaveSystem.cs | 2 +- .../Medieval/Ships/Wave/WaveSystem.cs | 6 +-- 4 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs new file mode 100644 index 00000000000..7fa7201d13c --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Imperial.Medieval.Ships.Sea.Generation; + +/// +/// This handles... +/// +public sealed class SeasGenerationSystem : EntitySystem +{ + /// + public override void Initialize() + { + + } +} diff --git a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs index f09fa773645..1010bbd6d73 100644 --- a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs @@ -1,3 +1,11 @@ +using Content.Server.Imperial.Medieval.Ships.Wave; +using Content.Shared.Imperial.Medieval.Administration.Ships; +using Content.Shared.Imperial.Medieval.Ships.Sea; +using Robust.Shared.Configuration; +using Robust.Shared.Map; +using Robust.Shared.Random; +using Robust.Shared.Timing; + namespace Content.Server.Imperial.Medieval.Ships.ShipTeleporting; /// @@ -5,9 +13,52 @@ namespace Content.Server.Imperial.Medieval.Ships.ShipTeleporting; /// public sealed class ShipTeleportSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly WaveSystem _wave = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private TimeSpan _nextCheckTime; /// public override void Initialize() { - + + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + + if (curTime > _nextCheckTime) + { + _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WaveDelay)); + + foreach (var seaComponent in EntityManager.EntityQuery()) + { + var ship = seaComponent.Owner; + + var coords = _transform.GetMapCoordinates(ship); + if (Math.Abs(coords.X) > 250 || Math.Abs(coords.Y) > 250) + { + TeleportShip(ship, coords); + } + + } + } + } + + private void TeleportShip(EntityUid ship, MapCoordinates coords) + { + var newcoords = coords.Position; + if (Math.Abs(newcoords.X) > 250) + newcoords.X *= -1; + if (Math.Abs(newcoords.Y) > 250) + newcoords.Y *= -1; + var nmapcoords = new MapCoordinates(newcoords, coords.MapId); + _transform.SetMapCoordinates(ship, nmapcoords); } } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs index 528cd129de1..a256a6ee574 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/Spawn/SpawnWindWaveSystem.cs @@ -93,7 +93,7 @@ private Vector2 GenerateWave(float radius = 0, float targetAngle = 0, float half var x = (float)(rho * Math.Cos(phi)); var y = (float)(rho * Math.Sin(phi)); - return new Vector2(x, y); + return new Vector2(x*100, y); } } diff --git a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs index 3cd9ab76f81..99df4fa16e8 100644 --- a/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Wave/WaveSystem.cs @@ -182,18 +182,16 @@ private void OnCollide(EntityUid uid, WaveComponent component, ref StartCollideE public void SpawnWave(EntityCoordinates coords, MapId mapId, Vector2 force = new Vector2(), bool deleteOnCollide = true, float lifetime = 60) { if (!_map.TryGetMap(mapId, out var mapEntity)) - { return; - } var grid = _mapManager.CreateGridEntity(mapId); - _transform.AttachToGridOrMap(grid); + _transform.SetParent(grid, mapEntity.Value); var waveComponent = EnsureComp(grid); waveComponent.DeleteOnCollide = deleteOnCollide; _tileDefinitionManager.TryGetDefinition("FloorWood", out var tileDefinition);// сюда поставить воду if (tileDefinition == null) return; - _map.SetTile(grid, new Vector2i(0,0),new Tile(tileDefinition.TileId, 0, 0));// создаёт тайлик воды надо поставить воду вон туда + _map.SetTile(grid, new Vector2i(0,0), new Tile(tileDefinition.TileId, 0, 0));// создаёт тайлик воды надо поставить воду вон туда if (HasComp(grid)) { _transform.SetCoordinates(grid, coords); From cbd9262404760e6cab8a0ed096995f39ca9c7ce9 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Sat, 14 Mar 2026 10:38:05 +0300 Subject: [PATCH 16/20] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=B8=D1=87?= =?UTF-8?q?=D0=BD=D0=B0=D1=8F=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BC=D0=BE=D1=80=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ура я починил материнку --- .../Medieval/Barrier/MagicBarrierComponent.cs | 17 ++ .../Medieval/Ships/Sail/SailSystem.cs | 3 +- .../Ships/Sea/Init/NotSeaComponent.cs | 14 ++ .../Ships/Sea/Init/SeaMatrixInitSystem.cs | 158 ++++++++++++++++++ .../Administration/Ships/ShipsCCVars.cs | 3 + 5 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Sea/Init/NotSeaComponent.cs create mode 100644 Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs diff --git a/Content.Server/Imperial/Medieval/Barrier/MagicBarrierComponent.cs b/Content.Server/Imperial/Medieval/Barrier/MagicBarrierComponent.cs index 225749c267e..ad4d8d58e44 100644 --- a/Content.Server/Imperial/Medieval/Barrier/MagicBarrierComponent.cs +++ b/Content.Server/Imperial/Medieval/Barrier/MagicBarrierComponent.cs @@ -1,3 +1,6 @@ +using System.Numerics; +using Content.Server.Imperial.Medieval.Ships.Sea.Init; +using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Prototypes; @@ -21,15 +24,19 @@ public sealed partial class MagicBarrierComponent : Component [DataField] public float MaxStability = 60f; + [DataField] public float Lose = 0.7f; + [DataField] public float Rate = 1.5f; + [DataField] public int Cycle = 0; [DataField, ViewVariables(VVAccess.ReadOnly)] public string EffectSoundOnScrollAdd = "/Audio/Imperial/Medieval/scroll_use.ogg"; + [DataField, ViewVariables(VVAccess.ReadOnly)] public string EffectSoundOnFinish = "/Audio/Imperial/Medieval/magic_craft.ogg"; @@ -49,5 +56,15 @@ public sealed partial class MagicBarrierComponent : Component [DataField] public Dictionary ReviveCount = new(); + + // Приветик, делаем генерацию морей + [DataField] + public bool SeaInitalazed = false; + + + [DataField] + public SeaMatrix? SeaMatrix = null; } } + + diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index 3fe822d3974..3fc47b04403 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -62,7 +62,8 @@ public override void Update(float frameTime) if (curTime > _nextCheckTime) { _nextCheckTime = curTime + TimeSpan.FromSeconds(_cfg.GetCVar(ShipsCCVars.WindDelay)); - + if (!_cfg.GetCVar(ShipsCCVars.WindEnabled)) + return; var ships = new List(); var windAngle = _cfg.GetCVar(ShipsCCVars.WindRotation); var windForce = _cfg.GetCVar(ShipsCCVars.StormLevel); diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/NotSeaComponent.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/NotSeaComponent.cs new file mode 100644 index 00000000000..dc45733eb13 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/NotSeaComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Imperial.Medieval.Ships.Sea.Init; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class NotSeaComponent : Component +{ + [DataField("NotSeaPosX")] + public int NotSeaPosX; + + [DataField("NotSeaPosY")] + public int NotSeaPosY; +} diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs new file mode 100644 index 00000000000..daa7a97f727 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs @@ -0,0 +1,158 @@ +using Content.Server.MagicBarrier.Components; +using Robust.Server.GameObjects; +using Robust.Shared.Map; + +namespace Content.Server.Imperial.Medieval.Ships.Sea.Init; + +/// +/// по идее должно работать с матрицой моря +/// грёбаные костыли, я только примерно понимаю что делаю но вроде как всё нормально(нет я не пишу нейронкой, просто не особо понимаю, райдер помогает исправлять слишком грубые ошибки) +/// +public sealed class SeaMatrixInitSystem : EntitySystem +{ + [Dependency] private readonly MapSystem _map = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EntityManager _entManager = default!; + /// + public override void Initialize() + { + SubscribeLocalEvent(OnInit); + } + + private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentInit args) + { + if (component.SeaMatrix == null) + component.SeaMatrix = new SeaMatrix(new List<(int x, int y)> + { + (2, 2), (2, 3), (2, 4), + (3, 2), (3, 3), (3, 4), + (4, 2), (4, 3), (4, 4), + }); + + if (component.SeaInitalazed) + return; + + var seamat = component.SeaMatrix; + + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + if (!seamat.NeedsGeneration(x, y)) + { + if (seamat.GetCell(x, y).SeaId == new MapId(1)) + { + seamat.SetSeaId(x, y, TryFoundMap(x,y)); + } + } + var mapUid = _map.CreateMap(); + var mapId = _transform.GetMapId(mapUid); + seamat.SetSeaId(x,y,mapId); + seamat.SetGenerated(x,y,true); + + } + } + + + } + + /// + /// ищем мапу либо -1 пишем + /// + public MapId TryFoundMap(int x, int y) + { + if (_entManager == null) + throw new InvalidOperationException("EntityManager not initialized!"); + foreach (var notSeaComponent in _entManager.EntityQuery()) + { + if (notSeaComponent.NotSeaPosX != x || notSeaComponent.NotSeaPosY != y) + continue; + + return _transform.GetMapId(notSeaComponent.Owner); + } + return new MapId(-1); + } + + + + + + +} +public sealed class SeaMatrix +{ + + private readonly SeaCell[,] _matrix = new SeaCell[5, 5]; // 5x5 матрица + + + public SeaMatrix(IEnumerable<(int x, int y)>? nonGeneratableCoordinates = null) + { + + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + _matrix[x, y] = new SeaCell + { + SeaId = new MapId(-1), + NeedGenerate = true + }; + } + } + + + if (nonGeneratableCoordinates != null) + { + foreach (var (x, y) in nonGeneratableCoordinates) + { + if (x >= 0 && x < 5 && y >= 0 && y < 5) + { + _matrix[x, y].NeedGenerate = false; + } + } + } + } + + + /// + /// Получение ячейки по координатам + /// + public SeaCell GetCell(int x, int y) + { + if (x < 0 || x >= 5 || y < 0 || y >= 5) + throw new ArgumentOutOfRangeException("Coordinates out of 5x5 range!"); + + return _matrix[x, y]; + } + /// + /// Установка ID моря + /// + public void SetSeaId(int x, int y, MapId seaId) + { + if (x < 0 || x >= 5 || y < 0 || y >= 5) + throw new ArgumentOutOfRangeException("Coordinates out of 5x5 range!"); + + _matrix[x, y].SeaId = seaId; + _matrix[x, y].NeedGenerate = false; // Сбрасываем флаг после назначения ID + } + + /// + /// Проверка, нужно ли генерировать море в ячейке + /// + public bool NeedsGeneration(int x, int y) + { + return GetCell(x, y).NeedGenerate; + } + /// + /// Ставим значение ячейке + /// + public void SetGenerated(int x, int y, bool generated) + { + _matrix[x, y].NeedGenerate = generated; + } +} +public struct SeaCell +{ + public MapId SeaId; // Уникальный ID моря + public bool NeedGenerate; // Флаг, что море нужно сгенерировать +} diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index 06e3b9f326e..68016f2c6c6 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -20,6 +20,9 @@ public sealed class ShipsCCVars : CVars // как часто появляются волны public static readonly CVarDef WaveDelay = CVarDef.Create("ships.wavedelay", 1f, CVar.REPLICATED | CVar.SERVER); + // Минимальный уровень для поломки лодки (Шторма если кто не понял) + public static readonly CVarDef WindEnabled = + CVarDef.Create("ships.waveenabled", true, CVar.REPLICATED | CVar.SERVER); // сила с которой ветер толкает public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); From 3a72432560574bdb37164801a587617d7cd512b5 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Tue, 17 Mar 2026 18:45:21 +0300 Subject: [PATCH 17/20] =?UTF-8?q?=D0=BA=D0=BE=D1=80=D0=B0=D0=B1=D0=BB?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=B2=D0=B0=D1=8E=D1=82=20=D0=BC=D0=B5=D0=B6?= =?UTF-8?q?=D0=B4=D1=83=20=D0=BA=D0=B0=D1=80=D1=82=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Medieval/Ships/Helm/HelmSystem.cs | 1 - .../Medieval/Ships/Sail/SailSystem.cs | 6 +- .../Ships/Sea/Init/SeaMatrixInitSystem.cs | 23 +++++- .../ShipTeleporting/ShipTeleportSystem.cs | 71 ++++++++++++++++--- .../Medieval/Ships/Sea/SeaComponent.cs | 2 +- 5 files changed, 88 insertions(+), 15 deletions(-) diff --git a/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs b/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs index f248a39cf0b..44516d3203a 100644 --- a/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Helm/HelmSystem.cs @@ -70,7 +70,6 @@ public float CheckForce(EntityUid boat, EntityUid helm) var helmsin = MathF.Sin(boatAngle); var helmVector = _physics.GetMapLinearVelocity(boat); var helmForce = Math.Abs(helmVector.X) + Math.Abs(helmVector.Y); - _adminLog.Add(LogType.Action, LogImpact.Extreme, $"хельма {helmForce}"); return helmForce; } } diff --git a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs index 3fc47b04403..4d5c1c227e8 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sail/SailSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Imperial.Medieval.Administration.Ships; using Content.Shared.Imperial.Medieval.Ships.Sail; +using Content.Shared.Imperial.Medieval.Ships.Sea; using Content.Shared.Imperial.Medieval.Ships.ShipDrowning; using Content.Shared.Imperial.Medieval.Ships.Wind; using Content.Shared.Imperial.Medieval.Skills; @@ -103,7 +104,10 @@ public override void Update(float frameTime) } var diffAngle = sailAngle - windAngle; - var force = windForce * MathF.Cos(diffAngle/180) * sailComponent.SailSize * _cfg.GetCVar(ShipsCCVars.WindPower); + var wind = _cfg.GetCVar(ShipsCCVars.WindPower); + if (!HasComp < SeaComponent > (_transform.GetMap(boat))) + wind = 1; + var force = windForce * MathF.Cos(diffAngle/180) * sailComponent.SailSize * wind; Push(sailEntity, force, boatAngle , push: sailComponent.Push, helm: sailComponent.Helm); if (!ships.Contains(boat)) diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs index daa7a97f727..a0721dfbaca 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs @@ -1,4 +1,5 @@ using Content.Server.MagicBarrier.Components; +using Content.Shared.Imperial.Medieval.Ships.Sea; using Robust.Server.GameObjects; using Robust.Shared.Map; @@ -13,6 +14,7 @@ public sealed class SeaMatrixInitSystem : EntitySystem [Dependency] private readonly MapSystem _map = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityManager _entManager = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; /// public override void Initialize() { @@ -46,7 +48,9 @@ private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentIni } } var mapUid = _map.CreateMap(); + _metaData.SetEntityName(mapUid, $"Море {x} {y}"); var mapId = _transform.GetMapId(mapUid); + AddComp(mapUid); seamat.SetSeaId(x,y,mapId); seamat.SetGenerated(x,y,true); @@ -113,7 +117,6 @@ public SeaMatrix(IEnumerable<(int x, int y)>? nonGeneratableCoordinates = null) } } - /// /// Получение ячейки по координатам /// @@ -150,6 +153,24 @@ public void SetGenerated(int x, int y, bool generated) { _matrix[x, y].NeedGenerate = generated; } + + /// + /// Поиск ячейки по айди + /// + public (int, int)? FoundSell(MapId seaId, SeaMatrix seaMatrix) + { + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + if (_matrix[x, y].SeaId == seaId) + { + return (x, y); + } + } + } + return null; + } } public struct SeaCell { diff --git a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs index 1010bbd6d73..d71ee711acf 100644 --- a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs @@ -1,8 +1,12 @@ +using Content.Server.Administration.Logs; using Content.Server.Imperial.Medieval.Ships.Wave; +using Content.Server.MagicBarrier.Components; +using Content.Shared.Database; using Content.Shared.Imperial.Medieval.Administration.Ships; using Content.Shared.Imperial.Medieval.Ships.Sea; using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -19,6 +23,7 @@ public sealed class ShipTeleportSystem : EntitySystem [Dependency] private readonly WaveSystem _wave = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; private TimeSpan _nextCheckTime; /// @@ -39,14 +44,22 @@ public override void Update(float frameTime) foreach (var seaComponent in EntityManager.EntityQuery()) { - var ship = seaComponent.Owner; - var coords = _transform.GetMapCoordinates(ship); - if (Math.Abs(coords.X) > 250 || Math.Abs(coords.Y) > 250) + var sea = seaComponent.Owner; + + var entities = new HashSet>(); + _lookup.GetEntitiesOnMap(_transform.GetMapId(sea), entities); + foreach (var shipComp in entities) { - TeleportShip(ship, coords); - } + var ship = shipComp.Owner; + + var coords = _transform.GetMapCoordinates(ship); + if (Math.Abs(coords.X) > 250 || Math.Abs(coords.Y) > 250) + { + TeleportShip(ship, coords); + } + } } } } @@ -54,11 +67,47 @@ public override void Update(float frameTime) private void TeleportShip(EntityUid ship, MapCoordinates coords) { var newcoords = coords.Position; - if (Math.Abs(newcoords.X) > 250) - newcoords.X *= -1; - if (Math.Abs(newcoords.Y) > 250) - newcoords.Y *= -1; - var nmapcoords = new MapCoordinates(newcoords, coords.MapId); - _transform.SetMapCoordinates(ship, nmapcoords); + foreach (var magicBarrier in EntityManager.EntityQuery()) + { + var seematrix = magicBarrier.SeaMatrix; + if ( seematrix is null) + continue; + var seamap = seematrix.FoundSell(coords.MapId, seematrix); + if (seamap is null) + continue; + + var (x, y) = seamap.Value; + + if (Math.Abs(newcoords.X) > 250) + { + if (newcoords.X > 0) + x += 1; + else + { + x -= 1; + } + newcoords.X *= -1; + } + + if (Math.Abs(newcoords.Y) > 250) + { + if (newcoords.Y > 0) + y += 1; + else + { + y -= 1; + } + newcoords.Y *= -1; + } + + var mapId = seematrix.GetCell(x,y).SeaId; + + var nmapcoords = new MapCoordinates(newcoords, mapId); + + _transform.SetMapCoordinates(ship, nmapcoords); + + } + + } } diff --git a/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs b/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs index 1ceecdf2585..5479917fa29 100644 --- a/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs +++ b/Content.Shared/Imperial/Medieval/Ships/Sea/SeaComponent.cs @@ -1,7 +1,7 @@ namespace Content.Shared.Imperial.Medieval.Ships.Sea; /// -/// This is used for... +/// Компонент моря /// [RegisterComponent] public sealed partial class SeaComponent : Component From 4db80952ebefcb5ccd20546af786df8b9b4b2f41 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Wed, 18 Mar 2026 19:34:52 +0300 Subject: [PATCH 18/20] =?UTF-8?q?=D1=82=D0=B5=D0=BB=D0=B5=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=B8=20=D0=BC=D0=BE?= =?UTF-8?q?=D1=80=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ships/Sea/Init/MapForSeasInitComponent.cs | 13 ++++ .../Ships/Sea/Init/SeaMatrixInitSystem.cs | 32 +++++++++ .../ShipTeleporting/ShipTeleportSystem.cs | 26 ++++++-- .../Administration/Ships/ShipsCCVars.cs | 66 +++++++++++++++---- 4 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Sea/Init/MapForSeasInitComponent.cs diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/MapForSeasInitComponent.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/MapForSeasInitComponent.cs new file mode 100644 index 00000000000..0a8a04af659 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/MapForSeasInitComponent.cs @@ -0,0 +1,13 @@ +namespace Content.Server.Imperial.Medieval.Ships.Sea.Init; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class MapForSeasInitComponent : Component +{ + [DataField("mapX")] + public int MapX { get; set; } + [DataField("mapY")] + public int MapY { get; set; } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs index a0721dfbaca..37ab0b4bef7 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs @@ -1,5 +1,6 @@ using Content.Server.MagicBarrier.Components; using Content.Shared.Imperial.Medieval.Ships.Sea; +using Content.Shared.Interaction; using Robust.Server.GameObjects; using Robust.Shared.Map; @@ -19,6 +20,9 @@ public sealed class SeaMatrixInitSystem : EntitySystem public override void Initialize() { SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnInitMap); + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnInteractUsing); } private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentInit args) @@ -60,6 +64,34 @@ private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentIni } + private void OnInitMap(EntityUid uid, MapForSeasInitComponent component, ComponentInit args) + { + if (component.MapX == 0 && component.MapY == 0) + return; + SetMapPos(uid, component.MapX, component.MapY); + } + + + private void OnActivate(EntityUid uid, MapForSeasInitComponent component, ActivateInWorldEvent args) + { + SetMapPos(args.Target, component.MapX, component.MapY); + } + + private void OnInteractUsing(EntityUid uid, MapForSeasInitComponent component, InteractUsingEvent args) + { + SetMapPos(args.Target, component.MapX, component.MapY); + } + + public void SetMapPos(EntityUid uid, int x, int y) + { + var mapId = _transform.GetMapId(uid); + foreach (var magicBarrier in EntityManager.EntityQuery()) + { + if (magicBarrier.SeaMatrix is null) + continue; + magicBarrier.SeaMatrix.SetSeaId(x,y,mapId); + } + } /// /// ищем мапу либо -1 пишем /// diff --git a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs index d71ee711acf..e441841746c 100644 --- a/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/ShipTeleporting/ShipTeleportSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Database; using Content.Shared.Imperial.Medieval.Administration.Ships; using Content.Shared.Imperial.Medieval.Ships.Sea; +using Content.Shared.Imperial.Medieval.Ships.ShipDrowning; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Map.Components; @@ -66,6 +67,9 @@ public override void Update(float frameTime) private void TeleportShip(EntityUid ship, MapCoordinates coords) { + var mapScale = _cfg.GetCVar(ShipsCCVars.MapScale); + var tpRange = _cfg.GetCVar(ShipsCCVars.TeleportRange); + var tpDist = mapScale + tpRange; var newcoords = coords.Position; foreach (var magicBarrier in EntityManager.EntityQuery()) { @@ -78,34 +82,46 @@ private void TeleportShip(EntityUid ship, MapCoordinates coords) var (x, y) = seamap.Value; - if (Math.Abs(newcoords.X) > 250) + if (Math.Abs(newcoords.X) > tpDist) { if (newcoords.X > 0) + { x += 1; + newcoords.X = -tpDist; + } else { x -= 1; + newcoords.X = tpDist; } - newcoords.X *= -1; } - if (Math.Abs(newcoords.Y) > 250) + if (Math.Abs(newcoords.Y) > tpDist) { if (newcoords.Y > 0) + { y += 1; + newcoords.Y = -tpDist; + } + else { y -= 1; + newcoords.Y = tpDist; } - newcoords.Y *= -1; } var mapId = seematrix.GetCell(x,y).SeaId; + if (mapId == new MapId(-1)) + { + EnsureComp(ship, out var comp); + comp.DrownLevel += (int)Math.Abs(coords.Position.X) + (int)Math.Abs(coords.Position.Y); + } var nmapcoords = new MapCoordinates(newcoords, mapId); _transform.SetMapCoordinates(ship, nmapcoords); - + break; } diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index 68016f2c6c6..b9f7c78fbe2 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -8,48 +8,86 @@ namespace Content.Shared.Imperial.Medieval.Administration.Ships; [CVarDefs] public sealed class ShipsCCVars : CVars { - // максимальная скорость + /// + /// максимальная скорость + /// public static readonly CVarDef ShipsMaxSpeed = CVarDef.Create("ships.maxspeed", 20f, CVar.REPLICATED | CVar.SERVER); - // как часто меняется ветер + /// + /// как часто меняется ветер + /// public static readonly CVarDef WindChangeTime = CVarDef.Create("ships.windchangetime", 1f, CVar.REPLICATED | CVar.SERVER); - // как часто ветер дует + /// + /// как часто ветер дует + /// public static readonly CVarDef WindDelay = CVarDef.Create("ships.winddelay", 1f, CVar.REPLICATED | CVar.SERVER); - // как часто появляются волны + /// + /// как часто появляются волны + /// public static readonly CVarDef WaveDelay = CVarDef.Create("ships.wavedelay", 1f, CVar.REPLICATED | CVar.SERVER); - // Минимальный уровень для поломки лодки (Шторма если кто не понял) + /// + /// Минимальный уровень для поломки лодки (Шторма если кто не понял) + /// public static readonly CVarDef WindEnabled = CVarDef.Create("ships.waveenabled", true, CVar.REPLICATED | CVar.SERVER); - // сила с которой ветер толкает + /// + /// сила с которой ветер толкает + /// public static readonly CVarDef WindPower = CVarDef.Create("ships.windpower", 1f, CVar.REPLICATED | CVar.SERVER); - // угол поворота ветра + /// + /// угол поворота ветра + /// public static readonly CVarDef WindRotation = CVarDef.Create("ships.windrotation", 0f, CVar.REPLICATED | CVar.SERVER); - // уровень шторма + /// + /// уровень шторма + /// public static readonly CVarDef StormLevel = CVarDef.Create("ships.stormlevel", 1f, CVar.REPLICATED | CVar.SERVER); - // скорость с которой волна спавнится + /// + /// скорость с которой волна спавнится + /// public static readonly CVarDef WaveForce = CVarDef.Create("ships.waveforce", 1f, CVar.REPLICATED | CVar.SERVER); - // радиус спавна волн + /// + /// радиус спавна волн + /// public static readonly CVarDef WaveSpawnRange = CVarDef.Create("ships.wavespawnrange", 40f, CVar.REPLICATED | CVar.SERVER); - // угол разброса волн + /// + /// угол разброса волн + /// public static readonly CVarDef WaveSpawnAngle = CVarDef.Create("ships.wavespawnangle", 10f, CVar.REPLICATED | CVar.SERVER); - // в каком радиусе ломает волна + /// + /// в каком радиусе ломает волна + /// public static readonly CVarDef WaveRadiusTiles = CVarDef.Create("ships.waveradiustiles", 3f, CVar.REPLICATED | CVar.SERVER); - // какое максимальное количество тайлов может сломать волна + /// + /// какое максимальное количество тайлов может сломать волна + /// public static readonly CVarDef WaveMaxBreakCount = CVarDef.Create("ships.wavemaxbreakcount", 3, CVar.REPLICATED | CVar.SERVER); - // Минимальный уровень для поломки лодки (Шторма если кто не понял) + /// + /// Минимальный уровень для поломки лодки (Шторма если кто не понял) + /// public static readonly CVarDef WaveMinToBreakLevel = CVarDef.Create("ships.wavemintobreaklevel", 2, CVar.REPLICATED | CVar.SERVER); + /// + /// размер карты + /// + public static readonly CVarDef MapScale = + CVarDef.Create("ships.mapscale", 200, CVar.REPLICATED | CVar.SERVER); + /// + /// радиус с которого телепортирует корабль + /// + public static readonly CVarDef TeleportRange = + CVarDef.Create("ships.teleportrange", 50, CVar.REPLICATED | CVar.SERVER); } From a8e8259269f981d1da5beaf8ebb240a5814a2f04 Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Wed, 18 Mar 2026 22:06:25 +0300 Subject: [PATCH 19/20] =?UTF-8?q?=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D0=BE=D1=81=D1=82=D1=80=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ships/Sea/Generation/IslandPrototype.cs | 33 ++++ .../Sea/Generation/SeasGenerationSystem.cs | 182 +++++++++++++++++- .../Ships/Sea/Init/SeaMatrixInitSystem.cs | 42 +--- .../Administration/Ships/ShipsCCVars.cs | 2 +- .../Medieval/Seas/Islands/Islands.yml | 20 ++ 5 files changed, 232 insertions(+), 47 deletions(-) create mode 100644 Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs create mode 100644 Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs new file mode 100644 index 00000000000..131219feb30 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server.Imperial.Medieval.Ships.Sea.Generation; + +/// +/// Прототип острова — содержит конфигурацию для генерации. +/// Используется для определения типа, размера и других параметров. +/// +[Prototype("island")] +public sealed partial class IslandPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; + + /// + /// Размер острова в тайлах (например, 1 = 1x1, 2 = 2x2, 3 = 3x3). + /// + [DataField("size", required: true)] + public int Size { get; private set; } + + /// + /// Множитель веса для случайной генерации (если нужно балансировать частоту). + /// + [DataField("spawnWeight")] + public int SpawnWeight { get; private set; } = 1; + + /// + /// Опциональное имя для логирования или интерфейса (не используется в спавне). + /// + [DataField("name", required: false)] + public string? Name { get; private set; } +} diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs index 7fa7201d13c..ed44b180c5f 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs @@ -1,13 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Content.Server.Imperial.Medieval.Ships.Sea.Init; +using Content.Server.MagicBarrier.Components; +using Content.Shared.Imperial.Medieval.Ships.Sea; +using Robust.Server.GameObjects; +using Robust.Shared.EntitySerialization.Systems; +using Robust.Shared.Map; +using Robust.Shared.Random; +using Robust.Shared.Prototypes; + namespace Content.Server.Imperial.Medieval.Ships.Sea.Generation; -/// -/// This handles... -/// public sealed class SeasGenerationSystem : EntitySystem { - /// + [Dependency] private readonly MapLoaderSystem _mapLoader = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly SeaMatrixInitSystem _seaMatrix = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; // 🔥 НОВЫЙ ДЕПЕНДЕНС + + private const int MapMin = -75; + private const int MapMax = 75; + + // Используем прототипы вместо строк + private static readonly (string PrototypeId, int Count)[] IslandConfig = { + ("PirateIsland", 1), // 1 большой + ("FrendlyIslands", 2), // 2 средних + ("VolcanicIsland", 10) // 10 мелких + }; + public override void Initialize() { - + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnSeasGeneration); + } + + private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentInit args) + { + if (component.SeaMatrix == null) + component.SeaMatrix = new SeaMatrix(new List<(int x, int y)> + { + (2, 2), (2, 3), (2, 4), + (3, 2), (3, 3), (3, 4), + (4, 2), (4, 3), (4, 4), + }); + + if (component.SeaInitalazed) return; + + var seaMatrix = component.SeaMatrix; + + // Создаем 25 карт моря (5x5) + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + if (!seaMatrix.NeedsGeneration(x, y)) continue; + + var mapUid = _map.CreateMap(); + _metaData.SetEntityName(mapUid, $"Море {x} {y}"); + var mapId = _transform.GetMapId(mapUid); + AddComp(mapUid); + seaMatrix.SetSeaId(x, y, mapId); + seaMatrix.SetGenerated(x, y, true); + } + } + + // ✅ ГЕНЕРИРУЕМ ОСТРОВА С ИСПОЛЬЗОВАНИЕМ IPrototypeManager + GenerateIslandsOnSeaMaps(seaMatrix); + + component.SeaInitalazed = true; + } + + /// + /// Генерирует острова, используя IPrototypeManager и конфигурацию по ID. + /// Все острова размещаются в общем пространстве [-75, 75], без пересечений. + /// + private void GenerateIslandsOnSeaMaps(SeaMatrix seaMatrix) + { + var generatedObjects = new List(); + var occupiedTiles = new HashSet<(int X, int Y)>(); + + // Собираем все MapId карт моря + var seaMapIds = new List(); + for (int x = 0; x < 5; x++) + { + for (int y = 0; y < 5; y++) + { + var cell = seaMatrix.GetCell(x, y); + if (cell.NeedGenerate && !(cell.SeaId == new MapId(-1))) + seaMapIds.Add(cell.SeaId); + } + } + + if (seaMapIds.Count == 0) + { + Logger.Warning("No sea maps found to generate islands on!"); + return; + } + + // Проходим по конфигурации островов + foreach (var (prototypeId, count) in IslandConfig) + { + // Проверяем, существует ли прототип + if (!_prototypeManager.TryIndex(prototypeId, out var prototype)) + { + Logger.Warning($"Island prototype '{prototypeId}' not found! Skipping."); + continue; + } + + for (int i = 0; i < count; i++) + { + int attempts = 0; + const int maxAttempts = 100; + EntityUid? newObj = null; + + while (++attempts <= maxAttempts) + { + // Выбираем случайную карту моря + var targetMapId = seaMapIds[_random.Next(seaMapIds.Count)]; + + // Случайная позиция на карте + int x = _random.Next(MapMin, MapMax - prototype.Size + 1); + int y = _random.Next(MapMin, MapMax - prototype.Size + 1); + + // Проверяем пересечения + bool overlaps = false; + var newTiles = new List<(int X, int Y)>(); + + for (int dx = 0; dx < prototype.Size; dx++) + { + for (int dy = 0; dy < prototype.Size; dy++) + { + var tile = (x + dx, y + dy); + if (occupiedTiles.Contains(tile)) + { + overlaps = true; + break; + } + newTiles.Add(tile); + } + if (overlaps) + break; + } + + if (!overlaps) + { + // ✅ СПАВН С ПРОТОТИПОМ, а не строкой! + newObj = EntityManager.SpawnEntity(prototypeId, new MapCoordinates(x, y, targetMapId)); + // _mapLoader.TryLoadGrid(targetMapId, ); + if (newObj.HasValue) + { + generatedObjects.Add(newObj.Value); + foreach (var tile in newTiles) + occupiedTiles.Add(tile); + break; // Успешно + } + } + } + + if (attempts > maxAttempts) + { + Logger.Warning($"Failed to generate {prototypeId} after {maxAttempts} attempts."); + } + } + } + + Logger.Info($"Successfully generated {generatedObjects.Count} islands across {seaMapIds.Count} sea maps."); + } + + public sealed class SeasGenerationEvent + { + public MapId MapId { get; set; } + public int Count { get; set; } + public string Prototype { get; set; } = "Reef"; + } + + private void OnSeasGeneration(SeasGenerationEvent ev) + { + // Оставлено для будущего расширения } } diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs index 37ab0b4bef7..ea6a20cc3c4 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Init/SeaMatrixInitSystem.cs @@ -15,55 +15,15 @@ public sealed class SeaMatrixInitSystem : EntitySystem [Dependency] private readonly MapSystem _map = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityManager _entManager = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; + /// public override void Initialize() { - SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnInitMap); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(OnInteractUsing); } - private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentInit args) - { - if (component.SeaMatrix == null) - component.SeaMatrix = new SeaMatrix(new List<(int x, int y)> - { - (2, 2), (2, 3), (2, 4), - (3, 2), (3, 3), (3, 4), - (4, 2), (4, 3), (4, 4), - }); - - if (component.SeaInitalazed) - return; - - var seamat = component.SeaMatrix; - - for (int x = 0; x < 5; x++) - { - for (int y = 0; y < 5; y++) - { - if (!seamat.NeedsGeneration(x, y)) - { - if (seamat.GetCell(x, y).SeaId == new MapId(1)) - { - seamat.SetSeaId(x, y, TryFoundMap(x,y)); - } - } - var mapUid = _map.CreateMap(); - _metaData.SetEntityName(mapUid, $"Море {x} {y}"); - var mapId = _transform.GetMapId(mapUid); - AddComp(mapUid); - seamat.SetSeaId(x,y,mapId); - seamat.SetGenerated(x,y,true); - - } - } - - - } - private void OnInitMap(EntityUid uid, MapForSeasInitComponent component, ComponentInit args) { if (component.MapX == 0 && component.MapY == 0) diff --git a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs index b9f7c78fbe2..4c9030c5ade 100644 --- a/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs +++ b/Content.Shared/Imperial/Medieval/Administration/Ships/ShipsCCVars.cs @@ -82,7 +82,7 @@ public sealed class ShipsCCVars : CVars /// размер карты /// public static readonly CVarDef MapScale = - CVarDef.Create("ships.mapscale", 200, CVar.REPLICATED | CVar.SERVER); + CVarDef.Create("ships.mapscale", 100, CVar.REPLICATED | CVar.SERVER); /// /// радиус с которого телепортирует корабль /// diff --git a/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml b/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml new file mode 100644 index 00000000000..c8483534e0c --- /dev/null +++ b/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml @@ -0,0 +1,20 @@ +- type: island + id: PirateIslands + size: 20 + spawnWeight: 20 + name: "Пиратский остров" + +- type: island + id: FrendlyIslands + name: "Дружелюбный остров" + size: 5 + spawnWeight: 5 + +- type: island + id: VolcanicIsland + name: "Вулканический остров" + size: 3 + spawnWeight: 8 + + + From 9f0b6907db26339cee9f53a7f2128f8494fd5efc Mon Sep 17 00:00:00 2001 From: Magic0stick <77047717+Magic0stick@users.noreply.github.com> Date: Thu, 19 Mar 2026 20:56:28 +0300 Subject: [PATCH 20/20] =?UTF-8?q?=D0=A3=D1=80=D0=B0=D0=B0=20=D0=B3=D0=BE?= =?UTF-8?q?=D1=82=D0=BE=D0=B2=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Ships/Sea/Generation/IslandPrototype.cs | 7 +++++++ .../Ships/Sea/Generation/SeasGenerationSystem.cs | 13 +++++++------ .../Imperial/Medieval/Seas/Islands/Islands.yml | 3 +++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs index 131219feb30..014f9779fbe 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/IslandPrototype.cs @@ -30,4 +30,11 @@ public sealed partial class IslandPrototype : IPrototype /// [DataField("name", required: false)] public string? Name { get; private set; } + + /// + /// Опциональное имя для логирования или интерфейса (не используется в спавне). + /// + [DataField("path")] + public string? Path { get; private set; } + } diff --git a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs index ed44b180c5f..8477e4f0213 100644 --- a/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs +++ b/Content.Server/Imperial/Medieval/Ships/Sea/Generation/SeasGenerationSystem.cs @@ -1,14 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using Content.Server.Imperial.Medieval.Ships.Sea.Init; using Content.Server.MagicBarrier.Components; using Content.Shared.Imperial.Medieval.Ships.Sea; using Robust.Server.GameObjects; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Random; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Server.Imperial.Medieval.Ships.Sea.Generation; @@ -57,7 +60,8 @@ private void OnInit(EntityUid uid, MagicBarrierComponent component, ComponentIni { for (int y = 0; y < 5; y++) { - if (!seaMatrix.NeedsGeneration(x, y)) continue; + if (!seaMatrix.NeedsGeneration(x, y)) + continue; var mapUid = _map.CreateMap(); _metaData.SetEntityName(mapUid, $"Море {x} {y}"); @@ -105,7 +109,7 @@ private void GenerateIslandsOnSeaMaps(SeaMatrix seaMatrix) foreach (var (prototypeId, count) in IslandConfig) { // Проверяем, существует ли прототип - if (!_prototypeManager.TryIndex(prototypeId, out var prototype)) + if (!_prototypeManager.TryIndex(prototypeId, out var prototype) || prototype.Path == null) { Logger.Warning($"Island prototype '{prototypeId}' not found! Skipping."); continue; @@ -115,7 +119,6 @@ private void GenerateIslandsOnSeaMaps(SeaMatrix seaMatrix) { int attempts = 0; const int maxAttempts = 100; - EntityUid? newObj = null; while (++attempts <= maxAttempts) { @@ -148,9 +151,7 @@ private void GenerateIslandsOnSeaMaps(SeaMatrix seaMatrix) if (!overlaps) { - // ✅ СПАВН С ПРОТОТИПОМ, а не строкой! - newObj = EntityManager.SpawnEntity(prototypeId, new MapCoordinates(x, y, targetMapId)); - // _mapLoader.TryLoadGrid(targetMapId, ); + _mapLoader.TryLoadGrid(targetMapId, new ResPath(prototype.Path), out var newObj, offset: new Vector2(x,y)); if (newObj.HasValue) { generatedObjects.Add(newObj.Value); diff --git a/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml b/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml index c8483534e0c..cf44d1adff3 100644 --- a/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml +++ b/Resources/Prototypes/Imperial/Medieval/Seas/Islands/Islands.yml @@ -3,18 +3,21 @@ size: 20 spawnWeight: 20 name: "Пиратский остров" + path: /Ships/Victoria.yml - type: island id: FrendlyIslands name: "Дружелюбный остров" size: 5 spawnWeight: 5 + path: /Ships/Victoria.yml - type: island id: VolcanicIsland name: "Вулканический остров" size: 3 spawnWeight: 8 + path: /Ships/Victoria.yml