Felinids (#18)

* Init

Pretty much copy pasted everything that was easily nyano stuff

* Fix build atleast

* PseudoItem

* Fixes actions

Should work, maybe, perhaps

* Remove unecessary comment

* Fix no ears/tails

* excess -> critThreshold

* Fix missing emotes

* Duplicate squeak emote

* Cleanup

* Update comments

---------

Signed-off-by: Debug <49997488+DebugOk@users.noreply.github.com>
Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com>
Co-authored-by: Debug <49997488+DebugOk@users.noreply.github.com>
This commit is contained in:
Fluffiest Floofers 2023-09-21 01:28:21 +02:00 committed by GitHub
parent 303884d60f
commit 9e06de6bb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1348 additions and 0 deletions

View File

@ -0,0 +1,11 @@
namespace Content.Server.Abilities.Felinid;
[RegisterComponent]
public sealed partial class CoughingUpHairballComponent : Component
{
[DataField("accumulator")]
public float Accumulator = 0f;
[DataField("coughUpTime")]
public TimeSpan CoughUpTime = TimeSpan.FromSeconds(2.15); // length of hairball.ogg
}

View File

@ -0,0 +1,26 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Prototypes;
using Content.Shared.Actions;
using Robust.Shared.Utility;
namespace Content.Server.Abilities.Felinid;
[RegisterComponent]
public sealed partial class FelinidComponent : Component
{
/// <summary>
/// The hairball prototype to use.
/// </summary>
[DataField("hairballPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string HairballPrototype = "Hairball";
//[DataField("hairballAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
//public string HairballAction = "ActionHairball";
[DataField("hairballAction")]
public EntityUid? HairballAction = null;
public EntityUid? EatActionTarget = null;
public EntityUid? EatAction = null;
}

View File

@ -0,0 +1,5 @@
namespace Content.Server.Abilities.Felinid;
[RegisterComponent]
public sealed partial class FelinidFoodComponent : Component
{}

View File

@ -0,0 +1,191 @@
using Content.Shared.Actions;
using Content.Shared.Actions.Events;
using Content.Shared.Audio;
using Content.Shared.StatusEffect;
using Content.Shared.Throwing;
using Content.Shared.Item;
using Content.Shared.Inventory;
using Content.Shared.Hands;
using Content.Shared.IdentityManagement;
using Content.Shared.Nutrition.Components;
using Content.Shared.Nutrition.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Medical;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Nutrition.Components;
using Content.Server.Chemistry.EntitySystems;
using Content.Server.Popups;
using Robust.Shared.Audio;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
namespace Content.Server.Abilities.Felinid;
public sealed partial class FelinidSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly HungerSystem _hungerSystem = default!;
[Dependency] private readonly VomitSystem _vomitSystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FelinidComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<FelinidComponent, HairballActionEvent>(OnHairball);
SubscribeLocalEvent<FelinidComponent, EatMouseActionEvent>(OnEatMouse);
SubscribeLocalEvent<FelinidComponent, DidEquipHandEvent>(OnEquipped);
SubscribeLocalEvent<FelinidComponent, DidUnequipHandEvent>(OnUnequipped);
SubscribeLocalEvent<HairballComponent, ThrowDoHitEvent>(OnHairballHit);
SubscribeLocalEvent<HairballComponent, GettingPickedUpAttemptEvent>(OnHairballPickupAttempt);
}
private Queue<EntityUid> RemQueue = new();
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var cat in RemQueue)
{
RemComp<CoughingUpHairballComponent>(cat);
}
RemQueue.Clear();
foreach (var (hairballComp, catComp) in EntityQuery<CoughingUpHairballComponent, FelinidComponent>())
{
hairballComp.Accumulator += frameTime;
if (hairballComp.Accumulator < hairballComp.CoughUpTime.TotalSeconds)
continue;
hairballComp.Accumulator = 0;
SpawnHairball(hairballComp.Owner, catComp);
RemQueue.Enqueue(hairballComp.Owner);
}
}
private void OnInit(EntityUid uid, FelinidComponent component, ComponentInit args)
{
if (component.HairballAction != null)
return;
component.HairballAction = Spawn("ActionHairball");
_actionsSystem.AddAction(uid, component.HairballAction.Value, uid);
}
private void OnEquipped(EntityUid uid, FelinidComponent component, DidEquipHandEvent args)
{
if (!HasComp<FelinidFoodComponent>(args.Equipped))
return;
component.EatActionTarget = args.Equipped;
component.EatAction = Spawn("ActionEatMouse");
_actionsSystem.AddAction(uid, component.EatAction.Value, null);
}
private void OnUnequipped(EntityUid uid, FelinidComponent component, DidUnequipHandEvent args)
{
if (args.Unequipped == component.EatActionTarget)
{
component.EatActionTarget = null;
if (component.EatAction != null)
_actionsSystem.RemoveAction(uid, component.EatAction.Value);
}
}
private void OnHairball(EntityUid uid, FelinidComponent component, HairballActionEvent args)
{
if (_inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) &&
EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUid, out var blocker) &&
blocker.Enabled)
{
_popupSystem.PopupEntity(Loc.GetString("hairball-mask", ("mask", maskUid)), uid, uid);
return;
}
_popupSystem.PopupEntity(Loc.GetString("hairball-cough", ("name", Identity.Entity(uid, EntityManager))), uid);
SoundSystem.Play("/Audio/Nyanotrasen/Effects/Species/hairball.ogg", Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.15f));
EnsureComp<CoughingUpHairballComponent>(uid);
args.Handled = true;
}
private void OnEatMouse(EntityUid uid, FelinidComponent component, EatMouseActionEvent args)
{
if (component.EatActionTarget == null)
return;
if (!TryComp<HungerComponent>(uid, out var hunger))
return;
if (hunger.CurrentThreshold == Shared.Nutrition.Components.HungerThreshold.Overfed)
{
_popupSystem.PopupEntity(Loc.GetString("food-system-you-cannot-eat-any-more"), uid, uid, Shared.Popups.PopupType.SmallCaution);
return;
}
if (_inventorySystem.TryGetSlotEntity(uid, "mask", out var maskUid) &&
EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUid, out var blocker) &&
blocker.Enabled)
{
_popupSystem.PopupEntity(Loc.GetString("hairball-mask", ("mask", maskUid)), uid, uid, Shared.Popups.PopupType.SmallCaution);
return;
}
if (component.HairballAction != null)
{
_actionsSystem.SetCharges(component.HairballAction, 1); // You get the charge back and that's it. Tough.
_actionsSystem.SetEnabled(component.HairballAction, true);
}
Del(component.EatActionTarget.Value);
component.EatActionTarget = null;
SoundSystem.Play("/Audio/Items/eatfood.ogg", Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.15f));
_hungerSystem.ModifyHunger(uid, 50f, hunger);
if (component.EatAction != null)
_actionsSystem.RemoveAction(uid, component.EatAction.Value);
}
private void SpawnHairball(EntityUid uid, FelinidComponent component)
{
var hairball = EntityManager.SpawnEntity(component.HairballPrototype, Transform(uid).Coordinates);
var hairballComp = Comp<HairballComponent>(hairball);
if (TryComp<BloodstreamComponent>(uid, out var bloodstream))
{
var temp = bloodstream.ChemicalSolution.SplitSolution(20);
if (_solutionSystem.TryGetSolution(hairball, hairballComp.SolutionName, out var hairballSolution))
{
_solutionSystem.TryAddSolution(hairball, hairballSolution, temp);
}
}
}
private void OnHairballHit(EntityUid uid, HairballComponent component, ThrowDoHitEvent args)
{
if (HasComp<FelinidComponent>(args.Target) || !HasComp<StatusEffectsComponent>(args.Target))
return;
if (_robustRandom.Prob(0.2f))
_vomitSystem.Vomit(args.Target);
}
private void OnHairballPickupAttempt(EntityUid uid, HairballComponent component, GettingPickedUpAttemptEvent args)
{
if (HasComp<FelinidComponent>(args.User) || !HasComp<StatusEffectsComponent>(args.User))
return;
if (_robustRandom.Prob(0.2f))
{
_vomitSystem.Vomit(args.User);
args.Cancel();
}
}
}

