From 35f7ad8f8e1075897f5001f71b250e9b6f02dab5 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sun, 5 Oct 2025 03:54:35 +0200 Subject: [PATCH 1/4] draft --- .../tips/yaml-inheritance.md | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/en/general-development/tips/yaml-inheritance.md diff --git a/src/en/general-development/tips/yaml-inheritance.md b/src/en/general-development/tips/yaml-inheritance.md new file mode 100644 index 0000000000..5667e49286 --- /dev/null +++ b/src/en/general-development/tips/yaml-inheritance.md @@ -0,0 +1,185 @@ +# YAML Inheritance Rules + +YAML prototype inheritance rules are confusing, so here is a guide. + +## Inheritance + +There are different ways datafields get merged or overwritten when inheriting from another yaml prototype and they can be a little confusing. +Let's look at a few examples: +```yml +# Define a few tags for testing purposes. +# Normally these should be in tags.yml. +- type: Tag + id: TagA1 + +- type: Tag + id: TagA2 + +- type: Tag + id: TagB + +- type: entity + abstract: true + id: ParentA + components: + - type: Tag + tags: + - TagA1 + - TagA2 + - type: MeleeWeapon # turn the entity into a weapon + damage: + types: + Heat: 10 + +- type: entity + abstract: true + id: ParentB + components: + - type: Tag + tags: + - TagB + - type: PointLight # make it glow + color: green + +- type: entity + abstract: true + id: ParentC + components: + - type: Sprite + sprite: Objects/Fun/Plushies/hampter.rsi # change the sprite folder only, but not the state + +# A simple item with a lizard sprite. +# A sprite needs both the 'sprite' datafield, which contains the path to the rsi folder the image file is in, +# and the 'state' datafield which is the name of the .png file itself. +- type: entity + parent: BaseItem + id: TestItem1 + name: test lizard 1 + components: + - type: Sprite + sprite: Objects/Fun/Plushies/lizard.rsi + state: icon # this state name is the same for all basic plushie sprites + +# The lizard sprite rsi is inherited first from TestItem1. +# The rsi is not overwritten by the hampter because it already exists in the first parent. +# So the resulting entity has a lizard sprite. +- type: entity + parent: [ TestItem1, ParentC ] + id: TestItem2 + name: test lizard 2 + +# If we inherit in this order the state will be taken from ParentC. +# TestItem1 will then add the state datafield, but not overwrite the rsi. +# The result will be a hampter. +- type: entity + parent: [ ParentC, TestItem1] + id: TestItem3 + name: test hampter 3 + +# This time we manually overwrite the inherited rsi. +# The inherited state remains unchanged. +# The result will be another hampter. +- type: entity + parent: TestItem1 + id: TestItem4 + name: test hampter 4 + components: + - type: Sprite + sprite: Objects/Fun/Plushies/hampter.rsi + +# This item will inherit the tags from ParentA, but not from ParentB. +# So it will have TagA1 and TagA2. +# The item will have both the PointLightComponent and the MeleeWeaponComponent and the corresponding datafields set in the parents. +- type: entity + parent: [ TestItem1, ParentA, ParentB ] + id: TestItem5 + name: test lizard 5 + +# To fix this and make the entity have all 3 tags we have to redefine the list manually. +- type: entity + parent: [ TestItem1, ParentA, ParentB ] + id: TestItem6 + name: test lizard 6 + components: + - type: Tag + tags: # this overwrites the inherited list + - TagA1 + - TagA2 + - TagB +``` + + +To summarize the inheritance rules: + 1. A datafield that is not set in yaml gets its default value from its C# definition. In C# all variables have a default value if not specified otherwise, for example `public bool SomeVariable;` will always be `false` (in other programming languages you may get random bits). + 2. If you inherit from multiple parents then components and their datafields are merged, but not overwritten. The order of inheritance matters. + 3. You can overwrite datafields by reassigning a new value in the child. + 4. If a datafield is overwritten, then the whole instance of the variable is reassigned. This means datatypes like lists (for example for tags) won't get merged, but replaced. + 5. You cannot remove components that are inherited from a parent. You will have to make another abstract parent instead to avoid copy pasting everything. + +```admonish warning +Common Mistake: +Rule 4 often gets overlooked for tags. If you add a new tag to an EntityPrototype make sure that +- the new list of tags for that entity contains all tags that were previously inherited. +- any child prototypes that have their own tags explicitly list the new tag as well. +``` + +## Abstract prototypes +If you got a base prototype that should not a fully functioning prototype on its own, but used for inheritance, then make sure to mark it as `abstract`. This will make sure it cannot be spawned by any means and it will be hidden from the F5 spawn menu. + Example: + ```yml + # A simplified plushie that inherits from BaseItem and has incomplete SpriteComponent datafields. + - type: entity + abstract: true # should not be spawned since this prototype is incomplete + parent: BaseItem + id: BasePlushie + components: + - type: Sprite + sprite: Objects/Fun/toys.rsi # we have a sprite, but not the state + - type: EmitSoundOnUse # make it squeak when used + sound: + collection: ToySqueak + + # A plushie that can be spawned in the game. + - type: entity + parent: BasePlushie + id: PlushieBee + name: bee plushie + components: + - type: Sprite + state: plushie_h # add a state (the sprite path is inherited from BasePlushie) + + # And another one. + - type: entity + parent: BasePlushie + id: PlushieLizard # Weh! + name: lizard plushie + components: + - type: Sprite + state: plushie_lizard + ``` + +If a prototype that is a fully functioning entity on its own should only be hidden from the F5 spawn menu, but can still be spawned through other means in-game, then you should be using The `HideSpawnMenu` category. +Examples are mind entities, objectives, or visual effects like this one used for flashbangs: + ``` + - type: entity + id: GrenadeFlashEffect + categories: [ HideSpawnMenu ] + components: + - type: PointLight + enabled: true + radius: 5 + energy: 8 + netsync: false + - type: LightFade + duration: 0.5 + - type: TimedDespawn + lifetime: 0.5 + ``` + This is a simple pointlight that quickly disappears and deletes itself. We use `categories: [HideSpawnMenu]` to make sure it does not show up in the spawn menu, but it can still be spawned using the `Spawn(protoId, coords)` method. + This will also exclude it from several integration tests, which may otherwise fail for such an entity. + +## AlwaysPushInheritance, NeverPushInheritance + +## Making a prototype inheritable +The above examples were all for `EntityPrototype`s, but they work the same for any other type of prototype. You can define your own prototype in C# and make it store datafields. To make it inheritable in YAML you will have to implement the `` interface. +Example: From 7ed76b8ca2bb2d82ed91cb994417c1132150a6b1 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Wed, 6 May 2026 22:53:17 +0200 Subject: [PATCH 2/4] the rest of the owl --- .../tips/yaml-inheritance.md | 223 ++++++++++++------ 1 file changed, 148 insertions(+), 75 deletions(-) diff --git a/src/en/general-development/tips/yaml-inheritance.md b/src/en/general-development/tips/yaml-inheritance.md index 5667e49286..d4cc86d9c5 100644 --- a/src/en/general-development/tips/yaml-inheritance.md +++ b/src/en/general-development/tips/yaml-inheritance.md @@ -1,10 +1,7 @@ -# YAML Inheritance Rules +# YAML Inheritance Guide +### Inheritance Rules -YAML prototype inheritance rules are confusing, so here is a guide. - -## Inheritance - -There are different ways datafields get merged or overwritten when inheriting from another yaml prototype and they can be a little confusing. +There are different ways DataFields get merged or overwritten when inheriting from another prototype in YAML and they can be a little confusing. Let's look at a few examples: ```yml # Define a few tags for testing purposes. @@ -52,9 +49,10 @@ Let's look at a few examples: # A sprite needs both the 'sprite' datafield, which contains the path to the rsi folder the image file is in, # and the 'state' datafield which is the name of the .png file itself. - type: entity - parent: BaseItem + parent: BaseItem # This parent has a bunch of components giving it physics, a fixture and allowing us to pick it up.. id: TestItem1 - name: test lizard 1 + name: test item 1 + description: lizard components: - type: Sprite sprite: Objects/Fun/Plushies/lizard.rsi @@ -66,15 +64,17 @@ Let's look at a few examples: - type: entity parent: [ TestItem1, ParentC ] id: TestItem2 - name: test lizard 2 + name: test item 2 + description: lizard -# If we inherit in this order the state will be taken from ParentC. -# TestItem1 will then add the state datafield, but not overwrite the rsi. +# If we inherit in this order the rsi path will be taken from ParentC first. +# TestItem1 will then add the state Datafield, but not overwrite the rsi. # The result will be a hampter. - type: entity - parent: [ ParentC, TestItem1] + parent: [ ParentC, TestItem1 ] id: TestItem3 - name: test hampter 3 + name: test item 3 + description: hampter # This time we manually overwrite the inherited rsi. # The inherited state remains unchanged. @@ -82,24 +82,27 @@ Let's look at a few examples: - type: entity parent: TestItem1 id: TestItem4 - name: test hampter 4 + name: test item 4 + description: hampter components: - type: Sprite sprite: Objects/Fun/Plushies/hampter.rsi # This item will inherit the tags from ParentA, but not from ParentB. # So it will have TagA1 and TagA2. -# The item will have both the PointLightComponent and the MeleeWeaponComponent and the corresponding datafields set in the parents. +# The item will have both the PointLightComponent and the MeleeWeaponComponent and the corresponding DataFields set in the parents. - type: entity parent: [ TestItem1, ParentA, ParentB ] id: TestItem5 - name: test lizard 5 + name: test item 5 + description: lizard # To fix this and make the entity have all 3 tags we have to redefine the list manually. - type: entity parent: [ TestItem1, ParentA, ParentB ] id: TestItem6 - name: test lizard 6 + name: test item 6 + description: lizard components: - type: Tag tags: # this overwrites the inherited list @@ -110,76 +113,146 @@ Let's look at a few examples: To summarize the inheritance rules: - 1. A datafield that is not set in yaml gets its default value from its C# definition. In C# all variables have a default value if not specified otherwise, for example `public bool SomeVariable;` will always be `false` (in other programming languages you may get random bits). - 2. If you inherit from multiple parents then components and their datafields are merged, but not overwritten. The order of inheritance matters. - 3. You can overwrite datafields by reassigning a new value in the child. - 4. If a datafield is overwritten, then the whole instance of the variable is reassigned. This means datatypes like lists (for example for tags) won't get merged, but replaced. - 5. You cannot remove components that are inherited from a parent. You will have to make another abstract parent instead to avoid copy pasting everything. + 1. A DataField that is not set in YAML gets its default value from its C# definition. In C# all variables have a default value if not specified otherwise, for example `public bool SomeVariable;` will always be `false` (in other programming languages you may get random bits). + 2. If you inherit from multiple parents, then components and their DataFields are inherited individually. + 3. The order of inheritance matters: Each DataField will be taken from the first parent that has it set in YAML. + 4. You can overwrite inherited DataFields by reassigning a new value in the child. + 5. If a DataField is overwritten, then the whole instance of the variable is reassigned. This means data types like lists (for example for tags) won't get merged, but replaced. However, you can change this behaviour for individual DataFields using `AlwasPushInheritance`(see further below). + 6. You cannot remove components that are inherited from a parent. You will have to make another abstract parent without that component instead to avoid copy pasting everything. ```admonish warning Common Mistake: -Rule 4 often gets overlooked for tags. If you add a new tag to an EntityPrototype make sure that +Rule 5 often gets overlooked for tags. If you add a new tag to an EntityPrototype make sure that - the new list of tags for that entity contains all tags that were previously inherited. - any child prototypes that have their own tags explicitly list the new tag as well. ``` -## Abstract prototypes -If you got a base prototype that should not a fully functioning prototype on its own, but used for inheritance, then make sure to mark it as `abstract`. This will make sure it cannot be spawned by any means and it will be hidden from the F5 spawn menu. - Example: - ```yml - # A simplified plushie that inherits from BaseItem and has incomplete SpriteComponent datafields. - - type: entity - abstract: true # should not be spawned since this prototype is incomplete - parent: BaseItem - id: BasePlushie - components: - - type: Sprite - sprite: Objects/Fun/toys.rsi # we have a sprite, but not the state +### Abstract Prototypes +If you got a base prototype that should not a fully functioning prototype on its own, but used for inheritance, then make sure to mark it as `abstract`. This will make sure it cannot be spawned by any means, it will be hidden from the F5 spawn menu, and will be ignored by integration tests. +Example: +```yml +# A simplified plushie that inherits from BaseItem and has incomplete SpriteComponent DataFields. +- type: entity + abstract: true # should not be spawned since this prototype is incomplete + parent: BaseItem + id: BasePlushie + components: + - type: Sprite + state: icon # all plushie sprites have the same state name - type: EmitSoundOnUse # make it squeak when used sound: collection: ToySqueak - # A plushie that can be spawned in the game. - - type: entity - parent: BasePlushie - id: PlushieBee - name: bee plushie - components: - - type: Sprite - state: plushie_h # add a state (the sprite path is inherited from BasePlushie) - - # And another one. - - type: entity - parent: BasePlushie - id: PlushieLizard # Weh! - name: lizard plushie - components: - - type: Sprite - state: plushie_lizard - ``` +# A plushie that can be spawned in the game. +- type: entity + parent: BasePlushie + id: PlushieBee + name: bee plushie + components: + - type: Sprite + state: plushie_h # add a state (the sprite path is inherited from BasePlushie) + +# And another one. +- type: entity + parent: BasePlushie + id: PlushieLizard # Weh! + name: lizard plushie + components: + - type: Sprite + state: plushie_lizard +``` If a prototype that is a fully functioning entity on its own should only be hidden from the F5 spawn menu, but can still be spawned through other means in-game, then you should be using The `HideSpawnMenu` category. Examples are mind entities, objectives, or visual effects like this one used for flashbangs: - ``` - - type: entity - id: GrenadeFlashEffect - categories: [ HideSpawnMenu ] - components: - - type: PointLight - enabled: true - radius: 5 - energy: 8 - netsync: false - - type: LightFade - duration: 0.5 - - type: TimedDespawn - lifetime: 0.5 - ``` - This is a simple pointlight that quickly disappears and deletes itself. We use `categories: [HideSpawnMenu]` to make sure it does not show up in the spawn menu, but it can still be spawned using the `Spawn(protoId, coords)` method. - This will also exclude it from several integration tests, which may otherwise fail for such an entity. - -## AlwaysPushInheritance, NeverPushInheritance - -## Making a prototype inheritable -The above examples were all for `EntityPrototype`s, but they work the same for any other type of prototype. You can define your own prototype in C# and make it store datafields. To make it inheritable in YAML you will have to implement the `` interface. +```yml +- type: entity + id: GrenadeFlashEffect + categories: [ HideSpawnMenu ] + components: + - type: PointLight + enabled: true + radius: 5 + energy: 8 + netsync: false + - type: LightFade + duration: 0.5 + - type: TimedDespawn + lifetime: 0.5 +``` +This is a simple pointlight that quickly disappears and deletes itself. We use `categories: [HideSpawnMenu]` to make sure it does not show up in the spawn menu, but it can still be spawned through code using the `Spawn(protoId, coords)` method or similar. +This will also exclude it from several integration tests, which may otherwise fail for such an entity. + +### Making a prototype inheritable +The above examples were all for `EntityPrototype`s, but they work the same for any other type of prototype. You can define your own prototype in C# and make it store DataFields. To make it inheritable in YAML you will have to implement the `IInheritingPrototype` interface. This requires a DataField for the parents and a bool for being abstract. Example: +```csharp +/// +/// A simple inheritable prototype for testing purposes. +/// +[Prototype] +public sealed partial class InheritanceTestPrototype : IPrototype, IInheritingPrototype +{ + /// + [IdDataField] + public string ID { get; private set; } = default!; + + /// + [ParentDataField(typeof(AbstractPrototypeIdArraySerializer))] + public string[]? Parents { get; private set; } + + /// + [NeverPushInheritance] + [AbstractDataField] + public bool Abstract { get; private set; } + + /// + /// A string you can set in YAML. + /// + [DataField] + public string Field1 = "Field1 default value" + + /// + /// And a second one. + /// + [DataField] + public string Field2 = "Field2 default value" + + /// + /// A datafield with different inheritance behaviour. + /// This List will be merged instead of overwritten. + /// + [DataField, AlwaysPushInheritance] + public List AlwaysInheritedField = new(); + + /// + /// A datafield with different inheritance behaviour. + /// This one will never get inherited. + /// + [DataField] + public string NeverInheridedField = "default value" +} +``` + +```yml +- type: inheritanceTest + id: Parent1 + field1: foo + +- type: inheritanceTest + id: Parent2 + field2: bar + +- type: inheritanceTest + parents: [ Parent1, Parent2 ] + id: Child + # This will inherit both field1 being foo, and field2 being bar. +``` + +### AlwaysPushInheritance, NeverPushInheritance +These two attributes can change the inheritance behaviour for a specific DataField. This works both for DataFields inside Components and inside custom Prototypes. +`AlwaysPushInheritance` will make `List`s and `HashSet`s get merged instead of overwritten. + +`NeverPushInheritance` will make this DataField not get inherited at all. + +### The End! +Congrats, you are now a YAML expert. Now go out there and make some PRs! From 8a9d91f2d4c189c9848114cd2d8773ea161a459b Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 9 May 2026 15:22:45 +0200 Subject: [PATCH 3/4] complete attribute section --- .../tips/yaml-inheritance.md | 67 ++++++++++++++----- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/src/en/general-development/tips/yaml-inheritance.md b/src/en/general-development/tips/yaml-inheritance.md index d4cc86d9c5..bc208cf705 100644 --- a/src/en/general-development/tips/yaml-inheritance.md +++ b/src/en/general-development/tips/yaml-inheritance.md @@ -37,6 +37,7 @@ Let's look at a few examples: - TagB - type: PointLight # make it glow color: green + energy: 10 - type: entity abstract: true @@ -113,16 +114,17 @@ Let's look at a few examples: To summarize the inheritance rules: - 1. A DataField that is not set in YAML gets its default value from its C# definition. In C# all variables have a default value if not specified otherwise, for example `public bool SomeVariable;` will always be `false` (in other programming languages you may get random bits). - 2. If you inherit from multiple parents, then components and their DataFields are inherited individually. - 3. The order of inheritance matters: Each DataField will be taken from the first parent that has it set in YAML. - 4. You can overwrite inherited DataFields by reassigning a new value in the child. - 5. If a DataField is overwritten, then the whole instance of the variable is reassigned. This means data types like lists (for example for tags) won't get merged, but replaced. However, you can change this behaviour for individual DataFields using `AlwasPushInheritance`(see further below). - 6. You cannot remove components that are inherited from a parent. You will have to make another abstract parent without that component instead to avoid copy pasting everything. + 1. A prototype can have one or multiple parents. + 2. A DataField that is not set in YAML gets its default value from its C# definition. In C# all variables have a default value if not specified otherwise, for example `public bool SomeVariable;` will always be `false` (in other programming languages you may get random bits). + 3. If you inherit from multiple parents, then components and their DataFields are inherited individually. + 4. The order of inheritance matters: Each DataField will be taken from the first parent that has it set in YAML. + 5. You can overwrite inherited DataFields by reassigning a new value in the child. + 6. If a DataField is overwritten, then the whole instance of the variable is reassigned. This means data types like lists (for example for tags) won't get merged, but replaced. However, you can change this behaviour for individual DataFields using the `AlwasPushInheritance`attribute (see further below for a detailed explanation). + 7. You cannot remove components that are inherited from a parent. You will have to make another abstract parent without that component instead to avoid copy pasting everything. ```admonish warning Common Mistake: -Rule 5 often gets overlooked for tags. If you add a new tag to an EntityPrototype make sure that +Rule 6 often gets overlooked for tags. If you add a new tag to an EntityPrototype make sure that - the new list of tags for that entity contains all tags that were previously inherited. - any child prototypes that have their own tags explicitly list the new tag as well. ``` @@ -186,6 +188,9 @@ This will also exclude it from several integration tests, which may otherwise fa The above examples were all for `EntityPrototype`s, but they work the same for any other type of prototype. You can define your own prototype in C# and make it store DataFields. To make it inheritable in YAML you will have to implement the `IInheritingPrototype` interface. This requires a DataField for the parents and a bool for being abstract. Example: ```csharp +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; + /// /// A simple inheritable prototype for testing purposes. /// @@ -204,18 +209,18 @@ public sealed partial class InheritanceTestPrototype : IPrototype, IInheritingPr [NeverPushInheritance] [AbstractDataField] public bool Abstract { get; private set; } - + /// /// A string you can set in YAML. /// [DataField] - public string Field1 = "Field1 default value" + public string Field1 = "Field1 default value"; /// /// And a second one. /// [DataField] - public string Field2 = "Field2 default value" + public string Field2 = "Field2 default value"; /// /// A datafield with different inheritance behaviour. @@ -229,7 +234,7 @@ public sealed partial class InheritanceTestPrototype : IPrototype, IInheritingPr /// This one will never get inherited. /// [DataField] - public string NeverInheridedField = "default value" + public string NeverInheridedField = "default value"; } ``` @@ -243,16 +248,46 @@ public sealed partial class InheritanceTestPrototype : IPrototype, IInheritingPr field2: bar - type: inheritanceTest - parents: [ Parent1, Parent2 ] + parent: [ Parent1, Parent2 ] id: Child # This will inherit both field1 being foo, and field2 being bar. ``` ### AlwaysPushInheritance, NeverPushInheritance These two attributes can change the inheritance behaviour for a specific DataField. This works both for DataFields inside Components and inside custom Prototypes. -`AlwaysPushInheritance` will make `List`s and `HashSet`s get merged instead of overwritten. -`NeverPushInheritance` will make this DataField not get inherited at all. +`AlwaysPushInheritance` will make `List`s, `HashSet`s and `Dictionaries` get merged instead of overwritten. +```yml +- type: inheritanceTest + id: Parent1 + alwaysInheritedField: + - foo + +- type: inheritanceTest + id: Parent2 + alwaysInheritedField: + - bar + +- type: inheritanceTest + parent: [ Parent1, Parent2 ] + id: Child + alwaysInheritedField: + - weh +``` +The `Child` prototype will have a `AlwaysInheritedField` DataField with a list containing all three strings `foo`, `bar` and `weh`. +This comes at the downside of not being able to remove any entries from that list in any inheritors. Ideally in the future we will add a more powerful YAML syntax to allow us to select the inheritance behaviour on a case by case basis (see some discussion [here](https://github.com/space-wizards/RobustToolbox/issues/5141), [here](https://github.com/space-wizards/space-station-14/issues/43326) and [here](https://forum.spacestation14.com/t/move-tags-to-rt-add-proper-entity-prototype-syntax/22759)). +This also works recursively for any custom `DataDefinition`s, see for example the `Solution` DataField in the `SolutionComponent`. + +`NeverPushInheritance` will make a DataField not get inherited at all. +```yml +- type: inheritanceTest + id: Parent + neverInheridedField: weh + +- type: inheritanceTest + parent: Parent + id: Child + # neverInheritedField will be the C# default "default value" again +``` -### The End! -Congrats, you are now a YAML expert. Now go out there and make some PRs! +So it's recommended to always take a look at a component's or prototype's C# definition to see which attributes their DataFields have and which inheritance behaviour they will follow. From 8e19ce212ec5b54ce34fb480cd7f6b337dd5d9ef Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 9 May 2026 15:34:53 +0200 Subject: [PATCH 4/4] move file and summary entry --- src/SUMMARY.md | 1 + .../yaml-inheritance-guide.md} | 0 2 files changed, 1 insertion(+) rename src/en/{general-development/tips/yaml-inheritance.md => ss14-by-example/yaml-inheritance-guide.md} (100%) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index bdd709ca0a..c7f21c93eb 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -53,6 +53,7 @@ SS14 By Example - [Adding a Simple Bikehorn](en/ss14-by-example/adding-a-simple-bikehorn.md) - [Making a Sprite Dynamic](en/ss14-by-example/making-a-sprite-dynamic.md) - [Porting Appearance Visualizers](en/ss14-by-example/making-a-sprite-dynamic/porting-appearance-visualizers.md) +- [YAML Inheritance Guide](en/ss14-by-example/yaml-inheritance-guide.md) - [Basic Networking and You](en/ss14-by-example/basic-networking-and-you.md) - [Guide to Prediction](en/ss14-by-example/prediction-guide.md) - [Fluent and Localization](en/ss14-by-example/fluent-and-localization.md) diff --git a/src/en/general-development/tips/yaml-inheritance.md b/src/en/ss14-by-example/yaml-inheritance-guide.md similarity index 100% rename from src/en/general-development/tips/yaml-inheritance.md rename to src/en/ss14-by-example/yaml-inheritance-guide.md