From the Burrow
Heya folks!
In preparation for creature modding feature shipping, we are making the tool available so you can try it out, and we can fix any egregious bugs you find.
You can find a guide on how to get the tool and how to use it over here:
https://talespire.com/taleweaverlite-guide
NOTE: You cannot load the mods into TaleSpire today. This is simply a build of the tooling used to make the mods.
What is this (and what is it not)
TaleSpire is going to have a couple of ways for you to add new creatures to the game:
- In-game creature creator: A way of building creatures out of a palette of configurable parts
- Mods: mod-files are composed of pre-made assets outside of TaleSpire
TaleWeaverLite is for the second approach. It is a very minimal tool for composing pre-made assets into a creature mod that is ready to be used by TaleSpire.
It is not a modeling or posing tool. It expects content in specific formats and with specific layouts.
The expected usage is that the mesh and textures are prepared in other tools, such as Blender, and using TaleWeaverLite is the final step in making a mod.
The good
Once you are producing meshes and textures in the correct format, it’s trivial to turn them into a mod. Simply drag them into the Unity project, set the fields in the creature panel, and save.
The meh
We’ve been using Unity for our asset tools for a long time. It handled processing assets for us and gave us a UI we could use to make tools.
This means that to use TaleWeaverLite, you need to install the free version of Unity.
We have mixed feelings about this, as packaging this stuff for public use has not been as easy as we had hoped. Your feedback will help us decide whether to stick with this in the long term or to start moving to a more standalone tool.
The future
We are busy working on the backend changes needed to support the new creatures. Once we have that done, we’ll wrap up the needed changes in TaleSpire and get ready to ship. We are pushing hard for this and hope we can get this in your hands very soon.
By getting TaleWeaverLite out now, there might even be some new creature mods ready to ship the moment that TaleSpire gets the feature!
Until then, thanks for stopping by.
Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team
TaleSpire Dev Log 453
Good evening all,
On a bit of a roll with dev-logs today. Today, I’ve been working on the translation file format.
The goal is for a translator to go into TaleSpire, choose a language, and click export to produce a file that has all the strings that need translation for that language.
As usual with human stuff, it’s never that simple.
If you like a more complete and entertaining version of this, check out this Tom Scott video. https://youtu.be/0j74jcxSunY
Let’s say you want to display: “{creature-name} has {some-number of} apples.”
In the case of one apple, we’d probably want to write “{creature-name} has 1 apple”, not “1 apples” so plurality is something we will need to care about.
Let’s say the number of apples is in parameter @1
. We could have some simple rules where the value is used to pick the correct text. For example:
{creature-name} has {@1=1:an apple|@1 apples}
.
This syntax could mean that if @1
equals 1, use an apple
. If not, use @1 apples
.
Seems like a good start, but many languages have more complicated systems[0]. Luckily, the Unicode Consortium has assembled a big ol’ table of pluralizing rules. Just look at that monster! This does help, though, as translation libraries can implement these rules to provide a tool for their translators.
Most language complications are not this readily solved, though. Translation tools generally just punt and let the translator handle the issues. Which, honestly, is the right move. You want a human translation, and many texts need significant restructuring to make them feel native.
The grammatical gender of words is another thing that will come up a lot. If you, like me, were raised in a language which didn’t have grammatial genders for nouns it can feel very confusing to hear that a city is female or a door is male or whatever. This is because we conflate it with sexual gender (at least, I certainly did), which it’s not. It’s quite simply the category of the noun[1] (often called a noun class IIRC). It frequently has some overlap with sexual gender, though, which can make things extra confusing[2]. So, let’s talk about these categories.
Just gotta shout out Worrorra, which also has “terrestrial,” “celestial,” and “collective” genders. How dope is that?!
Right, so in our “apples” example above, languages will translate the text differently based on the class of both “apples” and the creature. The word “apples” is hard-coded into the string so it can be handled there, but the creature-name is passed in from the game. What we can do is support passing values (like gender) to the translation library and let the translator pattern match those parameters. Of course, in all cases, the translator also has to provide a version of the text for the “unspecified” case. In the future, we’ll need to give you various options so that, if you want (and if it makes sense), you can specify the gender of your characters.
So that’s a little ramble about some complications with translations. At the end of the day, you just gotta give the translators decent information and let them work their magic.
I’ve written a small specification for our file format. It’ll allow for pattern matching on the input arguments and a few kinds of inline formatting tools within the translation string. Those formatters include:
- Simple list handling
- A plural form selector (following the rules from unicode)
- Picking the nth of some number of forms based on a parameter or conditional checks
I don’t need to implement all this tomorrow, though. My current goal is to get our (very wip) Norwegian translation working again and get this system merged into master. That translation doesn’t use any fancy features yet, so I can implement the rest another day.
Well, that’s all from me for now.
Seeya!
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
[0] And some languages, like Indonesian, have no differentiation! Which is awesome. This is the equivalent of “one sheep, many sheep” in English.
[1] Gender, in this case, derives from “genus,” meaning “kind,” which more clearly highlights that relation to “category.”
[2] In French, you get cases where you can end up referring to a rabbit using the masculine class, even if you know the rabbit is female.
TaleSpire Dev Log 450
On the last community stream, someone asked whether Bouncyrock gives us employees time to do our own projects. I said that we don’t have an official policy around it but are open to any of us requesting that. I’m not sure if it stuck in my head, but I realized having a few study days a month would be really helpful.
So, this month, I’ve started a habit I hope to keep up with going forward. I’ll take three work days for study towards the end of each month. Like most folks, I’ve got years of bookmarks and a bunch of books in the queue to read, and having dedicated time to get into things will be great.
I took my first one on Monday, and Thursday was my second. I’m unsure if any of this will interest you, but here we go.
I’ve started working through Projective Geometric Algebra Illuminated, a fascinating book by Dr. Eric Lengyel. I’m no mathematician, but my hope is that this will provide tools that unify a bunch of different mathematical concepts I’ve dealt with before. It seems to set things out in a way that extends naturally across and between various dimensions.
I’m slowly working through the first chapter, which revisits the classical approaches. I’ve reached quaternions and got a bit stuck doing the (admittedly simple) derivations of the multiplication rules. I bashed my head against it for a bit, and after a chat with Chairmander, I understood what I got wrong. Good progress, but by then my brain was a little cooked on the math front, so I decided to switch to something else.[0]
Next up, The Garbage Collection Handbook. This lovely tome is one I’ve started, but got distracted from, before. It’s not directly related to anything I’m making, but memory management is always a fundamental concern, and reading about all the approaches and trade-offs of such a practical problem is very cool. I happy whiled away some time reading the first chapter and am excited to jump into the second in my next session.
In the evening, I had a real treat. The font designer who is making the TaleSpire font graciously spared a bunch of time to talk about fonts with me! Getting to sit down with anyone who is an expert in something is a blast, and two hours just FLEW by. Amongst learning lots of random things about the challenges in making fonts, I also got to see our new font[2], which is nearing a testable state. Having proper support for Latin and Cyrilic will be great, and we have a good idea of where to start with CJK (Chinese, Japanese, and Korean) support too.
All in all, Thursday was grand.
On Friday I worked on settings, and I’ll take my last study day today[1].
Hope all is well for you folks,
Peace.
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
[0] I’m also revisiting good ol’ highschool algebra. There are so many things that have faded from school, so going through those materials has been a nice refresher. [1] Although we are now in September, I’m taking my last study day then as it fits in better with home life. [2] It’s styled very similarly to the current one. But this one is good :P
TaleSpire Dev Log 448 - When you accidentally a localization system
This is a video of me recently.
So I’m meant to be working on dice. I was. And I will. But I was chatting to Chairmander, and we agreed that it was a bit of a shame that we’ve done so much work on the tagging, but you folks haven’t seen any of it yet. It feels like just[0] having the new search implementation would make things so much nicer.
So, I took a day and worked on the new search. And I hated the code I wrote.
I had another go. Hmm, this is tricky. What am I missing?
I realized I was making it harder for myself as the tag type was very work in progress. I needed to nail down what the tag data layout should be.
After a bit of poking around, I started making progress, but one crucial detail of tags was still unexplored. Translation.
Now, translation is its own kettle of fish. We currently use Unity.Localization, which has some nice aspects, but we want to allow the community to be able to translate not just the game, but also each other’s creations. For that, we need something a bit more dynamic. Also, it’s dependent on Addressables, which has caused us no end of issues with our builds; we’ve been longing to get rid of it for ages.m/Manual/com.unity.addressables.html), which has caused us no end of issues with our builds. We’ve been longing to get rid of it for ages.
I did a bunch of research into localization last year, so I dug out those resources and spent an evening going through them again. With my brain loaded back up, I dove in. Before I knew it, I had evaluated a couple of libraries, decided on what we needed, and started work on replacing Unity.Localization
.
I replaced every text UI element in TaleSpire with our subclass that hooked into our new text and localization registry. I migrated all the existing translations to the new system[1], and then it was time to add a language dropdown to the settings window.
Now, our settings system was an experiment from years back, and it was a bit of a failure[2]. It works okay for you folks, but it’s a bit of a mess behind the scenes. It will not work for the language setting as we want to be able to use the setting inside the Unity editor, even when the game isn’t running. This leaves me two options: I can either make a settings file just for the localization, or I can bite the bullet and rewrite the whole damn settings system.
So I’m rewriting the whole damn settings system :P
This is going well. I should get most of the settings bit done tomorrow.
That’ll let me get the localization working.
Which will let me implement the tags system with a better understanding of how language will be represented.
Which will let me implement search.
And that will let me get back to dice!
This is all a little silly, and I could, of course, switch back any time. But this progress is hugely valuable, so I will keep riding this wave and see how much we can get out of it.
I hope you are all well.
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
[0] “Just”, the most devilish word in the english language. [1] We only had a very incomplete Norwegian translation, but it’s good for testing. [2] This is fine; lots of experiments fail. But we’ve been living with this one for a while.
TaleSpire - Beta update and box ruler
Hi folks,
The “AoE in initiative tracker” Beta has just got an update fixing a few bugs:
- Fix the issue that moving a tile showed the bounds of the tile at the location you moved the tile from.
- Fix a case where hide volume bounds briefly flash when edited.
- AoE names are now correctly positioned when using the “scene scaling” setting.
- Fixed a case where the GM turning your ruler into an AoE put the ruler tool into a weird state
- Fetching the initiative list from Symbiotes now contains AoEs
These fixes will be coming to the main branch soon, too.
Also coming soon is the box ruler. This has been a long time coming. There are multiple valid ways to make a box ruler, and previously we would get into that design discussion before getting distracted by something more pressing. This time, we just decided to send it and see. We can evolve this ruler over time.
Here is a work-in-progress video of it in action. We are not happy with the visuals yet, but once those are fixed, we’ll get this thing out to you. Thanks to all those who requested this feature on https://feedback.talespire.com/
The “AoE in initiative tracker” feature is looking good so far. So, barring any last-minute surprises, we should be shipping it to everyone this week.
Have a good one folks!
BUILD-ID: 15281430
TaleSpire Dev Log 447
Hi folks.
Today, I continued with the dice engine.
For those who didn’t see yesterday’s post, here is a warning:
GMs, Players, and even implementors of rule systems will not have to deal with the things I’m talking about below. The code gubbins I’ll be showing is for the under-the-hood implementation that will make the nice, user-friendly stuff work.
Currently, the available operations in the language are:
Math:
+
-
=
>
>=
<
<=
abs
floor
ceil
round
*
/
expt
mod
remainder
Functions and special-forms:
dice - Creates a dice set
roll - Puts the dice passed to this function into the dice tray for a user to roll
sort - sorts dice based on their score given a user-provided predicate
take-n - make a new dice-set containing the first N dice from another dice-set
take-if - make a new dice-set containing any dice from the given dice-set that satisfy a predicate
drop-if - make a new dice-set without any dice from the given dice-set that satisfy a predicate
high-face - return the 'highest' face for a given die (e.g. 6 for a d6)
low-face - return the 'lowest' face for a given die (e.g. 1 for a d6)
count - returns the number of dice in a dice-set
show - Push an entry into the dice history
let - for lexically defining variables and functions
if - it's a conditional
recurse - for calling the function you are in, only works from the tail position
This allowed me to implement:
- various kinds of exploding dice
- fate dice
- re-rolling on various criteria
- more complete math functionality
- counting the number of dice that satisfied some criteria
Many of the functions support dice-sets as well as numbers, so 2d6+5 would compile to (+ (roll (dice d6 2)) 5)
.
It’s going rather well. Next on the todo-list, I’d like to implement currying, clean things up, and start implementing the compiler.
I’m also considering using fixed-point math instead of floats, and I’m looking into combining recursion with re-rolls[0].
This will have to wait as I really need to do some work for TitanCraft tomorrow. They’ve been waiting patiently for me to sort out some bugs, and this week has gotten away from me.
We’ll get back to dice by the middle of next week for sure.
Disclaimer: This DevLog is from the perspective of one developer. So it doesn’t reflect everything going on with the team
[0] This sounds weird. However, we don’t want infinite loops, and this would mean the code would always yield to the user so they can cancel the roll if it goes on for too long.
TaleSpire Dev Log 446
Hi folks!
I’ve started work on my next big feature, the dice engine!
This is a complete rewrite not just of the networking code but also (and more excitingly) the system that handles the automation of different kinds of dice rolls.
Until now, we’ve supported very basic dice syntax. For example, you can type !farble:2d20-2/garble:3d6+2d4
into the chat bar and get a combined roll with 2d20, 3d6, and 2d4. Once you’ve rolled, it will do the math and show the results with the titles “fable” and “garble” above the respective results.
This is very limited, though; we don’t yet support exploding dice, re-rolls, fate dice, or many other useful constructions.
My task is to fix that limitation.
I’m gonna talk about work-in-progress techno hoo-hah for a bit, so a warning:
GMs, Players, and even implementors of rule systems will not have to deal with the things I’m talking about below. The code gubbins I’ll be showing is for the under-the-hood implementation that will make the nice, user-friendly stuff work.
Alrighty! With that out of the way, let’s get nerdy.
Many VTTs have a nice, terse dice syntax language that supports a range of useful kinds of rolls.
I want us to have something similar, of course, but eventually, I’d like to be able to support dice rolls written in other systems’ syntax, too. This is nice for people who have experience with other VTTs and don’t want to learn a new system each time they move.
I see most dice languages like regular expressions, a (nice?) compact way to express a specific class of problem.
However, there is usually a trade-off for that terseness, and it’s a loss of generality. For example, if a new game comes out with a new style of roll, you’d have to wait for new syntax to be added. However, with a richer language, the community could implement the new rule and start using it immediately.
Alright, so I want dice languages to be available to the users, but behind the scenes, I want something more flexible.
I also don’t want to have to make a whole new backend for each dice language we support. So it’s time for compilers \o/
The first step is transforming these strings into an AST. The AST is a data structure that describes a dice roll. It’s much more verbose than but much more general. It supports variables, function definition, recursion, etc.
Here is a contrived example where we roll 6d6, re-roll the two lowest results, and add up the final score[0]
(let ((rolled (roll (dice d6 6))))
(+ (take-top 4 rolled)
(roll (take-bottom 2 rolled))))
Phew, you wouldn’t want to have to write that every time! And, of course, you wouldn’t, as this representation would be used by TaleSpire behind the scenes.
Next up, to run it. We could write an eval function that simply walks the AST and executes it. That’s what I’ve done in my experiments so far, as it was a quick way to get something working.
For release, I think I’d like to take the AST and compile it into byte-code instead. We will then have a small byte-code evaluator to run that code. There are some advantages to this:
- The evaluator is simpler: For example, with tail-call elimination, we should be able to flatten the recursion to jumps.
- Easier to port to other languages: I’d like to port the evaluator to different languages. Having the code be a flat array makes working with it trivial, whether from C++ or Python.
- Trivial to encode for sending as a URL and embedding in rules documents: We have talespire:// URLs and a rule system coming in the future. I like the idea of making it very to interface with those things.
I’m currently experimenting with this system in common-lisp, as that’s just a nice place to noodle with ideas. I have an AST and working eval, so I’m able to make dice rolls in the REPL and check the behavior.
The language needs plenty of work, but to guide that, I will implement a bunch of common rolls from various rule systems. When I’m happy that this covers what we need, I’ll start on cleanup and then on the byte-code compilation.
This all may sound a bit complicated, but it’s actually straightforward to make. The code is way less involved than most things in making games. I recommend playing with toy compilers if you are curious. Once you get past parsing the text, it’s a lot of fun.
Okay, that’s enough nattering for now. I’ll see you in the next one.
p.s. I’d like to open-source the language and compiler once it’s been in production for a while. I couldn’t find a good place to stick this in the text above, so it’s chilling out down here.
[0] Again, this is very much not it’s final form. We will be using s-expressions for the textual representation of the AST, but take-top and take-bottom feel like they could easily be reworked as a take
function that itself would take a predicate function to tell it what to take.
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
TaleSpire Dev Log 444
I’m back! Having ten days to get a proper mental reset was very nice. I’ve barely been online (except to download things to read), so I’m definitely out of the loop, but I’ll get caught up to speed this week.
To give myself something fun to jump back into, I looked at a performance problem that has been on the list for a while.
The problem
Picking in TaleSpire is pixel-perfect. This is critical for moving in crowded spaces. We don’t pick based on physics colliders as a good collider doesn’t necessarily match the shape of the object exactly (if you are curious, do check out this dev log).
Instead, we render pickable things from the scene in a specific way and read back the thing the mouse is over the very next frame. That “very next frame” bit is important as we don’t want things to feel sluggish. However, reading back data so soon is generally not how the GPU would prefer to work. If you try to read data back from somewhere the GPU was using, it wants to wait until it’s done everything it was doing, and this causes a huge delay. Luckily, MJP’s post over here explained a feature in DirectX that we can use to read data back the next frame without stalling the rendering. We’ve used this for ages, and it’s been great, but there are cases where it has not worked.
My laptop is a Thinkpad with an integrated GPU (Intel UHD Graphics 620), and it runs terribly on an empty board in TaleSpire. I got out the profiler and saw this:
Jumpin’ Jesus, what is going on with PixelPicker.FetchSingleResult
?
The theory
If I recall correctly, Unity will try to overlap the rendering of frames (this is a good thing), so my theory is that something is touching the buffer that holds the picking results too soon, causing a stall.
Mitigating this could be really simple. We can just add a second buffer and switch between them each frame. That way, we don’t get any accidental prodding of the resources.
The result, and what next
Let’s not beat around the bush.
Yeah, that’s much better. The time to fetch the result is insignificant now (from 22ms down to 0.003ms or something in that area).
Of course, the frame rate is still far too low. There are a lot of things that need to be done to make it run well. But a win is a win, and this needed to be fixed so we could see the other issues. I’m happy with this.
We will be shipping this fix in the next couple of days.
So what next? I’m not exactly sure. I’ve got to get back to some partners now I’m back, and I need to get caught up with community happenings. However, I will do some more profiling and start thinking about what I want to tackle next on the performance front.
I’ll also look into the next big feature I’ll be starting work on. But more on that another day.
Until next time.
Peace.
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
TaleSpire Dev Log 442
Hi again folks,
In yesterday’s dev-log, I talked about us working with TitanCraft to help you bring your creations into TaleSpire.
The first thing they would like to do is allow their users to export their miniatures in the TaleSpire creature format. To that end, I’ve been working on a command-line tool they can use in their export pipeline.
Today, I fixed a bug in the handling of thumbnails, and suddenly, we had TitanCraft creations in TaleSpire.
These are looking very much at home! [0]
The rest of the day was spent on tweaks and fixes as we tried more minis.
Tomorrow, I’ll be looking at scale. TaleSpire has some limits on scaling due to our current movement code not being enjoyable once things get really big. As with our HeroForge support, we will need to massage the input to work well with our system. It’s something we’ve done before, though, so it shouldn’t be too hard.
That’s all for now,
Peace
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team
[0] Please note that these visuals are not final, we will likely tweak the shader before this feature ships.
TaleSpire Dev Log 441
Heya folks,
Today, I’ve been working on something different. A feature, for sure. But not one that goes directly into TaleSpire.
Today, I was making a program that can fit into TitanCraft’s pipeline so that, sometime in the future, you’ll be able to download TaleSpire-compatible assets right from their creator!
For those who don’t know, TitanCraft is an excellent way to create miniatures online, both for downloading and also 3D printing. If you haven’t checked them out before, I’d recommend it. Their approach to asset packs is really neat.
We’ve been chatting for a while about working together, so getting started on this is a lot of fun. My main task for today was taking TaleWeaverLite and reshaping it into something that could run headlessly. The goal being that they can then use this as part of the export process. I haven’t tried to share the codebase with TaleWeaverLite yet as I’m not sure how this project will evolve, but I expect we might do that once we know what shape this will be in the longer term.
I got the basics working. We have a command-line tool that takes a digital miniature, and repacks it for TaleSpire. They result opens in TaleWeaverLite, but I’ve got a few bugs in-game that I’ll fix up tomorrow.
Oh, and just in case anyone was worrying, no, we aren’t moving away from HeroForge or anything like that. We love what they do, and we’d like to work with more folks in the space, not fewer! So, if you have a favorite way to bring characters and content into your adventures, why not let us know at https://feedback.talespire.com. If it’s been suggested already, please upvote it. We keep an eye on the vote to help prioritize what we are up to.
Anyhoo, that’s all for tonight.
Have a good one!
Disclaimer: This DevLog is from the perspective of one developer. It doesn’t reflect everything going on with the team