View File

@ -0,0 +1,7 @@
namespace Content.Server.Abilities.Felinid;
[RegisterComponent]
public sealed partial class HairballComponent : Component
{
public string SolutionName = "hairball";
}

View File

@ -0,0 +1,15 @@
namespace Content.Server.Item.PseudoItem;
/// <summary>
/// For entities that behave like an item under certain conditions,
/// but not under most conditions.
/// </summary>
[RegisterComponent]
public sealed partial class PseudoItemComponent : Component
{
[DataField("size")]
public int Size = 120;
public bool Active = false;
}

View File

@ -0,0 +1,154 @@
using System.Threading;
using Content.Shared.Verbs;
using Content.Shared.Item;
using Content.Shared.Hands;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Item.PseudoItem;
using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems;
using Content.Server.DoAfter;
using Robust.Shared.Containers;
namespace Content.Server.Item.PseudoItem;
public sealed partial class PseudoItemSystem : EntitySystem
{
[Dependency] private readonly StorageSystem _storageSystem = default!;
[Dependency] private readonly ItemSystem _itemSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PseudoItemComponent, GetVerbsEvent<InnateVerb>>(AddInsertVerb);
SubscribeLocalEvent<PseudoItemComponent, GetVerbsEvent<AlternativeVerb>>(AddInsertAltVerb);
SubscribeLocalEvent<PseudoItemComponent, EntGotRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<PseudoItemComponent, GettingPickedUpAttemptEvent>(OnGettingPickedUpAttempt);
SubscribeLocalEvent<PseudoItemComponent, DropAttemptEvent>(OnDropAttempt);
SubscribeLocalEvent<PseudoItemComponent, PseudoItemInsertDoAfterEvent>(OnDoAfter);
}
private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent<InnateVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;
if (component.Active)
return;
if (!TryComp<ServerStorageComponent>(args.Target, out var targetStorage))
return;
if (component.Size > targetStorage.StorageCapacityMax - targetStorage.StorageUsed)
return;
if (Transform(args.Target).ParentUid == uid)
return;
InnateVerb verb = new()
{
Act = () =>
{
TryInsert(args.Target, uid, component, targetStorage);
},
Text = Loc.GetString("action-name-insert-self"),
Priority = 2
};
args.Verbs.Add(verb);
}
private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;
if (args.User == args.Target)
return;
if (args.Hands == null)
return;
if (!TryComp<ServerStorageComponent>(args.Hands.ActiveHandEntity, out var targetStorage))
return;
AlternativeVerb verb = new()
{
Act = () =>
{
StartInsertDoAfter(args.User, uid, args.Hands.ActiveHandEntity.Value, component);
},
Text = Loc.GetString("action-name-insert-other", ("target", Identity.Entity(args.Target, EntityManager))),
Priority = 2
};
args.Verbs.Add(verb);
}
private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args)
{
if (!component.Active)
return;
RemComp<ItemComponent>(uid);
component.Active = false;
}
private void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args)
{
if (args.User == args.Item)
return;
Transform(uid).AttachToGridOrMap();
args.Cancel();
}
private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args)
{
if (component.Active)
args.Cancel();
}
private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args)
{
if (args.Handled || args.Cancelled || args.Args.Used == null)
return;
args.Handled = TryInsert(args.Args.Used.Value, uid, component);
}
public bool TryInsert(EntityUid storageUid, EntityUid toInsert, PseudoItemComponent component, ServerStorageComponent? storage = null)
{
if (!Resolve(storageUid, ref storage))
return false;
if (component.Size > storage.StorageCapacityMax - storage.StorageUsed)
return false;
var item = EnsureComp<ItemComponent>(toInsert);
_itemSystem.SetSize(toInsert, component.Size, item);
if (!_storageSystem.Insert(storageUid, toInsert, storage))
{
component.Active = false;
RemComp<ItemComponent>(toInsert);
return false;
} else
{
component.Active = true;
Transform(storageUid).AttachToGridOrMap();
return true;
}
}
private void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, EntityUid storageEntity, PseudoItemComponent? pseudoItem = null)
{
if (!Resolve(toInsert, ref pseudoItem))
return;
var ev = new PseudoItemInsertDoAfterEvent();
var args = new DoAfterArgs(inserter, 5f, ev, toInsert, target: toInsert, used: storageEntity)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
NeedHand = true
};
_doAfter.TryStartDoAfter(args);
}
}

