mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 05:27:38 +03:00
Gladiabot (#1548)
Co-authored-by: stellar-novas <stellar_novas@riseup.net> Co-authored-by: RedFoxIV <38788538+RedFoxIV@users.noreply.github.com> (cherry picked from commit 01d1ec4d4b04f6d76f239ec0da0970dc705736ef)
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.NPC.Events;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Reflection;
|
||||
|
||||
namespace Content.Client.NPC.Systems;
|
||||
public sealed partial class NpcFactionSpriteStateSetterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NpcFactionMemberComponent, NpcFactionAddedEvent>(OnFactionAdded);
|
||||
}
|
||||
|
||||
private void OnFactionAdded(Entity<NpcFactionMemberComponent > entity, ref NpcFactionAddedEvent args)
|
||||
{
|
||||
if (!_entityManager.HasComponent(entity, typeof(NpcFactionSpriteStateSetterComponent)))
|
||||
return;
|
||||
|
||||
SpriteComponent spriteComponent = _entityManager.GetComponent<SpriteComponent>(entity);
|
||||
spriteComponent.LayerSetState(0, new Robust.Client.Graphics.RSI.StateId(args.FactionID));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.NPC.Components;
|
||||
using Content.Server.NPC.HTN;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.NPC;
|
||||
using Robust.Shared.Map;
|
||||
@@ -10,6 +11,7 @@ namespace Content.Server.NPC.Systems;
|
||||
|
||||
public sealed partial class NPCCombatSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _rng = default!;
|
||||
private const float TargetMeleeLostRange = 14f;
|
||||
|
||||
private void InitializeMelee()
|
||||
@@ -114,5 +116,8 @@ public sealed partial class NPCCombatSystem
|
||||
{
|
||||
_melee.AttemptLightAttack(uid, weaponUid, weapon, component.Target);
|
||||
}
|
||||
|
||||
if (Comp<HTNComponent>(uid).Blackboard.TryGetValue<float>("AttackDelayDeviation", out var dev, EntityManager))
|
||||
weapon.NextAttack += TimeSpan.FromSeconds(_rng.NextFloat(-dev, dev));
|
||||
}
|
||||
}
|
||||
|
||||
11
Content.Shared/NPC/Components/NpcFactionSelectorComponent.cs
Normal file
11
Content.Shared/NPC/Components/NpcFactionSelectorComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.NPC.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSelectorSystem))]
|
||||
public sealed partial class NpcFactionSelectorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public List<string> SelectableFactions = new();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.NPC.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class NpcFactionSpriteStateSetterComponent : Component {}
|
||||
|
||||
25
Content.Shared/NPC/Events/NpcFactionChangedEvent.cs
Normal file
25
Content.Shared/NPC/Events/NpcFactionChangedEvent.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.NPC.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised from client to server to notify a faction was added to an NPC.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class NpcFactionAddedEvent : EntityEventArgs
|
||||
{
|
||||
public string FactionID;
|
||||
|
||||
public NpcFactionAddedEvent(string factionId) => FactionID = factionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised from client to server to notify a faction was removed from an NPC.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class NpcFactionRemovedEvent : EntityEventArgs
|
||||
{
|
||||
public string FactionID;
|
||||
|
||||
public NpcFactionRemovedEvent(string factionId) => FactionID = factionId;
|
||||
}
|
||||
@@ -24,6 +24,9 @@ public sealed partial class NpcFactionPrototype : IPrototype
|
||||
/// </summary>
|
||||
public record struct FactionData
|
||||
{
|
||||
[ViewVariables]
|
||||
public bool IsHostileToSelf;
|
||||
|
||||
[ViewVariables]
|
||||
public HashSet<ProtoId<NpcFactionPrototype>> Friendly;
|
||||
|
||||
|
||||
60
Content.Shared/NPC/Systems/NpcFactionSelectorSystem.cs
Normal file
60
Content.Shared/NPC/Systems/NpcFactionSelectorSystem.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.NPC.Systems;
|
||||
public sealed partial class NpcFactionSelectorSystem : EntitySystem
|
||||
{
|
||||
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _factionSystem = default!;
|
||||
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NpcFactionSelectorComponent, GetVerbsEvent<Verb>>(OnGetVerb);
|
||||
}
|
||||
|
||||
private void OnGetVerb(Entity<NpcFactionSelectorComponent> entity, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
||||
NpcFactionSelectorComponent factionSelectorComponent = _entityManager.GetComponent<NpcFactionSelectorComponent>(entity);
|
||||
|
||||
if (factionSelectorComponent.SelectableFactions.Count < 2)
|
||||
return;
|
||||
|
||||
foreach (var type in factionSelectorComponent.SelectableFactions)
|
||||
{
|
||||
var proto = _prototype.Index<NpcFactionPrototype>(type);
|
||||
|
||||
var v = new Verb
|
||||
{
|
||||
Priority = 1,
|
||||
Category = VerbCategory.SelectFaction,
|
||||
Text = proto.ID,
|
||||
Impact = LogImpact.Medium,
|
||||
DoContactInteraction = true,
|
||||
Act = () =>
|
||||
{
|
||||
_popup.PopupPredicted(Loc.GetString("npcfaction-component-faction-set", ("faction", proto.ID)), entity.Owner, null);
|
||||
foreach (var type in factionSelectorComponent.SelectableFactions)
|
||||
{
|
||||
_factionSystem.RemoveFaction(entity.Owner, type);
|
||||
}
|
||||
|
||||
_factionSystem.AddFaction(entity.Owner, proto.ID);
|
||||
}
|
||||
};
|
||||
args.Verbs.Add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.NPC.Events;
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Collections.Frozen;
|
||||
@@ -106,6 +107,8 @@ public sealed partial class NpcFactionSystem : EntitySystem
|
||||
if (!ent.Comp.Factions.Add(faction))
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(ent.Owner, new NpcFactionAddedEvent(faction));
|
||||
|
||||
if (dirty)
|
||||
RefreshFactions((ent, ent.Comp));
|
||||
}
|
||||
@@ -125,6 +128,8 @@ public sealed partial class NpcFactionSystem : EntitySystem
|
||||
continue;
|
||||
}
|
||||
|
||||
RaiseLocalEvent(ent.Owner, new NpcFactionAddedEvent(faction));
|
||||
|
||||
ent.Comp.Factions.Add(faction);
|
||||
}
|
||||
|
||||
@@ -149,6 +154,8 @@ public sealed partial class NpcFactionSystem : EntitySystem
|
||||
if (!ent.Comp.Factions.Remove(faction))
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(ent.Owner, new NpcFactionRemovedEvent(faction));
|
||||
|
||||
if (dirty)
|
||||
RefreshFactions((ent, ent.Comp));
|
||||
}
|
||||
@@ -217,7 +224,12 @@ public sealed partial class NpcFactionSystem : EntitySystem
|
||||
if (!Resolve(ent, ref ent.Comp, false) || !Resolve(other, ref other.Comp, false))
|
||||
return false;
|
||||
|
||||
return ent.Comp.Factions.Overlaps(other.Comp.Factions) || ent.Comp.FriendlyFactions.Overlaps(other.Comp.Factions);
|
||||
var intersect = ent.Comp.Factions.Intersect(other.Comp.Factions); // factions which have both ent and other as members
|
||||
foreach (var faction in intersect)
|
||||
if (_factions[faction].IsHostileToSelf)
|
||||
return false;
|
||||
|
||||
return intersect.Count() > 0 || ent.Comp.FriendlyFactions.Overlaps(other.Comp.Factions);
|
||||
}
|
||||
|
||||
public bool IsFactionFriendly(string target, string with)
|
||||
@@ -301,8 +313,9 @@ public sealed partial class NpcFactionSystem : EntitySystem
|
||||
{
|
||||
_factions = _proto.EnumeratePrototypes<NpcFactionPrototype>().ToFrozenDictionary(
|
||||
faction => faction.ID,
|
||||
faction => new FactionData
|
||||
faction => new FactionData
|
||||
{
|
||||
IsHostileToSelf = faction.Hostile.Contains(faction.ID),
|
||||
Friendly = faction.Friendly.ToHashSet(),
|
||||
Hostile = faction.Hostile.ToHashSet()
|
||||
});
|
||||
|
||||
@@ -100,6 +100,8 @@ namespace Content.Shared.Verbs
|
||||
|
||||
public static readonly VerbCategory SelectType = new("verb-categories-select-type");
|
||||
|
||||
public static readonly VerbCategory SelectFaction = new("verb-categories-select-faction");
|
||||
|
||||
public static readonly VerbCategory PowerLevel = new("verb-categories-power-level");
|
||||
|
||||
public static readonly VerbCategory Interaction = new("verb-categories-interaction");
|
||||
|
||||
@@ -72,6 +72,7 @@ petting-success-syndicate-cyborg = You pet {THE($target)} on {POSS-ADJ($target)}
|
||||
petting-success-derelict-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} rusty metal head.
|
||||
petting-success-recycler = You pet {THE($target)} on {POSS-ADJ($target)} mildly threatening steel exterior.
|
||||
petting-success-station-ai = You pet {THE($target)} on {POSS-ADJ($target)} cold, square screen.
|
||||
petting-success-gladiabot = You pet {THE($target)} on {POSS-ADJ($target)} vicious cardboard head.
|
||||
|
||||
petting-failure-honkbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} honks in refusal!
|
||||
petting-failure-cleanbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy mopping!
|
||||
@@ -87,6 +88,7 @@ petting-failure-service-cyborg = You reach out to pet {THE($target)}, but {SUBJE
|
||||
petting-failure-syndicate-cyborg = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} treacherous affiliation makes you reconsider.
|
||||
petting-failure-derelict-cyborg = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} rusty and jagged exterior makes you reconsider.
|
||||
petting-failure-station-ai = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BASIC($target, "zap", "zaps")} your hand away.
|
||||
petting-failure-gladiabot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} only wants to fight!
|
||||
|
||||
petting-success-station-ai-others = { CAPITALIZE(THE($user)) } pets {THE($target)} on {POSS-ADJ($target)} cold, square screen.
|
||||
|
||||
|
||||
1
Resources/Locale/en-US/npc/factions.ftl
Normal file
1
Resources/Locale/en-US/npc/factions.ftl
Normal file
@@ -0,0 +1 @@
|
||||
npcfaction-component-faction-set = Faction set to: {$faction}
|
||||
@@ -26,6 +26,7 @@ verb-categories-set-sensor = Sensor
|
||||
verb-categories-timer = Set Delay
|
||||
verb-categories-lever = Lever
|
||||
verb-categories-select-type = Select Type
|
||||
verb-categories-select-faction = Select Faction
|
||||
verb-categories-fax = Set Destination
|
||||
verb-categories-power-level = Power Level
|
||||
verb-categories-interaction = Interact
|
||||
|
||||
90
Resources/Prototypes/Entities/Mobs/NPCs/gladiabot.yml
Normal file
90
Resources/Prototypes/Entities/Mobs/NPCs/gladiabot.yml
Normal file
@@ -0,0 +1,90 @@
|
||||
- type: entity
|
||||
parent: MobSiliconBase
|
||||
id: MobGladiaBot
|
||||
name: gladiabot
|
||||
description: For glory!
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Mobs/Silicon/Bots/gladiabot.rsi
|
||||
state: GladiabotFFA
|
||||
- type: Inventory
|
||||
templateId: gladiabot
|
||||
- type: InventorySlots
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
enum.StrippingUiKey.Key:
|
||||
type: StrippableBoundUserInterface
|
||||
- type: Construction
|
||||
graph: GladiaBot
|
||||
node: bot
|
||||
- type: Stripping
|
||||
- type: Strippable
|
||||
- type: SentienceTarget
|
||||
flavorKind: station-event-random-sentience-flavor-mechanical
|
||||
- type: UseDelay
|
||||
delay: 1
|
||||
- type: NpcFactionMember
|
||||
factions:
|
||||
- GladiabotFFA
|
||||
- type: NpcFactionSelector
|
||||
selectableFactions:
|
||||
- GladiabotFFA
|
||||
- GladiabotRed
|
||||
- GladiabotBlue
|
||||
- GladiabotGreen
|
||||
- GladiabotYellow
|
||||
- type: NpcFactionSpriteStateSetter
|
||||
- type: CombatMode
|
||||
- type: MeleeWeapon
|
||||
altDisarm: false
|
||||
soundHit:
|
||||
path: /Audio/Weapons/bladeslice.ogg
|
||||
angle: 0
|
||||
animation: WeaponArcPunch
|
||||
damage:
|
||||
types:
|
||||
Slash: 3
|
||||
- type: MobThresholds
|
||||
thresholds:
|
||||
0: Alive
|
||||
40: Dead
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 30
|
||||
behaviors:
|
||||
- !type:TriggerBehavior
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 40
|
||||
behaviors:
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
ProximitySensor:
|
||||
min: 1
|
||||
max: 1
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
MaterialCloth1:
|
||||
min: 1
|
||||
max: 1
|
||||
- !type:EmptyContainersBehaviour
|
||||
containers:
|
||||
- head
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: MovementSpeedModifier
|
||||
baseWalkSpeed: 2
|
||||
baseSprintSpeed: 3
|
||||
- type: HTN
|
||||
rootTask:
|
||||
task: GladiabotCompound
|
||||
blackboard:
|
||||
AttackDelayDeviation: !type:Single
|
||||
0.3
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: petting-success-gladiabot
|
||||
interactFailureString: petting-failure-gladiabot
|
||||
interactSuccessSound:
|
||||
path: /Audio/Ambience/Objects/periodic_beep.ogg
|
||||
@@ -0,0 +1,10 @@
|
||||
- type: inventoryTemplate
|
||||
id: gladiabot
|
||||
slots:
|
||||
- name: head
|
||||
slotTexture: head
|
||||
slotFlags: HEAD
|
||||
uiWindowPos: 0,1
|
||||
strippingWindowPos: 0,0
|
||||
displayName: Head
|
||||
offset: 0.1, -0.15
|
||||
12
Resources/Prototypes/NPCs/gladiabot.yml
Normal file
12
Resources/Prototypes/NPCs/gladiabot.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
- type: htnCompound
|
||||
id: GladiabotCompound
|
||||
branches:
|
||||
- tasks:
|
||||
- !type:HTNPrimitiveTask
|
||||
operator: !type:UtilityOperator
|
||||
proto: NearbyMeleeTargets
|
||||
- !type:HTNCompoundTask
|
||||
task: MeleeAttackTargetCompound
|
||||
- tasks:
|
||||
- !type:HTNCompoundTask
|
||||
task: IdleCompound
|
||||
@@ -0,0 +1,25 @@
|
||||
- type: constructionGraph
|
||||
id: GladiaBot
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: bot
|
||||
steps:
|
||||
- material: Cardboard
|
||||
amount: 1
|
||||
doAfter: 2
|
||||
- tag: ProximitySensor
|
||||
icon:
|
||||
sprite: Objects/Misc/proximity_sensor.rsi
|
||||
state: icon
|
||||
name: proximity sensor
|
||||
doAfter: 2
|
||||
- tag: Shiv
|
||||
icon:
|
||||
sprite: Objects/Weapons/Melee/shiv.rsi
|
||||
state: icon
|
||||
name: shiv
|
||||
doAfter: 2
|
||||
- node: bot
|
||||
entity: MobGladiaBot
|
||||
@@ -75,3 +75,16 @@
|
||||
icon:
|
||||
sprite: Mobs/Silicon/Bots/supplybot.rsi
|
||||
state: supplybot
|
||||
|
||||
- type: construction
|
||||
name: gladiabot
|
||||
id: gladiabot
|
||||
graph: GladiaBot
|
||||
startNode: start
|
||||
targetNode: bot
|
||||
category: construction-category-utilities
|
||||
objectType: Item
|
||||
description: This bot fights for honour and glory!
|
||||
icon:
|
||||
sprite: Mobs/Silicon/Bots/gladiabot.rsi
|
||||
state: GladiabotFFA
|
||||
|
||||
@@ -107,3 +107,44 @@
|
||||
|
||||
- type: npcFaction
|
||||
id: AnimalFriend
|
||||
|
||||
- type: npcFaction
|
||||
id: GladiabotFFA
|
||||
hostile:
|
||||
- GladiabotFFA
|
||||
- GladiabotRed
|
||||
- GladiabotGreen
|
||||
- GladiabotBlue
|
||||
- GladiabotYellow
|
||||
|
||||
- type: npcFaction
|
||||
id: GladiabotRed
|
||||
hostile:
|
||||
- GladiabotFFA
|
||||
- GladiabotGreen
|
||||
- GladiabotBlue
|
||||
- GladiabotYellow
|
||||
|
||||
- type: npcFaction
|
||||
id: GladiabotGreen
|
||||
hostile:
|
||||
- GladiabotFFA
|
||||
- GladiabotRed
|
||||
- GladiabotBlue
|
||||
- GladiabotYellow
|
||||
|
||||
- type: npcFaction
|
||||
id: GladiabotBlue
|
||||
hostile:
|
||||
- GladiabotFFA
|
||||
- GladiabotRed
|
||||
- GladiabotGreen
|
||||
- GladiabotYellow
|
||||
|
||||
- type: npcFaction
|
||||
id: GladiabotYellow
|
||||
hostile:
|
||||
- GladiabotFFA
|
||||
- GladiabotRed
|
||||
- GladiabotGreen
|
||||
- GladiabotBlue
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
56
Resources/Textures/Mobs/Silicon/Bots/gladiabot.rsi/meta.json
Normal file
56
Resources/Textures/Mobs/Silicon/Bots/gladiabot.rsi/meta.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Tim Falken",
|
||||
"states": [
|
||||
{
|
||||
"name": "GladiabotFFA",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GladiabotRed",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GladiabotGreen",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GladiabotBlue",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "GladiabotYellow",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user