Adds Spray Bottles (#1522)
* Spray Bottle * -"Proper" wall detection -Removed some using -Fixed sound * Just don't add it * -Removed spill sound -Added sound parameter to SpillHelper.SpillAt * Now spawns Vapor instead of Puddles instantly * -Review -Nullable * Reworkkkk * AABB shittery Co-authored-by: Víctor Aguilera Puerto <6766154+Zumorica@users.noreply.github.com>
This commit is contained in:
@@ -151,6 +151,8 @@
|
|||||||
"Flippable",
|
"Flippable",
|
||||||
"Airtight",
|
"Airtight",
|
||||||
"MovedByPressure",
|
"MovedByPressure",
|
||||||
|
"Spray",
|
||||||
|
"Vapor",
|
||||||
"DamageOnHighSpeedImpact",
|
"DamageOnHighSpeedImpact",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
using Content.Server.GameObjects.Components.Fluids;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.EntityFrameworkCore.Update.Internal;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Map;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Timers;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using Timer = Robust.Shared.Timers.Timer;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Chemistry
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
class VaporComponent : Component, ICollideBehavior
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
#pragma warning enable 649
|
||||||
|
public override string Name => "Vapor";
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
private SolutionComponent _contents;
|
||||||
|
[ViewVariables]
|
||||||
|
private ReagentUnit _transferAmount;
|
||||||
|
|
||||||
|
private bool _running;
|
||||||
|
private Vector2 _direction;
|
||||||
|
private float _velocity;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_contents = Owner.GetComponent<SolutionComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(Vector2 dir, float velocity)
|
||||||
|
{
|
||||||
|
_running = true;
|
||||||
|
_direction = dir;
|
||||||
|
_velocity = velocity;
|
||||||
|
// Set Move
|
||||||
|
if (Owner.TryGetComponent(out ICollidableComponent collidable))
|
||||||
|
{
|
||||||
|
var controller = collidable.EnsureController<VaporController>();
|
||||||
|
controller.Move(_direction, _velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (!_running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get all intersecting tiles with the vapor and spray the divided solution on there
|
||||||
|
if (Owner.TryGetComponent(out ICollidableComponent collidable))
|
||||||
|
{
|
||||||
|
var worldBounds = collidable.WorldAABB;
|
||||||
|
var mapGrid = _mapManager.GetGrid(Owner.Transform.GridID);
|
||||||
|
|
||||||
|
var tiles = mapGrid.GetTilesIntersecting(worldBounds);
|
||||||
|
var amount = _transferAmount / ReagentUnit.New(tiles.Count());
|
||||||
|
foreach (var tile in tiles)
|
||||||
|
{
|
||||||
|
var pos = tile.GridIndices.ToGridCoordinates(_mapManager, tile.GridIndex);
|
||||||
|
SpillHelper.SpillAt(pos, _contents.SplitSolution(amount), "PuddleSmear", false); //make non PuddleSmear?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_contents.CurrentVolume == 0)
|
||||||
|
{
|
||||||
|
// Delete this
|
||||||
|
Owner.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool TryAddSolution(Solution solution)
|
||||||
|
{
|
||||||
|
if (solution.TotalVolume == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var result = _contents.TryAddSolution(solution);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICollideBehavior.CollideWith(IEntity collidedWith)
|
||||||
|
{
|
||||||
|
// Check for collision with a impassable object (e.g. wall) and stop
|
||||||
|
if (collidedWith.TryGetComponent(out ICollidableComponent collidable))
|
||||||
|
{
|
||||||
|
if ((collidable.CollisionLayer & (int) CollisionGroup.Impassable) != 0 && collidable.Hard)
|
||||||
|
{
|
||||||
|
if (Owner.TryGetComponent(out ICollidableComponent coll))
|
||||||
|
{
|
||||||
|
var controller = coll.EnsureController<VaporController>();
|
||||||
|
controller.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,11 @@ namespace Content.Server.GameObjects.Components.Fluids
|
|||||||
/// <param name="entity">Entity location to spill at</param>
|
/// <param name="entity">Entity location to spill at</param>
|
||||||
/// <param name="solution">Initial solution for the prototype</param>
|
/// <param name="solution">Initial solution for the prototype</param>
|
||||||
/// <param name="prototype">Prototype to use</param>
|
/// <param name="prototype">Prototype to use</param>
|
||||||
internal static void SpillAt(IEntity entity, Solution solution, string prototype)
|
/// <param name="sound">Play the spill sound</param>
|
||||||
|
internal static void SpillAt(IEntity entity, Solution solution, string prototype, bool sound = true)
|
||||||
{
|
{
|
||||||
var entityLocation = entity.Transform.GridPosition;
|
var entityLocation = entity.Transform.GridPosition;
|
||||||
SpillAt(entityLocation, solution, prototype);
|
SpillAt(entityLocation, solution, prototype, sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other functions will be calling this one
|
// Other functions will be calling this one
|
||||||
@@ -32,7 +33,8 @@ namespace Content.Server.GameObjects.Components.Fluids
|
|||||||
/// <param name="gridCoordinates"></param>
|
/// <param name="gridCoordinates"></param>
|
||||||
/// <param name="solution">Initial solution for the prototype</param>
|
/// <param name="solution">Initial solution for the prototype</param>
|
||||||
/// <param name="prototype">Prototype to use</param>
|
/// <param name="prototype">Prototype to use</param>
|
||||||
internal static PuddleComponent? SpillAt(GridCoordinates gridCoordinates, Solution solution, string prototype)
|
/// <param name="sound">Play the spill sound</param>
|
||||||
|
internal static PuddleComponent? SpillAt(GridCoordinates gridCoordinates, Solution solution, string prototype, bool sound = true)
|
||||||
{
|
{
|
||||||
if (solution.TotalVolume == 0)
|
if (solution.TotalVolume == 0)
|
||||||
{
|
{
|
||||||
@@ -67,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!puddleComponent.TryAddSolution(solution))
|
if (!puddleComponent.TryAddSolution(solution, sound))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -84,7 +86,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
|||||||
|
|
||||||
var puddle = serverEntityManager.SpawnEntity(prototype, spillGridCoords);
|
var puddle = serverEntityManager.SpawnEntity(prototype, spillGridCoords);
|
||||||
var newPuddleComponent = puddle.GetComponent<PuddleComponent>();
|
var newPuddleComponent = puddle.GetComponent<PuddleComponent>();
|
||||||
newPuddleComponent.TryAddSolution(solution);
|
newPuddleComponent.TryAddSolution(solution, sound);
|
||||||
return newPuddleComponent;
|
return newPuddleComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
101
Content.Server/GameObjects/Components/Fluids/SprayComponent.cs
Normal file
101
Content.Server/GameObjects/Components/Fluids/SprayComponent.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.GameObjects.Components.Chemistry;
|
||||||
|
using Content.Server.Interfaces;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Microsoft.EntityFrameworkCore.Update.Internal;
|
||||||
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Map;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Fluids
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
class SprayComponent : Component, IAfterInteract
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||||
|
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||||
|
#pragma warning restore 649
|
||||||
|
public override string Name => "Spray";
|
||||||
|
|
||||||
|
private ReagentUnit _transferAmount;
|
||||||
|
private string _spraySound;
|
||||||
|
private float _sprayVelocity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of solution to be sprayer from this solution when using it
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public ReagentUnit TransferAmount
|
||||||
|
{
|
||||||
|
get => _transferAmount;
|
||||||
|
set => _transferAmount = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The speed at which the vapor starts when sprayed
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public float Velocity
|
||||||
|
{
|
||||||
|
get => _sprayVelocity;
|
||||||
|
set => _sprayVelocity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SolutionComponent _contents;
|
||||||
|
public ReagentUnit CurrentVolume => _contents.CurrentVolume;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_contents = Owner.GetComponent<SolutionComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
|
{
|
||||||
|
base.ExposeData(serializer);
|
||||||
|
serializer.DataField(ref _transferAmount, "transferAmount", ReagentUnit.New(10));
|
||||||
|
serializer.DataField(ref _sprayVelocity, "sprayVelocity", 5.0f);
|
||||||
|
serializer.DataField(ref _spraySound, "spraySound", string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if (CurrentVolume <= 0)
|
||||||
|
{
|
||||||
|
_notifyManager.PopupMessage(Owner, eventArgs.User, Loc.GetString("It's empty!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var playerPos = eventArgs.User.Transform.GridPosition;
|
||||||
|
if (eventArgs.ClickLocation.GridID != playerPos.GridID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized;
|
||||||
|
var solution = _contents.SplitSolution(_transferAmount);
|
||||||
|
|
||||||
|
playerPos = playerPos.Offset(direction); // Move a bit so we don't hit the player
|
||||||
|
//TODO: check for wall?
|
||||||
|
var vapor = _serverEntityManager.SpawnEntity("Vapor", playerPos);
|
||||||
|
// Add the solution to the vapor and actually send the thing
|
||||||
|
var vaporComponent = vapor.GetComponent<VaporComponent>();
|
||||||
|
vaporComponent.TryAddSolution(solution);
|
||||||
|
vaporComponent.Start(direction, _sprayVelocity); //TODO: maybe make the velocity depending on the distance to the click
|
||||||
|
|
||||||
|
//Play sound
|
||||||
|
EntitySystem.Get<AudioSystem>().PlayFromEntity(_spraySound, Owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Content.Server/GameObjects/EntitySystems/VaporSystem.cs
Normal file
18
Content.Server/GameObjects/EntitySystems/VaporSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Content.Server.GameObjects.Components.Chemistry;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
public class VaporSystem : EntitySystem
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
foreach (var vaporComp in ComponentManager.EntityQuery<VaporComponent>())
|
||||||
|
{
|
||||||
|
vaporComp.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Content.Shared/Physics/VaporController.cs
Normal file
16
Content.Shared/Physics/VaporController.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Content.Shared.Physics
|
||||||
|
{
|
||||||
|
public class VaporController : VirtualController
|
||||||
|
{
|
||||||
|
public void Move(Vector2 velocityDirection, float speed)
|
||||||
|
{
|
||||||
|
LinearVelocity = velocityDirection * speed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Resources/Audio/Effects/spray.ogg
Normal file
BIN
Resources/Audio/Effects/spray.ogg
Normal file
Binary file not shown.
@@ -174,3 +174,24 @@
|
|||||||
state: soapomega
|
state: soapomega
|
||||||
- type: Slippery
|
- type: Slippery
|
||||||
paralyzeTime: 7
|
paralyzeTime: 7
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: spray bottle
|
||||||
|
id: SprayBottle
|
||||||
|
parent: BaseItem
|
||||||
|
description: A spray bottle with an unscrewable top.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
texture: Objects/Specific/Janitorial/cleaner.png
|
||||||
|
- type: Icon
|
||||||
|
texture: Objects/Specific/Janitorial/cleaner.png
|
||||||
|
- type: Solution
|
||||||
|
maxVol: 100
|
||||||
|
caps: 1
|
||||||
|
- type: Pourable
|
||||||
|
transferAmount: 5.0
|
||||||
|
- type: CanSpill
|
||||||
|
- type: Spray
|
||||||
|
transferAmount: 10
|
||||||
|
sprayVelocity: 5
|
||||||
|
spraySound: /Audio/Effects/spray.ogg
|
||||||
|
|||||||
@@ -6,3 +6,21 @@
|
|||||||
components:
|
components:
|
||||||
- type: Solution
|
- type: Solution
|
||||||
maxVol: 5
|
maxVol: 5
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: Vapor
|
||||||
|
name: "vapor"
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: SnapGrid
|
||||||
|
offset: Center
|
||||||
|
- type: Solution
|
||||||
|
maxVol: 50
|
||||||
|
- type: Vapor
|
||||||
|
- type: Physics
|
||||||
|
- type: Collidable
|
||||||
|
shapes:
|
||||||
|
- !type:PhysShapeAabb
|
||||||
|
bounds: "-0.25,-0.25,0.25,0.25"
|
||||||
|
mask:
|
||||||
|
- Impassable
|
||||||
BIN
Resources/Textures/Objects/Specific/Janitorial/cleaner.png
Normal file
BIN
Resources/Textures/Objects/Specific/Janitorial/cleaner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 411 B |
Reference in New Issue
Block a user