View File

@ -0,0 +1,3 @@
namespace Content.Shared.Actions.Events;
public sealed partial class EatMouseActionEvent : InstantActionEvent {}

View File

@ -0,0 +1,3 @@
namespace Content.Shared.Actions.Events;
public sealed partial class HairballActionEvent : InstantActionEvent {}

View File

@ -0,0 +1,11 @@
using Robust.Shared.Serialization;
using Content.Shared.DoAfter;
namespace Content.Shared.Item.PseudoItem;
[Serializable, NetSerializable]
public sealed partial class PseudoItemInsertDoAfterEvent : SimpleDoAfterEvent
{
}

View File

@ -0,0 +1 @@
hairball.ogg taken from https://en.wikipedia.org/wiki/File:Common_house_cat_coughing_hairball.ogv CC-BY-SA-3.0

View File

@ -0,0 +1,24 @@
- files: ["cat_hiss1.ogg", "cat_hiss2.ogg"]
license: "CC-BY-4.0"
copyright: "Original sound by https://freesound.org/people/secondbody/ - cut out two clips of cat hissing, cleaned up, and converted to ogg."
source: "https://freesound.org/people/secondbody/sounds/50357/"
- files: ["cat_meow1.ogg", "cat_meow2.ogg", "cat_meow3.ogg"]
license: "CC-BY-3.0"
copyright: "Original sound by https://freesound.org/people/ignotus/ - cut out three clips of cat meowing, cleaned up, and converted to ogg."
source: "https://freesound.org/people/ignotus/sounds/26104/"
- files: ["cat_mew1.ogg", "cat_mew2.ogg"]
license: "CC0-1.0"
copyright: "Original sound by https://freesound.org/people/videog/ - cut out two clips of kittens mewing, cleaned up, and converted to ogg."
source: "https://freesound.org/people/videog/sounds/149191/"
- files: ["cat_purr1.ogg"]
license: "CC-BY-4.0"
copyright: "Original sound by https://freesound.org/people/klangstrand/ - cut out short clip, fade in and out, converted to ogg."
source: "https://freesound.org/people/klangstrand/sounds/213951/"
- files: ["cat_growl1.ogg"]
license: "CC0-1.0"
copyright: "Original sound by https://freesound.org/people/Zabuhailo/ - fade in and out, converted to ogg."
source: "https://freesound.org/people/Zabuhailo/sounds/146968/"

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,4 @@
cat_scream1.ogg licensed under CC0 1.0 taken from Queen_Westeros at https://freesound.org/people/queen_westeros/sounds/222590/
cat_scream2.ogg licensed under CC4.0 taken from InspectorJ at https://freesound.org/people/InspectorJ/sounds/415209/
cat_scream3.ogg licensed under CC sampling plus 1.0 taken from Hamface at https://freesound.org/people/Hamface/sounds/98669/
cat_wilhelm.ogg used with apologies to type moon

