#Yarn Spinner

377 messages Β· Page 1 of 1 (latest)

forest briar
remote spruce
narrow fox
#

πŸ‘‹ (Hello! I'm the lead developer on the main Yarn Spinner project, which this crate adds Rust support for!)

hollow vale
#

Hiyo, I'm curious if we ought consider the lack of a closing === a compiler error? It's bitten me a few times forgetting to close the node. This specifically:

title: Test
---
// ...
-> Go to Foo
    <<jump Foo>>
-> Go to Bar
    <<jump Bar>>

// The missing === here isn't an error.
// But with this setup, we won't jump to Foo if the user selects to jump to it.

title: Foo
---
This is Foo
===

title: Bar
---
This is Bar
===
hollow vale
#

so confused lol, shouldn't it already disallow it? This is the current grammar

node
    : header+  BODY_START  body BODY_END
    ;

header 
    : header_key=ID HEADER_DELIMITER  header_value=REST_OF_LINE?
    ;

If the conditionals (go to foo/bar) are being considered the body shouldn't it disallow header+ until it sees a BODY_END?

narrow fox
#

Yes, this is a syntax error in the mainline Yarn Spinner compiler.

#

That is, failing to end your last node will result in a syntax error. The reason why you're not getting a syntax error in your example is because the lack of a === at the end of your Test node is causing the lines starting at title: Foo to be interpreted as lines of dialogue.

#

This is a side effect of a Yarn Spinner design decision - anything that isn't a control symbol like -> or === is treated as a line of dialogue.

hollow vale
#

Oh, interesting! Yeah that makes sense. I'm curious why it doesn't fail on seeing two --- in a row though as those are control symbols no? I'm guessing because after the first --- (as you mentioned) everything afterward is just a line

remote spruce
#

A quick way to test these things is to use https://try.yarnspinner.dev. If you use --- twice, you’ll see that the second one is indeed just printed out to the player πŸ™‚

remote spruce
# hollow vale Hiyo, I'm curious if we ought consider the lack of a closing `===` a compiler er...

That one would be suuper hard to parse if we didn’t have an ===. You already know about title: Foo, but the header can actually hold arbitrary metadata, like scene: Sunny or mood: Somber. It would be hard to decide whether these are metadata for your second node or still characters talking in your first node! Sure, you could use the whitespace, but introducing more significant whitespace is a tricky design decision because it ends up having many consequences across the grammar

hollow vale
remote spruce
hollow vale
#

For sure ! Will have something soon for you πŸ™‚

remote spruce
#

I'm moving some questions here for discoverability
@honest mango asked:

I've got a question, is there a way to add dialog at runtime?

#

My answer:

Depends on what you want. What you cannot do is load your files and then add more later. Instead, if you've got hot reloading enabled, you can change existing files at runtime and the dialog will be updated accordingly. If you want to load all files at some late point during runtime (because you need to fetch them first, for example), you can load them in deferred mode, see this chapter in the book. If you want to dynamically change some lines, you can interpolate a variable in the Yarn file and then change the variable in the Rust code through the variable storage.

#

Another good question by @hollow vale from a while ago:

When you extend a MemoryVariableStorage with variables why are you still be required to declare them in yarn file? Is that by design?
Answer:
The variable thing is taken from Yarn Spinner's design. It's a consequence of the fact that the Yarn linter and compiler know nothing about the underlying implementation and just look at the files themselves.
Follow-up:
Hmm, so I'm not really bothered about the linter complaining with squigglies but more so at runtime getting the error. So, say I've extended it with a $playerName and given it a yarn value, if I don't have a declaration in the yarn I'll still get:

called Result::unwrap() on an Err value: error: Can't figure out the type of variable $playerName given its context. Specify its type with a <<declare>> statement.
--> test.yarn:5:2
|
3 | ---
4 |
5 | {$playerName} walks into the vibrant Beachside Cafe, the salty sea breeze mingling with the aroma of fresh coffee.
| ^^^^^^^^^^
6 |
7 | Barista: Welcome to the Beachside Cafe! What can I get for you?
|

Is this behavior still expected?
Answer:
Yep, it is. The compiler doesn't know of any variable $playerName existing, so it also does not know any type for it. On the upside, the variable can be defined literally anywhere, even in a file you never execute! You can have a file called variables.yarn and simply fill it like this:
title: VariableDeclarations

<<declare $playerName = "UNKNOWN">>

if it is included in your compiled files, the variable will be visible to the compiler

honest mango
remote spruce
remote spruce
#

Of course you can take this to its logical extreme by having a node like this:

title: History
---
{ $history }
===

and then handling the whole dialog for the node in Rust via that variable

honest mango
#

thats what im likely going to do unfortunately

remote spruce
honest mango
#

yeah thats likely the route i will go

remote spruce
honest mango
#

is there a way to dynamically have options? other than conditionals?

#

like say I want the player to always have access to a growing list of topics, but also have options specific to this node

remote spruce
remote spruce
honest mango
narrow fox
#

You might find it useful to send content directly to your dialogue views when it’s dynamically generated, and let Yarn Spinner do its thing when the content is pre-authored

remote spruce
#

@narrow fox oh right, good idea! @honest mango you can send out one of these events to present whatever you want to the user πŸ™‚

hollow vale
#

Hey @remote spruce how would you feel about a PR to make VirtualMachine generic over VariableStorage and TextProvider rather than using the Boxs? (Really shaving the yak here as I just couldn't cast the dyn TextProvider to my own TextProvider lol). It should be a little more type safe than the dynamic dispatch but if those were there for a reason that lemme know. I've got tests passing but just haven't updated the bevy examples. It'd be breaking but user code would look a little simpler as you wouldn't have to Box either type before passing them in

remote spruce
# hollow vale Hey <@227474830470021130> how would you feel about a PR to make `VirtualMachine`...

If it is simpler, sure! I'm a bit weary about generics in APIs since I've made the experience that they quickly litter signatures with stuff like Foo<_, _>, but if this is not the case here (e.g. the Query for DialogueRunner doesn't need a generic param) I'm happy with it πŸ™‚
Another alternative would have been to expose an as_any methods, like here

#

If you open a PR I can take a look in about 2-3 days, is that fine?

hollow vale
#

Lol it definitely does end up polluting other stuff like DialogueRunner πŸ˜› I was just trying to make it now and it gets a little messy but not too bad haha. In other areas it actually simplifies the api a bit and looks relatively clean. Even if it doesn't workout I'll make the PR just to compare and contrast for posterity.

#

Most of the mess in the bevy plugin would go away if we went with a concrete type of storage and text_provider in the DialogueRunner or DialogueRunnerBuilder but then it's not really the same functionality of what was there previously

#

Also ya, short term I think I'll just PR to add the as_any method

remote spruce
remote spruce
hollow vale
#

πŸ‘

hollow vale
remote spruce
#

Thanks! Running CI for it now

remote spruce
hollow vale
#

Will do

hollow vale
#

Right, um because it's not callable on the trait itself but the type that implements that trait ig. Dunno, just took the easy way out and changed the wording

remote spruce
#

I predict that your example in the docs won't compile tho, lemme fix that

remote spruce
#

@hollow vale there ya go, published your changes πŸ™‚ Thanks for contributing!

hollow vale
#

Hmm, @remote spruce, so I'm trying to get the script variables in a log file for debugging purposes and the change I made in my recent pr I think didn't capture all edge cases.

Here's what I'm seeing with this dummy script:

title: IslandAdventure
---
<<declare $hasMap to false>>
<<declare $discoveredSecret to false>>

// ..
<<set $hasMap to true>>
===

Without PR I only got the variables (and their values) of those I passed into the Dialogue constructor ($playerName for example).

After the PR, I'm now seeing I have a key value for $hasMap:true but a missing $discoveredSecret. What instruction is responsible for declarations?

hollow vale
#

Aha, so <<declare ...>> statements aren't instructions. THey're actually held as initial_values in the program. Makes sense. Made a small PR to add them tho. MY pr does clone the program but only when someone adds/replaces a program. Lemme know if there's a way to remove it tho

remote spruce
#

I’ll check later πŸ™‚

hollow vale
#

I'm trying to run some the tests locally and can't get them to work. I think it's because /third-party is just an external symlink and for me it's just an empty dir. @remote spruce How do I pull that down so I can run all tests?

remote spruce
hollow vale
#

Thx!

hollow vale
#

I found something odd and I'm not sure if it's to do with the rust yarn spinner or yarn spinner in general. Say I have a custom command greet and I try to use it:

title: Example
---
<<set $greeting = "hi there!">>
<<greet {$greeting}>>
<<greet "hi there!">>
===

If i log the paramaters of the command I get different results:

command.parameters: [String("hi"), String("there!")]
command.parameters: [String("hi there!")]

I'd have thought if you used an interpreted variable like that you'd get only a single string. Right now it breaks it up on every space.

hollow vale
#

Well I found you can do <<greet "{$greeting}">> to get the expected output but that is still kinda ugly imo

remote spruce
#

Pretty sure that is just Yarn Spinner commands in general being weird. The parsing there is surprising in places πŸ˜…

hollow vale
#

yeah makes sense lol

acoustic stream
#

Is there a more minimal example somewhere for this crate? (Aside from the help dialog runner that is included)

hollow orchid
#

Is EventReader<ExecuteCommandEvent> the only way to execute commands where you need to know the source DialogueRunner's Entity?

hollow orchid
#

Also, I don't know if this is the place for this question, but yarn spinner plugin for VSCode keeps complaining about my custom commands not being defined, not sure how to fix it?

remote spruce
#

These are all very minimal πŸ™‚

remote spruce
remote spruce
#

@hollow orchid a command has full access to the ECS, so you can simple use a Query<Entity, With<DialogueRunner>> in it πŸ™‚

hollow orchid
#

I have multiple dialogue runners though, I can't identify which one issued the command

acoustic stream
remote spruce
#

Or are you asking about how to use it without Bevy at all?

remote spruce
hollow orchid
#

Multiple players, with their own dialogue boxes, but they are able to encounter the same dialogues individually. Think Baldur's Gate 3 or Stardew valley.

If I make them uniquely identifiable, such as adding a "player1" component, how do I make the yarn script know which to execute the command on?

acoustic stream
acoustic stream
remote spruce
remote spruce
hollow orchid
#

With custom commands, the yarnspinner plugin on vscode lists problems "Could not find command defintion for <command>", is there a workaround for this?

remote spruce
hollow orchid
#

Oh my goodness how embarrassing that I missed that XP thank you!

hollow orchid
#

I created a commands.ysls.json file, and created a definition for "open_shop" command, but the problem is still there. Is there something I need to do for the linter to pick up the file?

hollow orchid
austere seal
#

Hey so I do have a question, and I might be doing something completely stupid, but I have some text that I would like to display using the example display -- but not actually tied to a Yarn Spinner file. I've gotten it to display but clicking and the like will not advance the text/make it go away.

I'm assuming the example dialogue viewer has some assumptions about its use, that causes it to just not work for this case. But wanted to ask and see if what I'm doing should be theoretically possible regardless.

remote spruce
#

Today I've published version 0.3.0-rc of all relevant yarnspinner crates onto crates.io.
On the Bevy side, you can now use the newest Bevy release candidate versioned 0.14.0-rc.2.
On the non-Bevy side, there's plenty of nice QOL improvements and fixes, most of which come from @Rexma. Thanks! ❀️

I didn't draft full release notes yet, but there shouldn't be anything breaking in there. At least, the stuff that is technically breaking is not something I would have expected anyone to depend on.
For most, this should be a drop-in upgrade. Ping me if you have any issues πŸ™‚

remote spruce
austere seal
#

More or less arbitrary stuff. (Really just a line of text or two)

remote spruce
austere seal
#

Fair enough, FWIW it mostly works. The only issue is...I can't interact with the display. So it just...remains on screen and nothing will remove it.

remote spruce
#

So you mean you can advance the text, but then it never quits?

austere seal
#

I mean it's only one line and nothing comes afterwards. So the text shows up and appears properly...but doesn't go away.

remote spruce
#

Since we export UiRootNode, you can hide its visibility manually if you want.

#

Out of curiosity, why are you using the dialogue view this way? Is it because you'd like to have a UI in general for text that prints stuff letter-by-letter?

#

For what it's worth, the source for the whole dialogue view is not that big.
Feel free to fork or copy-paste whatever you want if you need to hack your own version πŸ™‚

austere seal
remote spruce
#

Aah I see πŸ˜„

austere seal
#

So I guess the best way would be to put the signs in yarn spinner, or find a way to template it so I can just have the sign replace the text when it's interacted with. I could also probably try and fork the dialogue_view, but I would need to come up with an alternative to handle the sign text in that area that the very least...

#

In any case, I figured I might've been doing something slightly cursed and 'out of spec' as it were, which is why I asked.

#

Thanks for the help.

remote spruce
remote spruce
austere seal
remote spruce
#

A yarn file is composed of multiple nodes. When you compile multiple yarn files, the result is the same as if you placed all nodes in a single big yarn file. So yeah, you only need one file. Everything else is just personal organizing πŸ™‚

#

Btw, don’t know if you’ve seen this link, but https://try.yarnspinner.dev is very nice for prototyping how your yarn file will work.

Try Yarn Spinner, the friendly dialogue tool for games, in your browser.

austere seal
#

I guess I meant multiple nodes then

austere seal
#

I ended up using a single node/file for signs, with variables to set the content.

hollow orchid
#

I literally just checked this morning to see if this was updated and was happy to see it was. Thank you!

austere seal
#

Alright so a question.

In some dialogue I need to check if the user has a specific item in their inventory. The inventory is a Bevy Component. So I would probably need to use a YarnSpinner Command or a Function to do this.

Looking at the documentation Commands are regular bevy systems, so they can just be used to get the inventory. However they can't return a value to YarnSpinner. Where-as Functions can't access any of the game world, but can return values to YarnSpinner.

What's the recommended way of doing something like this? ATM the only way I can really see this working is to use a command and set a variable in there.

remote spruce
# austere seal Alright so a question. In some dialogue I need to check if the user has a spec...

This is a limitation adopted from the Unity version that I have thought about removing before because I find it a bit annoying. For the moment, there are the following solutions:

  • Store your state in a global mutex that you can access from both a Yarn function and Bevy code (this is what I currently do for my own quest system)
  • Set a variable like $has_foo by accessing the underlying default memory storage in a Bevy system and check it from Yarn
  • Implement your own memory storage that is always synced to Bevy in the first place
  • Use an own node to encapsulate what should happen when you have the item and start that one dynamically from the dialogue runner

@narrow fox how do you usually handle this? I think accessing "engine stuff" from functions or having return values from commands is not planned for V3, is it?

austere seal
#

I ended up doing the second option, where I take in the variable as an argument and then change it. However, there are a few oddities that can occur it seems.

I also had to use Arc<AtomicBool> as the return type to get it to work properly, among some other things. (These are able to be dealt with, just mildly annoying.

#

Doing 3 or 4 is...probably the better option tbqh, but given the nature of the game I'm working on, option 2 works well enough for now.

remote spruce
remote spruce
austere seal
#

I'll probably need to do 3 anyway at some point, for save files and stuff. But for rn, I'm still in the 'early development' phase of things.

remote spruce
#

Let me know when there’s anything to show, I love to see what people do with the library πŸ™‚

austere seal
#

Sure thing! I'm definitely doing...a lot of cursed stuff with it as well given I'm using it entirely for all of my in-game UI atm ^^ (which granted, is mostly just debug and testing stuff) .

#

FWIW I do quite like Yarn Spinner.

My only 'complaint' is that I don't have Syntax Highlighting in RustRover πŸ˜›

There are a few rough edges, but given that I can work around them somewhat easily enough...

#

(Also the Bevy Plugin is magic and I love it.)

remote spruce
remote spruce
austere seal
#

That's...actually not that far off of what I'm doing tbqh

#

in terms of what they did...not the type of game lol

#

Although I also have inventory being displayed through the text as well and I don't really have a lot of stuff to test.

austere seal
#

Hey I keep getting an error when trying to use the latest YarnSpinner with Bevy 0.14:

thread 'IO Task Pool (0)' panicked at /home/sapeint/.cargo/registry/src/index.crates.io-6f17d22bba15001f/antlr-rust-0.3.0-beta/src/tree.rs:385:9:
internal error: entered unreachable code: should have been properly implemented by generated context when reachable

Not sure if it's something with me, I'm currently trying to double check, but.

remote spruce
#

Do you have a minimal reproducible example?

austere seal
#

Give me a moment.

#

Using the following Yarn file causes it:

title: Start
---
<<declare $name = "">>
RΓΆck.exe: This is a test
-> Samarino <<if $name is "Sam">>
-> Bobino <<if $name is "Bob">>
-> Pepino
===
#

It's specifically the "option if", with the if statement after the option. This works properly in the prior version.

#

@remote spruce hope that helps.

remote spruce
#

Yeah that should definitely work in theory. Since this is the parser crashing, let's try changing it a bit. What happens when you do == instead of is?

#

Also try using set instead of declare

austere seal
#

All of those still cause the same issue.

#

(using set instead of declare, and using == instead of is both individually and together)

remote spruce
# austere seal All of those still cause the same issue.

Bummer. I'll look into it tomorrow, but given how arcane and esoteric the antlr_rust codebase is, I don't think there's a good fix here other than finding a workaround.
I know that the -> Option <<if something>> syntax generally works, e.g. in this snippet of my own project:

-> Dig up the grave <<if is_quest_at_least_at("DigUpGrave", "Accepted")>>
    There's a skeleton requesting payment for the grave digging service.
    The skeleton is asking for 50 coins.
    <<if can_afford(50)>>
    It seems you have enough money to pay the skeleton.
    -> Pay the skeleton <<if can_afford(50)>>
        <<wallet -50 "Grave digging service">>
        You pay the skeleton for its service.
        <<set $grave_generic2_robbed = true>>
    -> Refuse to pay
        The skeleton is not happy with your decision.
    <<else>>
    It seems you don't have enough money to pay the skeleton.
    <<endif>>
-> Leave the grave alone

So there must be something we can do. Does it work if you use a function call inside the if instead of directly comparing the variable?

austere seal
#

Alright so I was messing around a bit more, and realized my minimal doesn't exactly work. However I figured out what the issue is:

title: Start
---
<<declare $name = "">>
RΓΆck.exe: This is a test
-> Samarino <<if $name == "Sam">>
    <<set $name = "Sam">
    <<jump Start>>
-> Bobino <<if $name == "Bob">>
    Bob!
-> Pepino
    Pep!
===
#

This reproduces the error. The other ones seemed to work.
Notice what's wrong?

#

I forgot a > in the Sam line, and then it's followed immediately by a jump.

This error seems to occur if and only if the following conditions are met:

  1. There is a branch (if-after-option or just if)

  2. There is a command/operation within the branch that contains a syntax error.

  3. The next line is a command/operation.

#

If any of these conditions are false, you get a yarn syntax error.

#

To show:

title: Start
---
<<declare $name = "">>
Norm!
<<if $name != "">>
<<set $potato = true>
<<set $name = "Norm">>
<<endif>>
===
title: Start
---
<<declare $name = "">>
Norm!
<<if $name != "">>
<<set $potato = true>
A
<<set $name = "Norm">>
<<endif>>
===
#

The first will produce the error, the second will produce an error that gives a bit more information.

#
 --> Minimal.yarn:8:1
  |
8 | <<set $name = "Norm">>
  | 
9 | ^
  |

error: mismatched input '<' expecting '('
 --> Minimal.yarn:8:1
  |
8 | <<set $name = "Norm">>
  | 
9 | ^
  |

error: extraneous input '<' expecting {'true', 'false', 'null', OPERATOR_LOGICAL_NOT, '-', '(', STRING, FUNC_ID, VAR_ID, NUMBER}
 --> Minimal.yarn:8:2
  |
8 | <<set $name = "Norm">>
  |  
9 |  ^
  |

error: missing '(' at '$name'
 --> Minimal.yarn:8:7
  |
8 | <<set $name = "Norm">>
  |       ^^^^
9 |       ^^^^^
  |

error: Unexpected "=" while reading a function call
 --> Minimal.yarn:8:13
  |
8 | <<set $name = "Norm">>
  |             
9 |             ^
  |

error: mismatched input '=' expecting {')', ','}
 --> Minimal.yarn:8:13
  |
8 | <<set $name = "Norm">>
  |             
9 |             ^
  |

remote spruce
austere seal
#

That's also probably why I didn't notice it. It also makes the antlr crash a bit more...understandable, but still..

Not sure what changed between the prior version and that to cause the issue though.

remote spruce
#

@austere seal Oh wait, I just realized it's you from the template group πŸ˜„

austere seal
#

XD

remote spruce
#

hehe

austere seal
#

That compartmentalization πŸ˜›

#

But yes it's me!

austere seal
#

In any case, glad the issue was figured out. Although it is a bit annoying.

ember yew
#

Hello there, after playing with yarn spinner, I like it a lot - is there a way to color a speakers name from within the .yarn file?

real flax
#

Is yarn spinner broken in Bevy 0.15? I'm getting trait issues when adding it as a plugin, DialogueRunner is not a Component, single exists for struct Query<&DialogueRunner>, but its trait bounds were not satisifed, etc

#

Repository seems inactive for the last four months, so I'm on the latest version

austere seal
#

I mean YarnSpinner is on bevy 0.14 atm, from what I can tell. So I wouldn't expect it to entirely work in Bevy 0.15 without some changes.

real flax
#

I might as well check it out and see if I can do anything

real flax
austere seal
#

@real flax just as a heads up with your PR, bevy_sprite3d has updated to Bevy 0.15 as of 8 hours ago.

real flax
#

o! Will update

austere seal
ember yew
#

@real flax I just tested your branch, my game drops to 13 FPS when inserting the plugin and doing nothing with it

#

I don't have a dialogue viewer yet, though

real flax
#

yeah i saw that

#

i wonder if you can get a frame graph with bevy or something?

#

i know i had to add multiple bevy features but i don't remember anything else that would have done this

real flax
#

@hollow orchid I think you commented on the PR, did you experience any changes in performance with my branch?

#

Also, how are you initialising YarnSpinnerPlugin, and with what source?

hollow orchid
#

I didn't notice any, but that doesn't mean much: It's a TUI game and 20 FPS is basically my target with it anyways XP

real flax
#

Ah I see

#

My game is also really simple and doesn't have any continuous movement, so I don't have a good example

real flax
#

On second thought, this person Tig0r said they notice the framerate drop even with nothing but YarnSpinnerPlugin::new(), so it wouldn't matter how you use it... I'm trying a profiler on my own project

ember yew
real flax
#

Lmao don't know what happened with "this person tig0r" sorry

#

Will investigate

ember yew
hollow orchid
#

(BTW, even with the framerate drop, your branch unblocked me from migrating my code to 0.15 so thank you)

ember yew
#

man, profiling is hard

ember yew
#

@real flax can you send me your flamegraphs?

real flax
#

this is 0.14

#

this is 0.15

#

i played the demo until clippy and ferris yell huzzah together (or whatever the line is, i know that's wrong)

real flax
#

@ember yew i fixed an issue in the example dialogue view, but you're not using it correct?

real flax
#

you aren't using the example dialogue view crate, right?

ember yew
real flax
ember yew
#

but I'm at a loss, I think I just have to work in release mode, where there seems to be no performance issue

#

or we add traces everywhere to try with tracy or trace_chrome

#

when I added iyes_perf_ui it wouldn't even show me the entity count, I think I'm woefully unprepared for this xD

#

there is some ui frame rounding that is apparently costing performance

remote spruce
#

Sorry for the long absence, was in a tough research phase for my thesis. Should be way better now, as I'm writing my thesis using Bevy πŸ˜„
I published versions of yarnspinner for 0.15 and 0.16 RC recently. Thanks a bunch @real flax and @ember yew for your help!

austere seal
#

Woo!

hollow orchid
#

Glad to see that's not an April Fool's joke ;]

remote spruce
remote spruce
#

Hey Yarn Spinners, over on the Yarn Spinner Discord, @tardy mirage has been cooking up a grand new feature: Yarn Spinner for Rust can now accept Bevy systems as functions! This means you can easily query some state through the ECS in functions now:

fn get_player_money(wallet: Single<Wallet, With<Player>>) -> usize {
  wallet.available_money()
}

which is registered with

dialogue_runner
  .library_mut()
  .add_function("get_player_money", commands.register_system(get_player_money));

and used like

<<if get_player_money() < 3>>
Shopkeeper: Sorry, you don't have enough cash on hand
<<else>>
Shopkeeper: Pleasure doing business with you!
<<subtract_money 3>>
<<endif>>
===
#

Will highlight this again when I do the next release, but this is a feature that I've personally been really excited about πŸ˜„

tardy mirage
remote spruce
#

I think World handles all of that, so it should work the exact same way as it does for regular systems

#

not sure though

tardy mirage
#

But there will be no return value to return

remote spruce
#

So yeah, panic in the current version, warn in 0.16

remote spruce
#

sounds like panic then

#

😬

#

Didn't consider that edge case before merging ,haha

tardy mirage
#

Yeah, it'll definitely panic. That's on me for not taking that unwrap seriously.

remote spruce
tardy mirage
#

(sorry to turn your announcement into a bug)

remote spruce
#

All good, I don't think many people will read this here anyways πŸ™‚ Real announcement is going to be in #crates

#

I think I'll do the release when 0.16 lands, as that should be in mere days

tardy mirage
remote spruce
#

And "just instantly complete the dialogue" doesn't sound right either

#

Although I guess that may be alright

#

Then the player can at least save their game before a panic?

#

But then again, it's on the developer to use Option<Single<..>> in this case to prevent a panic

#

But then again again, Single won't panic by default in 0.16, so we have slightly different semantics

tardy mirage
#

The runtime already panics on missing functions. This is sort of that same case. An unresolvable function.

remote spruce
#

(I'm really tired, haha)

tardy mirage
# remote spruce You feel like fixing this?

Sure. I think I'm inclined to keep it as a panic since that's consistent with the runtime's other behavior. Maybe we should overhaul error handling generally, but that's a separate question. I'll see what the error message currently looks like. The unwrap will just spit out bevy's internal error which might actually be fine, but it's probably worth adding some additional context like the function's name in yarn.

remote spruce
austere seal
#

Woo!

#

This is honestly a really awesome feature, and actually makes things...quiet a bit nicer in some areas.

remote spruce
#

New release! #crates message

shrewd breach
#

New to using yarnspinner, but why are the yarn files all dumped into one big resource? It seems like each yarn file could just be an asset that you load?

remote spruce
#

This is why they are also compiled in the context of each other

#

While the YarnProject does keep handles to the individual files, its actual contexts are the result of compiling all of these files.

shrewd breach
#

Cross references in the sense of jumps to other nodes?

#

Or is there more involved?

#

Maybe it checks that defines of variables are consistent?

#

If it's just the former, shaders have a solution to this that might be if interest

remote spruce
remote spruce
#

(IIRC, been literal years since I ported that part)

remote spruce
#

You can also register custom diagnostics that run on the whole compiled shebang

#

don't know if anyone ever did that, but the consistent type stuff is implemented like that

#

you could also e.g. write a diagnostic that verifies that all characters are in the list of ["Narrator", "Evil Twin", "Monster Whale"]

shrewd breach
# remote spruce could you elaborate?

Shaders can specify their module name inside the asset. Then any other shader can reference that module name and at runtime the two will be glued together. It does this by just listening for shader add events, then looking into the asset to see if it has a module name, and storing that relationship in a hashmap resource

shrewd breach
remote spruce
#

Yeah, that makes sense. That would probably be quite the clean solution πŸ€”

#

The current approach is just a 1:1 port of the C# functionality

shrewd breach
#

Fair enough! And it's already quite functional!

#

Maybe when I have some time I'll play around with a PR

remote spruce
austere seal
#

Honestly I'm just looking forward to yarnspinner getting nostd so I can get one step closer to making console games with yarnspinner πŸ˜›

remote spruce
#

hehehe

austere seal
#

(Particularly for the GBA, PS2, and maybe PSX)

#

Honestly if someone could get the PSX as a working target for Bevy, then I think Bevy would honestly be a good engine to make Haunted PSX games in.

remote spruce
#

@narrow fox this would be the first time any version of Yarn Spinner ran on any of those, right?

austere seal
#

I also have my eyes on a GBA game, but I'd need something like bevy_ecs_ldtk to support nostd as well.

austere seal
# remote spruce Is that far off?

I don't even think it's on the radar atm. It would also require bevy_ecs_tilemap to support nostd as well. I could probably just make my own solution for it, but I think being able to use ldtk for level design in bevy, while making a GBA game would be really nice.

remote spruce
narrow fox
open onyx
shrewd breach
#

Most quest systems work like this: you're chatting with an NPC. When you reach a certain point in the conversation, your journal adds the next step, something like "I need to find John's ring near the castle".

My dream quest system would be one where everything a character says is recorded in a journal. So instead of your journal being a to-do list, you can just reread the character dialogue to know what needs to be done (which also helps communicate the story if you forget).

#

Any ideas how to achieve something like this with yarnspinner? Is it even possible?

remote spruce
shrewd breach
remote spruce
#

When implementing a custom dialogue view, you get all the lines that should be presented to the screen

#

So you can just store those

shrewd breach
#

Ideally I'd like to avoid just copy-pasting the text into the save file, because that wouldn't handle changing languages, or updates to the dialogue.

remote spruce
#

that's fair

#

every line also gets a line ID generated by the compiler

shrewd breach
#

So I could record the line IDs and look them up somehow later

remote spruce
#

The dialogue runner has a TextProvider that you can access

#

That is automatically the correct translation

#

(by default)

shrewd breach
#

I've also been picturing maybe I could have the journal let you "relive" dialogue. You click a button and it lets you replay that node of the dialogue or something. I'd need to figure out which variables are relevant to that node though and save them for future reads

remote spruce
remote spruce
#

@shrewd breach for reference, implementing a custom dialogue view works by having an EventReader for these events here and handling them by doing UI and then callind things like continue_in_next_update on the dialogue runner

#

The example dialogue runner is quite big since I wanted to have fancy things like typewriter-styled text that is printed letter by letter

#

But that's just a UI detail

#

I believe the only things you need to do is call continue_in_next_update to advance the dialogue and select_option to tell the dialogue runner which option was selected

shrewd breach
#

Yeah totally makes sense! Do options also have IDs? And hopefully ones that aren't indices (since changing the list of options could result in picking the wrong path)

remote spruce
#

I didn't consider the case where you wanted to replay them

#

But setting them to something more robust should totally be doable

#

They have line IDs as well, since they can also be translated

shrewd breach
#

Yeah understandable, I'm definitely trying to push the envelope here πŸ˜›

remote spruce
#

So we can just accept those line IDs as identifiers too πŸ™‚

#

For the options IDs, you may even want to write those yourself.
Every line gets an autogenerated id at the end, like #line:98123.
But you can also do -> Come and get me! #line:fight-me and use that ID

shrewd breach
#

I'm also considering just allowing users to replay the dialogue entirely. I'd need to design these nodes in such a way that that makes sense, but it could be tractable!

remote spruce
shrewd breach
remote spruce
#

But your suggestion should also work, sure πŸ˜„

shrewd breach
#

Ohhhh I see!

#

Is this part of yarnspinner's syntax (the hash stuff)?

remote spruce
#

Every line can have metadata there, like

Lucy: I'm sorry it turned out like this #face:sad
shrewd breach
#

Oh nice!

remote spruce
#

that line ID is just metadata that is conveniently generated for you

shrewd breach
#

I think the yarnspinner docs could use a more complete description of its syntax hahaha

remote spruce
#

If you use the translation stuff, this line is what is used in your autogenerated CSV file

remote spruce
#

You can also do markup stuff

#
Shopkeeper: You mean to tell [b]me[/b] how to run [wiggly]my[/wiggly] business?!
#

There's nothing in place that actually translates this into Bevy UI, but you can access it in a dialogue view and do whatever with that info

shrewd breach
#

Does yarnspinner parse that markup and annotate the text for it? Or is this something your dialogue view needs to parse itself?

remote spruce
#
pub struct MarkupAttribute {
    pub name: String,
    pub position: usize,
    pub length: usize,
    pub properties: HashMap<String, MarkupValue>,
    pub source_position: usize,
}
#

So all information is right there πŸ™‚

shrewd breach
#

Thank you so much!

remote spruce
#

I was just wayyyy to lazy to implement anything that converts that into Bevy UI for the example dialogue view πŸ˜„

remote spruce
#

Opened this to track it

errant pasture
#

Hi @remote spruce! Reviving this thread, I have encountered a problem when trying to create a command. I'm pretty sure I'm running the print_addition example verbatim, but I get an error, perhaps this page is outdated? https://docs.yarnspinner.dev/yarn-spinner-for-other-engines/bevy/creating-commands-functions/creating-commands

Here's the error I get:

error[E0277]: the trait bound `fn(bevy::prelude::In<(f32, f32)>) {print_addition}: bevy_yarnspinner::prelude::YarnCommand<_>` is not satisfied
  --> src/yarn.rs:54:40
   |
54 |         .add_command("print_addition", print_addition)
   |          -----------                   ^^^^^^^^^^^^^^ the trait `bevy_yarnspinner::prelude::YarnCommand<_>` is not implemented for fn item `fn(bevy::prelude::In<(f32, f32)>) {print_addition}`
   |          |
   |          required by a bound introduced by this call
   |
   = help: the following other types implement trait `bevy_yarnspinner::prelude::YarnCommand<Marker>`:
             `SystemId<(), Output>` implements `bevy_yarnspinner::prelude::YarnCommand<((), Output)>`
             `SystemId<bevy::prelude::In<P>, Output>` implements `bevy_yarnspinner::prelude::YarnCommand<(P, Output)>`
note: required by a bound in `YarnCommands::add_command`
  --> /home/mario/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_yarnspinner-0.5.0/src/commands/command_registry.rs:58:12
   |
51 |     pub fn add_command<Marker, F>(
   |            ----------- required by a bound in this associated function
...
58 |         F: YarnCommand<Marker> + 'static + Clone,
   |            ^^^^^^^^^^^^^^^^^^^ required by this bound in `YarnCommands::add_command`
remote spruce
errant pasture
errant pasture
#

Hi again, do you know if it's possible to send bevy events from yarn commands? I assumed it was possible, but this doesn't seem to be sent, I'm not being able to read it.

fn pay_rent (
    In(amount):     In<i32>, // Getting it from here for now
    mut character:  Single<&mut character::PlayableCharacter>,
    mut duty_event: EventWriter<mind::responsibility::duty::DutyEvents>,
) {

    character.money = character.money - amount;

    let test = duty_event.write(
        mind::responsibility::duty::DutyEvents::DebtPaid(String::from("rent"))
    );

    println!("{:?}", test);
}

No errors or anything, that prints event<DutyEvents>#0

remote spruce
#

WIll investigate later

errant pasture
#

Thanks, that would be lovely! I hope it's not some dumb mistake on my part or something, but it compiles, the event is detected if I send it from some other place, etc.

errant pasture
#

Aaaand it was a dumb mistake! The receiving system wasn't running in the game was paused, and the yarn dialogue windows are pausing my game, so the event was lost. I can confirm that events work then, that's something. Sorry for the trouble!

remote spruce
#

happy you found it out!

#

Hope the crate is working out for you so far πŸ™‚

errant pasture
remote spruce
grim peak
#

We were thinking about using yarn spinner for dialogues in our game since it looks very neat ! But what I'm trying to wrap my head around is how to localize it ? I see that there are string files, yet we are already using fluent for UI and ingame locs, and I don't quite understand from my research if it's possible to use fluent with yarn spinner as well ? Is there a way to do locs in one format for dialog + UI + ingame descriptions ?

remote spruce
#

Yarn Spinner itself uses a CSV to do localization

#

With some goodies in there, thanks to ICU

#

But it's all pretty modular

grim peak
#

Hmm I wonder if I can substitute CSVs for fluent's bundles

remote spruce
#

It was just never pressing enough to look into it haha

#

but if you try, let me know if there's anything I can do to help πŸ™‚

grim peak
#

Ok, sounds promising. Thank you ! We will look into that for sure

remote spruce
#

the TextProvider is what serves the localized strings

#

and you can implement that trait yourself however you want

grim peak
#

Aha, yes, I was hoping that there is something like this that we can reimplement

remote spruce
#

You may need to check the implementation of StringsFileTextProvider for reference

remote spruce
grim peak
#

@remote spruce Hi ! In your example_dialogue_view, ExampleYarnSpinnerDialogueViewPlugin expect YarnSpinnerPlugin, but we would like to use the deferred plugin with our own asset loading. I didn't really look into it enough, but it seems that just using the deferred plugin in the example plugin doesn't work ?.. Do you think it's possible ? It's okay if not/time consuming, it's just nice to have a dialog view right away

remote spruce
#

Been a while πŸ™‚

grim peak
remote spruce
amber yew
#

Hello! I'm following the documentation to create my own VariableStorage, but the documentation speak of returning a VariableStorageError, which is not in the bevy_yarnspinner prelude. What is the proper way to include it in our code to implement the VariableStorage trait ?

remote spruce
#

for now, you can add a dependency to yarnspinner directly to get that type πŸ™‚