Random book story generator refactor (#28082)
* Randomized book overhaul * Fix prototype names * Improved setting paper content * Praise Ratvar
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
|
using Content.Shared.StoryGen;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Paper;
|
namespace Content.Server.Paper;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds randomly generated stories to Paper component
|
/// Adds a randomly generated story to the content of a <see cref="PaperComponent"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, Access(typeof(PaperRandomStorySystem))]
|
[RegisterComponent, Access(typeof(PaperRandomStorySystem))]
|
||||||
public sealed partial class PaperRandomStoryComponent : Component
|
public sealed partial class PaperRandomStoryComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="StoryTemplatePrototype"/> ID to use for story generation.
|
||||||
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public List<string>? StorySegments;
|
public ProtoId<StoryTemplatePrototype> Template;
|
||||||
|
|
||||||
[DataField]
|
|
||||||
public string StorySeparator = " ";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Content.Server.RandomMetadata;
|
using Content.Shared.StoryGen;
|
||||||
|
|
||||||
namespace Content.Server.Paper;
|
namespace Content.Server.Paper;
|
||||||
|
|
||||||
public sealed class PaperRandomStorySystem : EntitySystem
|
public sealed class PaperRandomStorySystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly StoryGeneratorSystem _storyGen = default!;
|
||||||
[Dependency] private readonly RandomMetadataSystem _randomMeta = default!;
|
[Dependency] private readonly PaperSystem _paper = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -19,11 +19,9 @@ public sealed class PaperRandomStorySystem : EntitySystem
|
|||||||
if (!TryComp<PaperComponent>(paperStory, out var paper))
|
if (!TryComp<PaperComponent>(paperStory, out var paper))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (paperStory.Comp.StorySegments == null)
|
if (!_storyGen.TryGenerateStoryFromTemplate(paperStory.Comp.Template, out var story))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var story = _randomMeta.GetRandomFromSegments(paperStory.Comp.StorySegments, paperStory.Comp.StorySeparator);
|
_paper.SetContent(paperStory.Owner, story, paper);
|
||||||
|
|
||||||
paper.Content += $"\n{story}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Shared.StoryGen;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to generate a story from a <see cref="StoryTemplatePrototype"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class StoryGeneratorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to generate a random story using the given template, picking a random word from the referenced
|
||||||
|
/// datasets for each variable and passing them into the localization system with template.
|
||||||
|
/// If <paramref name="seed"/> is specified, the randomizer will be seeded with it for consistent story generation;
|
||||||
|
/// otherwise the variables will be randomized.
|
||||||
|
/// Fails if the template prototype cannot be loaded.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the template was loaded, otherwise false.</returns>
|
||||||
|
public bool TryGenerateStoryFromTemplate(ProtoId<StoryTemplatePrototype> template, [NotNullWhen(true)] out string? story, int? seed = null)
|
||||||
|
{
|
||||||
|
// Get the story template prototype from the ID
|
||||||
|
if (!_protoMan.TryIndex(template, out var templateProto))
|
||||||
|
{
|
||||||
|
story = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If given a seed, use it
|
||||||
|
if (seed != null)
|
||||||
|
_random.SetSeed(seed.Value);
|
||||||
|
|
||||||
|
// Pick values for all of the variables in the template
|
||||||
|
var variables = new ValueList<(string, object)>(templateProto.Variables.Count);
|
||||||
|
foreach (var (name, list) in templateProto.Variables)
|
||||||
|
{
|
||||||
|
// Get the prototype for the world list dataset
|
||||||
|
if (!_protoMan.TryIndex(list, out var listProto))
|
||||||
|
continue; // Missed one, but keep going with the rest of the story
|
||||||
|
|
||||||
|
// Pick a random word from the dataset and localize it
|
||||||
|
var chosenWord = Loc.GetString(_random.Pick(listProto.Values));
|
||||||
|
variables.Add((name, chosenWord));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the variables to the localization system and build the story
|
||||||
|
story = Loc.GetString(templateProto.LocId, variables.ToArray());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs
Normal file
33
Content.Shared/StoryGen/Prototypes/StoryTemplatePrototype.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Content.Shared.Dataset;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.StoryGen;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prototype for a story template that can be filled in with words chosen from <see cref="DatasetPrototype"/>s.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, Prototype("storyTemplate")]
|
||||||
|
public sealed partial class StoryTemplatePrototype : IPrototype
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifier for this prototype instance.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[IdDataField]
|
||||||
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Localization ID of the Fluent string that forms the structure of this story.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId LocId { get; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary containing the name of each variable to pass to the template and the ID of the
|
||||||
|
/// <see cref="DatasetPrototype"/> from which a random entry will be selected as its value.
|
||||||
|
/// For example, <c>name: book_character</c> will pick a random entry from the book_character
|
||||||
|
/// dataset which can then be used in the template by <c>{$name}</c>.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Dictionary<string, ProtoId<DatasetPrototype>> Variables { get; } = default!;
|
||||||
|
}
|
||||||
@@ -86,7 +86,7 @@ story-gen-book-character29 = space dragon
|
|||||||
story-gen-book-character30 = revolutionary
|
story-gen-book-character30 = revolutionary
|
||||||
story-gen-book-character31 = nuclear operative
|
story-gen-book-character31 = nuclear operative
|
||||||
story-gen-book-character32 = narsie cultist
|
story-gen-book-character32 = narsie cultist
|
||||||
story-gen-book-character33 = ratwar cultist
|
story-gen-book-character33 = ratvar cultist
|
||||||
story-gen-book-character34 = greytider
|
story-gen-book-character34 = greytider
|
||||||
story-gen-book-character35 = arachnid
|
story-gen-book-character35 = arachnid
|
||||||
story-gen-book-character36 = vox
|
story-gen-book-character36 = vox
|
||||||
@@ -125,16 +125,16 @@ story-gen-book-event2 = a nuclear explosion
|
|||||||
story-gen-book-event3 = a mass murder
|
story-gen-book-event3 = a mass murder
|
||||||
story-gen-book-event4 = a sudden depressurization
|
story-gen-book-event4 = a sudden depressurization
|
||||||
story-gen-book-event5 = a blackout
|
story-gen-book-event5 = a blackout
|
||||||
story-gen-book-event6 = the starvation of the protagonists
|
story-gen-book-event6 = the protagonists nearly starving
|
||||||
story-gen-book-event7 = a wasting illness
|
story-gen-book-event7 = a wasting illness
|
||||||
story-gen-book-event8 = love at first sight
|
story-gen-book-event8 = love at first sight
|
||||||
story-gen-book-event9 = a rush of inspiration
|
story-gen-book-event9 = a rush of inspiration
|
||||||
story-gen-book-event10 = the occurrence of some mystical phenomena
|
story-gen-book-event10 = some mystical phenomena
|
||||||
story-gen-book-event11 = divine intervention
|
story-gen-book-event11 = divine intervention
|
||||||
story-gen-book-event12 = the characters' own selfish motives
|
story-gen-book-event12 = the characters' own selfish motives
|
||||||
story-gen-book-event13 = an unforeseen deception
|
story-gen-book-event13 = an unforeseen deception
|
||||||
story-gen-book-event14 = the resurrection of one of these characters from the dead
|
story-gen-book-event14 = the resurrection of one of the characters from the dead
|
||||||
story-gen-book-event15 = the terrible torture of the protagonist
|
story-gen-book-event15 = the brutal torture of the protagonists
|
||||||
story-gen-book-event16 = the inadvertent loosing of a gravitational singularity
|
story-gen-book-event16 = the inadvertent loosing of a gravitational singularity
|
||||||
story-gen-book-event17 = a psychic prediction of future events
|
story-gen-book-event17 = a psychic prediction of future events
|
||||||
story-gen-book-event18 = an antimatter explosion
|
story-gen-book-event18 = an antimatter explosion
|
||||||
@@ -145,20 +145,20 @@ story-gen-book-event22 = having a quarrel with a close friend
|
|||||||
story-gen-book-event23 = the sudden loss of their home in a fiery blaze
|
story-gen-book-event23 = the sudden loss of their home in a fiery blaze
|
||||||
story-gen-book-event24 = the loss of a PDA
|
story-gen-book-event24 = the loss of a PDA
|
||||||
|
|
||||||
story-gen-book-action1 = share in a kiss with a
|
story-gen-book-action1 = share in a kiss with
|
||||||
story-gen-book-action2 = strangle to death a
|
story-gen-book-action2 = strangle
|
||||||
story-gen-book-action3 = manage to blow apart a
|
story-gen-book-action3 = blow apart
|
||||||
story-gen-book-action4 = manage to win a game of chess against a
|
story-gen-book-action4 = win a game of chess against
|
||||||
story-gen-book-action5 = narrowly lose a game of chess against a
|
story-gen-book-action5 = lose a game of chess against
|
||||||
story-gen-book-action6 = reveal the hidden secrets of a
|
story-gen-book-action6 = reveal the hidden secrets of
|
||||||
story-gen-book-action7 = manipulate a
|
story-gen-book-action7 = manipulate
|
||||||
story-gen-book-action8 = sacrifice upon an altar a
|
story-gen-book-action8 = sacrifice a hamster to
|
||||||
story-gen-book-action9 = attend the wedding of a
|
story-gen-book-action9 = infiltrate the wedding of
|
||||||
story-gen-book-action10 = join forces to defeat their common enemy, a
|
story-gen-book-action10 = join forces to defeat their common enemy,
|
||||||
story-gen-book-action11 = are forced to work together to escape a
|
story-gen-book-action11 = are forced to work together to escape
|
||||||
story-gen-book-action12 = give a valuable gift to
|
story-gen-book-action12 = give a valuable gift to
|
||||||
|
|
||||||
story-gen-book-action-trait1 = terribly
|
story-gen-book-action-trait1 = clumsily
|
||||||
story-gen-book-action-trait2 = disgustingly
|
story-gen-book-action-trait2 = disgustingly
|
||||||
story-gen-book-action-trait3 = marvelously
|
story-gen-book-action-trait3 = marvelously
|
||||||
story-gen-book-action-trait4 = nicely
|
story-gen-book-action-trait4 = nicely
|
||||||
@@ -192,7 +192,7 @@ story-gen-book-location17 = standing too close to an anomaly
|
|||||||
story-gen-book-location18 = while huddling on the evacuation shuttle
|
story-gen-book-location18 = while huddling on the evacuation shuttle
|
||||||
story-gen-book-location19 = standing in freshly fallen snow
|
story-gen-book-location19 = standing in freshly fallen snow
|
||||||
story-gen-book-location20 = lost in the woods
|
story-gen-book-location20 = lost in the woods
|
||||||
story-gen-book-location21 = iin the harsh desert
|
story-gen-book-location21 = in the harsh desert
|
||||||
story-gen-book-location22 = worrying about their social media networks
|
story-gen-book-location22 = worrying about their social media networks
|
||||||
story-gen-book-location23 = atop of a mountain
|
story-gen-book-location23 = atop of a mountain
|
||||||
story-gen-book-location24 = while driving a car
|
story-gen-book-location24 = while driving a car
|
||||||
@@ -207,15 +207,15 @@ story-gen-book-location32 = while trapped in a shadow dimension
|
|||||||
story-gen-book-location33 = while trying to escape a destroyed space station
|
story-gen-book-location33 = while trying to escape a destroyed space station
|
||||||
story-gen-book-location34 = while sandwiched between a Tesla ball and a gravitational singularity
|
story-gen-book-location34 = while sandwiched between a Tesla ball and a gravitational singularity
|
||||||
|
|
||||||
story-gen-book-element1 = The plot
|
story-gen-book-element1 = plot
|
||||||
story-gen-book-element2 = The twist
|
story-gen-book-element2 = twist
|
||||||
story-gen-book-element3 = The climax
|
story-gen-book-element3 = climax
|
||||||
story-gen-book-element4 = The final act
|
story-gen-book-element4 = final act
|
||||||
story-gen-book-element5 = The ending
|
story-gen-book-element5 = ending
|
||||||
story-gen-book-element6 = The moral of the story
|
story-gen-book-element6 = moral of the story
|
||||||
story-gen-book-element7 = The theme of this work
|
story-gen-book-element7 = theme of this work
|
||||||
story-gen-book-element8 = The literary style
|
story-gen-book-element8 = literary style
|
||||||
story-gen-book-element9 = The illustrations
|
story-gen-book-element9 = artwork
|
||||||
|
|
||||||
story-gen-book-element-trait1 = terrifying
|
story-gen-book-element-trait1 = terrifying
|
||||||
story-gen-book-element-trait2 = disgusting
|
story-gen-book-element-trait2 = disgusting
|
||||||
|
|||||||
4
Resources/Locale/en-US/storygen/story-template.ftl
Normal file
4
Resources/Locale/en-US/storygen/story-template.ftl
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
story-template-generic =
|
||||||
|
This is { INDEFINITE($bookGenre) } {$bookGenre} about { INDEFINITE($char1Adj) } {$char1Adj} {$char1Type} and { INDEFINITE($char2Adj) } {$char2Adj} {$char2Type}. Due to {$event}, they {$actionTrait} {$action} { INDEFINITE($char3Type) } {$char3Type} {$location}.
|
||||||
|
|
||||||
|
The {$element} is {$elementTrait}.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_type
|
id: BookTypes
|
||||||
values:
|
values:
|
||||||
- story-gen-book-type1
|
- story-gen-book-type1
|
||||||
- story-gen-book-type2
|
- story-gen-book-type2
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
- story-gen-book-type12
|
- story-gen-book-type12
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_genre
|
id: BookGenres
|
||||||
values:
|
values:
|
||||||
- story-gen-book-genre1
|
- story-gen-book-genre1
|
||||||
- story-gen-book-genre2
|
- story-gen-book-genre2
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
- story-gen-book-genre14
|
- story-gen-book-genre14
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_hint_appearance
|
id: BookHintAppearances
|
||||||
values:
|
values:
|
||||||
- story-gen-book-appearance1
|
- story-gen-book-appearance1
|
||||||
- story-gen-book-appearance2
|
- story-gen-book-appearance2
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
- story-gen-book-appearance27
|
- story-gen-book-appearance27
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_character
|
id: BookCharacters
|
||||||
values:
|
values:
|
||||||
- story-gen-book-character1
|
- story-gen-book-character1
|
||||||
- story-gen-book-character2
|
- story-gen-book-character2
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
- story-gen-book-character40
|
- story-gen-book-character40
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_character_trait
|
id: BookCharacterTraits
|
||||||
values:
|
values:
|
||||||
- story-gen-book-character-trait1
|
- story-gen-book-character-trait1
|
||||||
- story-gen-book-character-trait2
|
- story-gen-book-character-trait2
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
|
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_event
|
id: BookEvents
|
||||||
values:
|
values:
|
||||||
- story-gen-book-event1
|
- story-gen-book-event1
|
||||||
- story-gen-book-event2
|
- story-gen-book-event2
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
- story-gen-book-event24
|
- story-gen-book-event24
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_action
|
id: BookActions
|
||||||
values:
|
values:
|
||||||
- story-gen-book-action1
|
- story-gen-book-action1
|
||||||
- story-gen-book-action2
|
- story-gen-book-action2
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
- story-gen-book-action12
|
- story-gen-book-action12
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_action_trait
|
id: BookActionTraits
|
||||||
values:
|
values:
|
||||||
- story-gen-book-action-trait1
|
- story-gen-book-action-trait1
|
||||||
- story-gen-book-action-trait2
|
- story-gen-book-action-trait2
|
||||||
@@ -198,7 +198,7 @@
|
|||||||
- story-gen-book-action-trait13
|
- story-gen-book-action-trait13
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_location
|
id: BookLocations
|
||||||
values:
|
values:
|
||||||
- story-gen-book-location1
|
- story-gen-book-location1
|
||||||
- story-gen-book-location2
|
- story-gen-book-location2
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
- story-gen-book-location34
|
- story-gen-book-location34
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_story_element
|
id: BookStoryElements
|
||||||
values:
|
values:
|
||||||
- story-gen-book-element1
|
- story-gen-book-element1
|
||||||
- story-gen-book-element2
|
- story-gen-book-element2
|
||||||
@@ -249,7 +249,7 @@
|
|||||||
- story-gen-book-element9
|
- story-gen-book-element9
|
||||||
|
|
||||||
- type: dataset
|
- type: dataset
|
||||||
id: book_story_element_trait
|
id: BookStoryElementTraits
|
||||||
values:
|
values:
|
||||||
- story-gen-book-element-trait1
|
- story-gen-book-element-trait1
|
||||||
- story-gen-book-element-trait2
|
- story-gen-book-element-trait2
|
||||||
|
|||||||
@@ -361,8 +361,8 @@
|
|||||||
components:
|
components:
|
||||||
- type: RandomMetadata
|
- type: RandomMetadata
|
||||||
nameSegments:
|
nameSegments:
|
||||||
- book_hint_appearance
|
- BookHintAppearances
|
||||||
- book_type
|
- BookTypes
|
||||||
- type: RandomSprite
|
- type: RandomSprite
|
||||||
available:
|
available:
|
||||||
- cover:
|
- cover:
|
||||||
@@ -423,33 +423,7 @@
|
|||||||
suffix: random visual, random story
|
suffix: random visual, random story
|
||||||
components:
|
components:
|
||||||
- type: PaperRandomStory
|
- type: PaperRandomStory
|
||||||
storySegments:
|
template: GenericStory
|
||||||
- "This is a "
|
|
||||||
- book_genre
|
|
||||||
- " about a "
|
|
||||||
- book_character_trait
|
|
||||||
- " "
|
|
||||||
- book_character
|
|
||||||
- " and "
|
|
||||||
- book_character_trait
|
|
||||||
- " "
|
|
||||||
- book_character
|
|
||||||
- ". Due to "
|
|
||||||
- book_event
|
|
||||||
- ", they "
|
|
||||||
- book_action_trait
|
|
||||||
- " "
|
|
||||||
- book_action
|
|
||||||
- " "
|
|
||||||
- book_character
|
|
||||||
- " "
|
|
||||||
- book_location
|
|
||||||
- ". \n\n"
|
|
||||||
- book_story_element
|
|
||||||
- " is "
|
|
||||||
- book_story_element_trait
|
|
||||||
- "."
|
|
||||||
storySeparator: ""
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BookBase
|
parent: BookBase
|
||||||
|
|||||||
16
Resources/Prototypes/StoryGen/story-templates.yml
Normal file
16
Resources/Prototypes/StoryGen/story-templates.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
- type: storyTemplate
|
||||||
|
id: GenericStory
|
||||||
|
locId: story-template-generic
|
||||||
|
variables:
|
||||||
|
bookGenre: BookGenres
|
||||||
|
char1Type: BookCharacters
|
||||||
|
char1Adj: BookCharacterTraits
|
||||||
|
char2Type: BookCharacters
|
||||||
|
char2Adj: BookCharacterTraits
|
||||||
|
event: BookEvents
|
||||||
|
action: BookActions
|
||||||
|
actionTrait: BookActionTraits
|
||||||
|
char3Type: BookCharacters
|
||||||
|
location: BookLocations
|
||||||
|
element: BookStoryElements
|
||||||
|
elementTrait: BookStoryElementTraits
|
||||||
Reference in New Issue
Block a user