View File

@ -0,0 +1,8 @@
action-name-hairball = Cough Up Hairball
action-description-hairball = Purge some of your chemstream, and gain a cool hairball to throw at people.
hairball-mask = Take off your {$mask} first.
hairball-cough = {CAPITALIZE(THE($name))} starts coughing up a hairball!
action-name-eat-mouse = Eat Mouse
action-description-eat-mouse = Eat the mouse in your hand, gaining nutriment and a hairball charge.

View File

@ -0,0 +1,56 @@
marking-FelinidEarsBasic = Basic Ears
marking-FelinidEarsBasic-basic_outer = Outer ear
marking-FelinidEarsBasic-basic_inner = Inner ear
marking-FelinidEarsCurled = Curled Ears
marking-FelinidEarsCurled-curled_outer = Outer ear
marking-FelinidEarsCurled-curled_inner = Inner ear
marking-FelinidEarsDroopy = Droopy Ears
marking-FelinidEarsDroopy-droopy_outer = Outer ear
marking-FelinidEarsDroopy-droopy_inner = Inner ear
marking-FelinidEarsFuzzy = Fuzzy Ears
marking-FelinidEarsFuzzy-basic_outer = Outer ear
marking-FelinidEarsFuzzy-fuzzy_inner = Ear fuzz
marking-FelinidEarsStubby = Stubby Ears
marking-FelinidEarsStubby-stubby_outer = Outer ear
marking-FelinidEarsStubby-stubby_inner = Inner ear
marking-FelinidEarsTall = Tall Ears
marking-FelinidEarsTall-tall_outer = Outer ear
marking-FelinidEarsTall-tall_inner = Inner ear
marking-FelinidEarsTall-tall_fuzz = Ear fuzz
marking-FelinidEarsTorn = Torn Ears
marking-FelinidEarsTorn-torn_outer = Outer ear
marking-FelinidEarsTorn-torn_inner = Inner ear
marking-FelinidEarsWide = Wide Ears
marking-FelinidEarsWide-wide_outer = Outer ear
marking-FelinidEarsWide-wide_inner = Inner ear
marking-FelinidTailBasic = Basic Tail
marking-FelinidTailBasic-basic_tail_tip = Tail tip
marking-FelinidTailBasic-basic_tail_stripes_even = Tail stripes, even
marking-FelinidTailBasic-basic_tail_stripes_odd = Tail stripes, odd
marking-FelinidTailBasicWithBow = Basic Tail with Bow
marking-FelinidTailBasicWithBow-basic_tail_tip = Tail tip
marking-FelinidTailBasicWithBow-basic_tail_stripes_even = Tail stripes, even
marking-FelinidTailBasicWithBow-basic_tail_stripes_odd = Tail stripes, odd
marking-FelinidTailBasicWithBow-basic_bow = Bow
marking-FelinidTailBasicWithBell = Basic Tail with Bell
marking-FelinidTailBasicWithBell-basic_tail_tip = Tail tip
marking-FelinidTailBasicWithBell-basic_tail_stripes_even = Tail stripes, even
marking-FelinidTailBasicWithBell-basic_tail_stripes_odd = Tail stripes, odd
marking-FelinidTailBasicWithBell-basic_bell = Bell
marking-FelinidTailBasicWithBowAndBell = Basic Tail with Bow & Bell
marking-FelinidTailBasicWithBowAndBell-basic_tail_tip = Tail tip
marking-FelinidTailBasicWithBowAndBell-basic_tail_stripes_even = Tail stripes, even
marking-FelinidTailBasicWithBowAndBell-basic_tail_stripes_odd = Tail stripes, odd
marking-FelinidTailBasicWithBowAndBell-basic_bow = Bow
marking-FelinidTailBasicWithBowAndBell-basic_bell = Bell

View File

@ -0,0 +1 @@
species-name-felinid = Felinid

View File

@ -1014,6 +1014,7 @@
price: 50
- type: Puller
needsHands: true
- type: FelinidFood # Nyanotrasen - Felinid, ability to eat mice, see Content.Server/Nyanotrasen/Abilities/Felinid/FelinidSystem.cs
- type: entity
parent: MobMouse

View File

@ -275,6 +275,7 @@
- type: GuideHelp
guides:
- MinorAntagonists
- type: FelinidFood # Nyanotrasen - Felinid, ability to eat rat, see Content.Server/Nyanotrasen/Abilities/Felinid/FelinidSystem.cs
- type: entity
id: ActionRatKingRaiseArmy

View File

@ -0,0 +1,21 @@
- type: entity
id: ActionEatMouse
name: action-name-eat-mouse
description: action-description-eat-mouse
noSpawn: true
components:
- type: InstantAction
icon: Nyanotrasen/Icons/verbiconfangs.png
event: !type:EatMouseActionEvent
- type: entity
id: ActionHairball
name: action-name-hairball
description: action-description-hairball
noSpawn: true
components:
- type: InstantAction
charges: 1
icon: { sprite: Nyanotrasen/Objects/Specific/Species/felinid.rsi, state: icon }
useDelay: 30
event: !type:HairballActionEvent

View File

@ -1,3 +1,10 @@
- type: damageModifierSet
id: Felinid
coefficients:
Blunt: 1.15
Slash: 1.15
Piercing: 1.15
# For airlocks, etc, to prevent cheesing.
- type: damageModifierSet
id: MetallicStructure

View File

@ -0,0 +1,49 @@
- type: body
id: Felinid
name: "felinid"
root: torso
slots:
head:
part: HeadHuman
connections:
- torso
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
torso:
part: TorsoHuman
connections:
- left arm
- right arm
- left leg
- right leg
organs:
heart: OrganAnimalHeart
lungs: OrganHumanLungs
stomach: OrganReptilianStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
right arm:
part: RightArmHuman
connections:
- right hand
left arm:
part: LeftArmHuman
connections:
- left hand
right hand:
part: RightHandHuman
left hand:
part: LeftHandHuman
right leg:
part: RightLegHuman
connections:
- right foot
left leg:
part: LeftLegHuman
connections:
- left foot
right foot:
part: RightFootHuman
left foot:
part: LeftFootHuman

View File

@ -0,0 +1,153 @@
# Felinid Ears
- type: marking
id: FelinidEarsBasic
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: basic_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: basic_inner
- type: marking
id: FelinidEarsCurled
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: curled_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: curled_inner
- type: marking
id: FelinidEarsDroopy
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: droopy_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: droopy_inner
- type: marking
id: FelinidEarsFuzzy
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: basic_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: fuzzy_inner
- type: marking
id: FelinidEarsStubby
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: stubby_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: stubby_inner
- type: marking
id: FelinidEarsTall
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: tall_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: tall_inner
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: tall_fuzz
- type: marking
id: FelinidEarsTorn
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: torn_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: torn_inner
- type: marking
id: FelinidEarsWide
bodyPart: HeadTop
markingCategory: HeadTop
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: wide_outer
- sprite: Nyanotrasen/Mobs/Customization/felinid_ears.rsi
state: wide_inner
# Felinid Tails
- type: marking
id: FelinidTailBasic
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_tip
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_even
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_odd
- type: marking
id: FelinidTailBasicWithBow
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_tip
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_even
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_odd
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_bow
- type: marking
id: FelinidTailBasicWithBell
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_tip
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_even
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_odd
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_bell
- type: marking
id: FelinidTailBasicWithBowAndBell
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Felinid]
sprites:
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_tip
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_even
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_tail_stripes_odd
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_bow
- sprite: Nyanotrasen/Mobs/Customization/felinid_tails.rsi
state: basic_bell

View File

@ -0,0 +1,35 @@
- type: entity
save: false
name: Urist McFelinid
parent: MobFelinidBase
id: MobFelinid
components:
- type: CombatMode
- type: InteractionPopup
successChance: 1
interactSuccessString: hugging-success-generic
interactSuccessSound: /Audio/Effects/thudswoosh.ogg
messagePerceivedByOthers: hugging-success-generic-others
- type: MindContainer
showExamineInfo: true
- type: Input
context: "human"
- type: MobMover
- type: InputMover
- type: Respirator
damage:
types:
Asphyxiation: 1.0
damageRecovery:
types:
Asphyxiation: -1.0
- type: Alerts
- type: Actions
- type: Eye
- type: CameraRecoil
- type: Examiner
- type: CanHostGuardian
- type: NpcFactionMember
factions:
- NanoTrasen
# - type: PotentialPsionic

View File

@ -0,0 +1,68 @@
- type: entity
save: false
name: Urist McFelinid
parent: BaseMobHuman
id: MobFelinidBase
abstract: true
components:
- type: Sprite
scale: 0.8, 0.8
- type: HumanoidAppearance
species: Felinid
- type: Fixtures
fixtures: # TODO: This needs a second fixture just for mob collisions.
fix1:
shape:
!type:PhysShapeCircle
radius: 0.28
density: 140
restitution: 0.0
mask:
- MobMask
layer:
- MobLayer
- type: Body
prototype: Felinid
- type: Damageable
damageModifierSet: Felinid
- type: MeleeWeapon
soundHit:
collection: Punch
animation: WeaponArcClaw
damage:
types:
Blunt: 1
Slash: 5
# - type: DiseaseCarrier
# naturalImmunities:
# - OwOnavirus
- type: Thieving
stealthy: true
stripTimeReduction: 1
- type: Speech
speechSounds: Alto
- type: DamageOnHighSpeedImpact
damage:
types:
Blunt: 1
- type: Stamina
critThreshold: 85
- type: PseudoItem
- type: Vocal
wilhelm: "/Audio/Nyanotrasen/Voice/Felinid/cat_wilhelm.ogg"
sounds:
Male: MaleFelinid
Female: FemaleFelinid
Unsexed: MaleFelinid
- type: Felinid
- type: entity
save: false
name: Urist McHands
parent: MobHumanDummy
id: MobFelinidDummy
noSpawn: true
description: A dummy felinid meant to be used in character setup.
components:
- type: HumanoidAppearance
species: Felinid

View File

@ -0,0 +1,23 @@
- type: entity
parent: BaseItem
id: Hairball
name: hairball
description: Felinids, man... Placeholder sprite.
components:
- type: Sprite
sprite: Nyanotrasen/Objects/Specific/Species/felinid.rsi
state: icon
- type: Hairball
- type: SolutionContainerManager
solutions:
hairball:
maxVol: 25
reagents:
- ReagentId: Protein
Quantity: 2
- type: Extractable
grindableSolutionName: hairball
- type: Tag
tags:
- Recyclable
- Trash

View File

@ -0,0 +1,35 @@
- type: soundCollection
id: FelinidScreams
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_scream1.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_scream2.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_scream3.ogg
- type: soundCollection
id: FelinidHisses
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_hiss1.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_hiss2.ogg
- type: soundCollection
id: FelinidMeows
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_meow1.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_meow2.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_meow3.ogg
- type: soundCollection
id: FelinidMews
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_mew1.ogg
- /Audio/Nyanotrasen/Voice/Felinid/cat_mew2.ogg
- type: soundCollection
id: FelinidGrowls
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_growl1.ogg
- type: soundCollection
id: FelinidPurrs
files:
- /Audio/Nyanotrasen/Voice/Felinid/cat_purr1.ogg

View File

@ -0,0 +1,36 @@
- type: species
id: Felinid
name: species-name-felinid
roundStart: true
prototype: MobFelinid
sprites: MobHumanSprites
markingLimits: MobFelinidMarkingLimits
dollPrototype: MobFelinidDummy
skinColoration: HumanToned
- type: markingPoints
id: MobFelinidMarkingLimits
points:
Hair:
points: 1
required: false
FacialHair:
points: 1
required: false
Tail:
points: 1
required: true
defaultMarkings: [ FelinidTailBasic ]
HeadTop:
points: 1
required: true
defaultMarkings: [ FelinidEarsBasic ]
Chest:
points: 1
required: false
Legs:
points: 2
required: false
Arms:
points: 2
required: false

View File

@ -1,3 +1,60 @@
# species
- type: emoteSounds
id: MaleFelinid
params:
variation: 0.125
sounds:
Scream:
collection: FelinidScreams
Laugh:
collection: MaleLaugh
Sneeze:
collection: MaleSneezes
Cough:
collection: MaleCoughs
Crying:
collection: MaleCry
Whistle:
collection: Whistles
Hiss:
collection: FelinidHisses
Meow:
collection: FelinidMeows
Mew:
collection: FelinidMews
Growl:
collection: FelinidGrowls
Purr:
collection: FelinidPurrs
- type: emoteSounds
id: FemaleFelinid
params:
variation: 0.125
sounds:
Scream:
collection: FelinidScreams
Laugh:
collection: FemaleLaugh
Sneeze:
collection: FemaleSneezes
Cough:
collection: FemaleCoughs
Crying:
collection: FemaleCry
Whistle:
collection: Whistles
Hiss:
collection: FelinidHisses
Meow:
collection: FelinidMeows
Mew:
collection: FelinidMews
Growl:
collection: FelinidGrowls
Purr:
collection: FelinidPurrs
# mobs
- type: emoteSounds
id: Mothroach

View File

@ -0,0 +1,68 @@
# vocal emotes
- type: emote
id: Hiss
category: Vocal
chatMessages: [hisses.]
chatTriggers:
- hiss
- hisses
- hisses.
- hisses!
- hissing
- hissed
- type: emote
id: Meow
category: Vocal
chatMessages: [meows.]
chatTriggers:
- meow
- meows
- meows.
- meows!
- meowing
- meowed
- miau
- miaus
- miaus.
- miaus!
- nya
- nyas
- nyas.
- nyas!
- type: emote
id: Mew
category: Vocal
chatMessages: [mews.]
chatTriggers:
- mew
- mews
- mews.
- mews!
- mewing
- mewed
- type: emote
id: Growl
category: Vocal
chatMessages: [growls.]
chatTriggers:
- growl
- growls
- growls.
- growls!
- growling
- growled
- type: emote
id: Purr
category: Vocal
chatMessages: [purrs.]
chatTriggers:
- purr
- purrs
- purrs.
- purrs!
- purring
- purred

View File

@ -30,6 +30,8 @@
RLeg: MobHumanRLeg
LFoot: MobHumanLFoot
RFoot: MobHumanRFoot
Tail: MobHumanoidAnyMarking # Nyanotrasen - Felinid
HeadTop: MobHumanoidAnyMarking # Nyanotrasen - Felinid & Oni
- type: markingPoints
id: MobHumanMarkingLimits

View File

@ -5,4 +5,6 @@
Human: 5
Reptilian: 4
SlimePerson: 4
Felinid: 4 # Nyanotrasen - Felinid, see Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml
Vulpkanin: 3 # DeltaV - Vulpkanin, see Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml
Diona: 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

View File

@ -0,0 +1,75 @@
{
"version": 1,
"copyright": "@Vordenburg",
"license": "CC-BY-SA-4.0",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "basic_inner",
"directions": 4
},
{
"name": "basic_outer",
"directions": 4
},
{
"name": "curled_inner",
"directions": 4
},
{
"name": "curled_outer",
"directions": 4
},
{
"name": "fuzzy_inner",
"directions": 4
},
{
"name": "tall_outer",
"directions": 4
},
{
"name": "tall_inner",
"directions": 4
},
{
"name": "tall_fuzz",
"directions": 4
},
{
"name": "torn_outer",
"directions": 4
},
{
"name": "torn_inner",
"directions": 4
},
{
"name": "stubby_outer",
"directions": 4
},
{
"name": "stubby_inner",
"directions": 4
},
{
"name": "droopy_outer",
"directions": 4
},
{
"name": "droopy_inner",
"directions": 4
},
{
"name": "wide_inner",
"directions": 4
},
{
"name": "wide_outer",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

View File

@ -0,0 +1,181 @@
{
"version": 1,
"copyright": "@Vordenburg",
"license": "CC-BY-SA-4.0",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "basic_tail_tip",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "basic_tail_stripes_even",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "basic_tail_stripes_odd",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "basic_bow",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "basic_bell",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2,
0.2,
0.2
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

View File

@ -0,0 +1,14 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Made by QuizzyQuin",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
}
]
}