The Lord of the Rings Minecraft Mod Wiki
Advertisement
This feature is present in: Renewed only.

Curuquesta (Quenya: "cunning speech") is the codename of the Conditional Speech Engine developed for the Renewed version of the mod. It is a greatly developed and expanded version of the Legacy speechbanks system for NPCs.

The central feature of the Curuquesta speech engine is its ability to determine a speech line based on numerous conditions - aspects of the NPC, the player, or the current environment. In Curuquesta speechbanks, all lines are associated with one or more conditions paired with corresponding values; for example, a line may be defined to only play in the morning, or in a particular biome, or for players whose alignment meets or exceeds a certain rank. When the speech engine is asked to provide a line, it will gather the list of conditions from the environment, and select from only among those lines which meet.

In effect, this now allows NPCs to 'know' where they are, when they are, and with whom they are talking - and change what they say accordingly.

Comparison with Legacy[]

This contrasts with how speechbanks work in the Legacy version of the mod. These were far simpler, essentially being plain .txt files with a list of different speech lines. There is no associated context, so any line can be played at any time or place.

The only categorisation of speech lines was at a very broad level, according to the player's alignment with the NPC's faction - there were separate 'friendly' vs. 'hostile' speechbanks (and 'neutral' for traders with whom you may have positive alignment, but not yet enough to trade). But within these two or three broad categories there was no restriction on which lines could be played when, where, or to whom.

(In the new Curuquesta system, this relation is incorporated as one of the speech conditions - so there is now a single speechbank file containing lines for both friendly and hostile players, instead of separate files.)

For many years, fans of the mod had noticed NPCs' seeming lack of awareness of their speech lines, leading to amusing situations like a hired Gondor Soldier declaring "Welcome to Gondor, Person!" despite gallivanting around in Mordor. Certain NPCs would also continue to address the player as if they were a new friend - or a stranger of dubious loyalty - despite the player having alignment well into the thousands. And there were plenty of less egregious examples, like a Hobbit wishing "Good day, Person!" in the middle of the night, or displaying total ignorance of the weather.

Well, sometimes long-requested wishes are granted, and now situations like these can be handled more convincingly with conditional speech. If the "Welcome to Gondor!" line is tagged to only play when in the biomes of Gondor, then Gondor Soldiers need no longer welcome people to Gondor unless they are quite certain that this is indeed where they are.

File format and structure[]

Format[]

Curuquesta speechbanks are defined in JSON, owing to the extra structure that's needed to associate lines with conditions. The file extension is .json. This is the same data format used for block/item models, Data Pack files, and translations (in Renewed).

Speechbank lines are not datapack files, though, because they are defined on the client side, not the server side. However, what is defined in datapack files is the definition of which NPC type uses which speechbank. See below for more on this distinction.

Example[]

What follows is an example of a complete speechbank file. This speechbank is the English-language bank for the Hobbits. It is located at /assets/lotr/speech/hobbits/hobbit/en_us.json.

The structure is explained below the file.

{
  "speech": [
    {
      "conditions": {
        "relation": "friendly"
      },
      "lines": [
        "Short cuts make for long delays!",
        "The only brew for the brave and true... comes from the Green Dragon!",
        "Where would we Hobbits be without pipe-weed?",
        "Don't you wish you lived in a Hobbit hole too?",
        "Hobbit holes! Warmth, good food, and comfort.",
        "It's a dangerous business, going out your front door.",
        "The Sackville-Bagginses came for lunch yesterday. Now I can't find any of my plates, nor my mugs...",
        "The road goes ever on and on...",
        "Adventures? Nasty things, they are. Make you late for dinner.",
        "There's nothing quite like a pint of ale from the Green Dragon!",
        "I don't know what I'd do without a good leaf of Longbottom on a hot summer's day!",
        "I have relatives in Buckland. They're funny fellows...",
        "The market in Stock is a great place for food, drink and pipe-weed!",
        "Have you seen the frogs in the marshes around Frogmorton way?",
        "The Northern Moors are wild and windy, but full of fine inns with homely hearths.",
        "People tell of Elves in the woods near Willowbottom. I reckon it's just the Largefoot family!",
        "It's best to avoid Sarn Ford. That's where all the travelling folk meet, and some of them can be very queer!",
        "I don't want any adventures, thank you very much!",
        "My knowledge of potatoes is second to only the Gamgees'!",
        "On a breezy day I like to lie beneath an oak tree and sleep. Like we Hobbits should!",
        "Longbottom leaf is the leaf of wonders! Is it not, #p?",
        "My flowerbeds could do with a good sprinkling!",
        "For a jolly day out I suggest Needlehole... or food!",
        "A fine meal is always the best option!",
        "Mmm, buttered potato and peas...",
        "Pipe-weed is good. Mushrooms are better!",
        "I am fond of mushrooms out of a field.",
        "Good plain food is the food for me.",
        "I go to bed late and get up late for breakfast. It's the only way to be!",
        "Gardening is a fine way to spend a day.",
        "A cup of tea for me!",
        "The parties in the Shire are the best you'll ever see. Food, drink and merriment!",
        "You're almost as tall as old Bullroarer was! Have you heard the tale of how he created the game of golf?",
        "They say the rich families keep treasure in their burrows. Chests packed with gold and silver... and jools!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "day_or_night": "day"
      },
      "lines": [
        "Good day, #p.",
        "Good day.",
        "Good day!",
        "Good day to you, #p! How are you doing?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "biome": "#home"
      },
      "lines": [
        "This here field is full of rabbits! Better get the little ones to chase them off!",
        "My relatives in Bree tell of strange goings-on..."
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "morning"
      },
      "lines": [
        "Good morning!",
        "Good morning to you!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "morning",
        "rank": ">=guest"
      },
      "lines": [
        "Good morning, #p!",
        "Good morning to you, #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "afternoon"
      },
      "lines": [
        "Good afternoon!",
        "Good afternoon to you!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "afternoon",
        "rank": ">=guest"
      },
      "lines": [
        "Good afternoon, #p!",
        "Good afternoon to you, #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "evening"
      },
      "lines": [
        "Good evening!",
        "Good evening to you!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "evening",
        "rank": ">=guest"
      },
      "lines": [
        "Good evening, #p!",
        "Good evening to you, #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "dawn | dusk",
        "weather": "clear"
      },
      "lines": [
        "What a pretty sky up there!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "dawn",
        "sun_admiring": "true",
        "rank": ">=guest"
      },
      "lines": [
        "Isn't it a beautiful sunrise, #p?",
        "Oh, good morning, #p. I was just watching the sun come up. Isn't it nice?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "dusk",
        "sun_admiring": "true",
        "rank": ">=guest"
      },
      "lines": [
        "Isn't it a beautiful sunset, #p?",
        "Oh, good evening, #p. I was just watching the sun go down. Isn't it nice?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "day_or_night": "night"
      },
      "lines": [
        "Well, I'd best be off to bed!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=friend"
      },
      "lines": [
        "May the hair on your toes never fall out, #p!",
        "May the hair on your toes never fall out.",
        "Always a pleasure, #p!",
        "You're not bad for one of the Big Folk!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=bounder"
      },
      "lines": [
        "Oh, it's you, #p! I didn't recognise you at first, see.",
        "See? I always said there was good Big Folk, like you, #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=bounder",
        "day_or_night": "day"
      },
      "lines": [
        "Well, if it isn't #p! How are you today?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=bounder",
        "day_or_night": "night"
      },
      "lines": [
        "Well, if it isn't #p! How are you tonight?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=shirriff",
        "day_or_night": "day",
        "player_gender_pref": "m"
      },
      "lines": [
        "Good day, sir!",
        "Good day, sir #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=shirriff",
        "day_or_night": "day",
        "player_gender_pref": "f"
      },
      "lines": [
        "Good day, miss!",
        "Good day, miss #p!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=shirriff"
      },
      "lines": [
        "Hello, #p! I ought to invite you in for afternoon tea one of these days.",
        "Hello, #p! I ought to invite you in for second breakfast one of these days.",
        "Hello, #p! I ought to invite you in for pipe-weed one of these days.",
        "All's well in the Shire, thanks to you!",
        "All's well in the Shire, thanks to you, #p!",
        "Oh, #p... I must be inviting you to my cousin's thirty-third birthday party. Don't let me forget!",
        "Oh, #p... I must be inviting you to my second cousin's marrying-party. Don't let me forget!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "morning"
      },
      "lines": [
        "Is it time for second breakfast yet?",
        "Anyone for a third breakfast?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "afternoon"
      },
      "lines": [
        "Why, it's already time for afternoon tea!",
        "I'd best be going for afternoon tea."
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "daytime": "evening"
      },
      "lines": [
        "It'll be time for supper soon!",
        "About time I got to the supper table."
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "weather": "rain"
      },
      "lines": [
        "Stay out of the rain, #p!",
        "Stay out of the rain!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": "<friend",
        "biome": "#home"
      },
      "lines": [
        "Welcome to the Shire!",
        "Welcome to the Shire, #p!",
        "Greetings, #p! I've seldom seen Big Folk like you around these parts!",
        "Welcome to the Shire, traveller from afar! What news do you bring from the wide world?",
        "Hello, #p. Are you staying long?",
        "Big Folk in the Shire? Strange goings-on all round!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": "<friend"
      },
      "lines": [
        "Oh, #p. I do believe we've met before?",
        "#p, is it? Yes, I remember you now. You Big Folk all look similar to me.",
        "Have we met, #p? I never forget a face!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "biome": "#foreign"
      },
      "lines": [
        "I do so miss the Shire.",
        "I've never been so far from the Shire before.",
        "If I take one more step, it'll be the farthest from home I've ever been.",
        "Perhaps it's time to return home. I miss the Shire.",
        "Adventures? Nasty things, they are. I think it's about time I returned home.",
        "The road goes ever on and on, under trees and over streams, and I must follow, so it seems! But I shall be turning home soon.",
        "Is this the land of your folk, #p? A strange place it is, and that's for sure!",
        "Now we ought to find the way back home, #p.",
        "Hobbit holes! Warmth, good food, and comfort. I do miss mine...",
        "I've had my fill of foreign lands for now, #p. Shall we be a-wandering home soon?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "biome": "lotr:mordor | lotr:mordor_mountains | lotr:gorgoroth | lotr:morgul_vale | lotr:angmar | lotr:angmar_mountains"
      },
      "lines": [
        "What a dreadful place! Let us return to the Shire at once!",
        "How have we come to such a dreadful place?",
        "What an awful land this is, #p!",
        "What a frightful land! We must find our way back to the Shire!",
        "This country is enough to fill even the stoutest hearts with dread!",
        "I declare I've seen quite enough of this place! Let's go back to the Shire, and quickly!",
        "Well, #p, this is a nasty place if I ever saw one!",
        "I feel all ill at ease here. Say, #p, do you know the road back to the Shire?",
        "I don't know how we ever came to find ourselves in such a place as this, #p.",
        "Even the air here is nasty stuff!",
        "They always warned me against following those roads out of the Shire!",
        "I wish I'd paid more attention to my old Gaffer! Never should've left the Shire, no no!",
        "If I'd known this was what lay beyond our Shire-borders, I never would've gone a-wandering!"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=guest"
      },
      "lines": [
        "Have you any pipe-weed, #p?",
        "I find farming to be a pleasant pastime! Have you tried it, #p?",
        "The finest pleasure is a meal well done. Do you know that feeling, #p?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": ">=guest",
        "day_or_night": "day"
      },
      "lines": [
        "And how are you today, #p?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "in_conversation": "other",
        "rank": "<shirriff"
      },
      "lines": [
        "Do you mind? I was in the middle of a conversation here!",
        "#p! I was just in the middle of a conversation.",
        "A bit rude to interrupt, isn't it?",
        "There's no need to interrupt, is there?",
        "I was just speaking! Wait your turn!",
        "How rude! We were having a good chatter there, if you please.",
        "Mind your own business. Can't you see we were talking?",
        "Mind your own business, please! Now leave us alone.",
        "I see you have no qualms about interrupting some good Hobbit chatter!",
        "If you please, #p, I was in the middle of a conversation.",
        "...and I turned to him and said - excuse me? Do you mind interrupting us, #p?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": "<guest",
        "biome": "#home"
      },
      "lines": [
        "These Big Folk! They come over here and steal our farms. Keep the Shire for the Hobbits, that's what I say!",
        "Not often we see Big Folk here in the Shire.",
        "One of the Big Folk in the Shire? Mind you take care!",
        "Who let you into the Shire? I hope you're not here to cause any mischief!",
        "#p, is it? Not often one of you Big Folk wanders into our Shire.",
        "I don't believe we've met, have we? Welcome to the Shire.",
        "Have we met? No? Well, this is our Shire. Lovely place, isn't it?"
      ]
    },
    {
      "conditions": {
        "relation": "friendly",
        "rank": "<guest"
      },
      "lines": [
        "What's your name, then? #p? I don't believe we've met.",
        "I don't believe we've met before.",
        "#p, you say? It's not often we meet one of you Big Folk.",
        "And who are you? #p? Well, I hope you're not one of those strange folk!",
        "Have we met?",
        "Have we met? No? Well, I'll be on my way, then.",
        "Hmm... I don't believe we've met.",
        "One of the Big Folk? What was your name, again?",
        "What was your name, again? Speak up!"
      ]
    },
    {
      "conditions": {
        "alternatives": [
          {
            "relation": "hostile"
          },
          {
            "relation": "friendly",
            "rank": "<guest"
          }
        ]
      },
      "lines": [
        "Strange folk have been seen in these parts, and you look like one of them.",
        "Aren't you one of those strange folk I've heard talk of?",
        "There's talk of strange folk round here. Might you be one of them?",
        "I've seen strange folk lurking around these parts, and you remind me of one of them. I don't trust you.",
        "I don't know about you..."
      ]
    },
    {
      "conditions": {
        "alternatives": [
          {
            "relation": "hostile"
          },
          {
            "relation": "friendly",
            "rank": "<guest"
          }
        ],
        "biome": "#home"
      },
      "lines": [
        "Big Folk make me uneasy. Mind you remember whose Shire this is."
      ]
    },
    {
      "conditions": {
        "relation": "hostile"
      },
      "lines": [
        "I hear you've been causing trouble round here.",
        "You look like a troublemaker!",
        "You look like a troublemaker, #p.",
        "Stay away from the Shire, #p.",
        "Stay out of the Shire!",
        "I shan't be lending you any of my pipe-weed!",
        "We don't take kindly to folk like you.",
        "You're not a nice fellow, #p.",
        "If you'll be begging my pardon, you make me feel uncomfortable.",
        "You're one for the Mewlips, #p!",
        "I knew you queer old Big Folk were trouble!",
        "We don't appreciate folk like you in the Shire.",
        "Don't try any of your Big Folk tricks on me!",
        "Don't try any of your Big Folk tricks on me, #p!",
        "May you never savour Longbottom Leaf again!",
        "I knew I couldn't trust you. Your feet are too small!",
        "The Green Dragon will never serve you!",
        "Leave me in peace!",
        "Leave me in peace, #p!",
        "My cousin owns some very large dogs. You don't want to meet them, do you?",
        "Interrupter of the peace!",
        "Stop! Troublemaker!",
        "Old Bullroarer would knock you down a hole or two!",
        "The wolf that one hears is worse than the Orc that one fears. But get between a Hobbit and their peace...",
        "Only a fool would interrupt my mealtimes!",
        "Strange folk meet strange ends!",
        "Don't make me fetch my scythe!",
        "You may be taller than me, #p. But you're greater in no other way!",
        "Folk like you should have been given a good drownding!",
        "You must be one of those ne'er-do-wells I keep hearing talk of!",
        "We in the Shire don't take kindly to ne'er-do-wells like you, #p.",
        "Back off!",
        "You're a ne'er-do-well if I ever saw one, #p.",
        "I don't want no trouble, #p!",
        "Now, now, let's not be having no trouble!",
        "I don't want no trouble with you, #p.",
        "Off you go now, or I'll be getting my rake!",
        "Don't make me reach for my rake!"
      ]
    },
    {
      "conditions": {
        "relation": "hostile",
        "biome": "#home"
      },
      "lines": [
        "The Shire is no place for troublemakers like you!",
        "You'd better leave the Shire, #p, or I'll be a-calling the Shirriffs!",
        "Get off my land!",
        "Get off my land, #p!",
        "My pitchfork is pointy enough to make you think twice about returning to the Shire!",
        "The Shire has been peaceful for hundreds of years and I daresay you'll regret changing that!",
        "Stay away from my potatoes!",
        "The Shirriffs will see to you, #p!",
        "Go back over the hedge!",
        "Go back over the hedge, #p!",
        "A troublemaker I see? The Shire only welcomes Hobbits! Begone!",
        "I don't mean no harm, #p, but your kind aren't welcome here."
      ]
    },
    {
      "conditions": {
        "relation": "hostile",
        "daytime": "morning"
      },
      "lines": [
        "Second breakfast would be too good for you, #p!"
      ]
    }
  ]
}


Wow! Now, let's get into how it works.

Structure[]

The overarching structure of the speechbank is a list of entries. Each entry describes one or more lines of speech, and associates it with a set of one or more conditions which must be satisfied in order for the speech line or lines to be played.

Schematically:

  • "speech" - a list of entries
    • "conditions" - a collection of one or more condition-value pairs
      • The name of the condition
      • The value expression to satisfy
    • "lines" - a list of one or more speech lines
      • Individual speech lines

Value expressions[]

The building blocks of the conditional speech format are condition-value pairs. The name of the condition (enclosed in double quotes) is on the left, and the value expression is on the right.

The value expression encodes a 'test' for the particular value or values of the condition which are required for these speech lines to play. This can be a straightforward equality check in the simplest case, or a more complex logical expression describing many separate particular values - or ranges - which are acceptable.

Simple equality[]

The basic form is an equality check against a particular condition value, e.g.

"weather": "snow"

which requires the current weather to be snowy.

Note on boolean values - JSON supports boolean true and false as primitive types, so the speech engine can parse single boolean value expressions in either string form (enclosed with double quotes) or in plain primitive form: both "true" / "false", and true / false will work.

Logical OR[]

All conditions except boolean types support OR-expressions with the | sign - e.g.

"condition_name": "value1 | value2 | value3"

Values of boolean data type have only two values and thus OR would be meaningless.

Range expressions[]

As seen in the table of conditions below, some condition types (numeric values, ranks, and the health/hunger divisions) also support range expressions. These use the symbols >= > = < <= (respectively: greater than or equal to; strictly greater than; exactly equal to; strictly lesser than; lesser than or equal to) to encode comparisons with a particular value. You can combine these with & (logical AND) to describe a range. You can also use | (logical OR), as above, with individual comparisons or range expressions, to describe a set of disjoint ranges where a value need satisfy only one of these.

Some examples of the form:

  • "align": ">= 10 & <50" to satisfy an alignment value between 10 (inclusive) and 50 (exclusive).
  • "align": "<20 | >100 & <= 300 | =800" to satisfy an alignment value lesser than 20 (exclusive), OR between 100 (exclusive) and 300 (inclusive), OR equal to exactly 800.
  • "rank": ">=friend & <captain" to satisfy a faction rank of at least Friend, but lower than Captain.
  • "rank": ">=soldier & <=lord" to satisfy any faction rank between Soldier and Lord inclusive.
  • "rank": "<guest" to satisfy a faction rank below Guest - so in most cases, either Stranger or Enemy - which usually corresponds to having less than +10 alignment.

This allows you to define a particular range (or ranges) of values which a line must satisfy, rather than needing to match a single exact value, which would be useless for continuous numbers.

Condition-value sets[]

The next step is to combine multiple condition-value pairs together. A speech line (or lines) may be required to satisfy multiple conditions - for example, the weather must be rainy, AND the time of day must be either morning or afternoon, AND the player's rank must fall within a particular range.

You can populate the conditions object with as many condition-value pairs as you like, as long as each condition name is unique. The following condition-value set encodes the above example.

"conditions": {
  "weather": "rain",
  "daytime": "morning | afternoon",
  "rank": ">=friend & <captain"
}

Alternative condition-value sets[]

TODO

Speech lines[]

The actual speech lines are the easy part. This is just a list of strings, enclosed in double quotes.

Speech lines can contain replaceable variables, which are tokens beginning with # that the speech engine replaces with a meaningful value when the line is played. An example of this is the player name, which is encoded as #p. Any instances of #p in a line will be replaced with the current player's username when the line is played.

For more on replaceable variables, read the page section below.

Conditions[]

There are 32 different speech conditions in the Curuquesta system. These generally fall into three categories:

  • aspects of the player;
  • aspects of the NPC;
  • aspects of the surrounding environment (biome, weather, time of day, etc.)

The full list of these conditions, and their possible values, is as follows.

Note that as described above:

  • Since JSON supports boolean values as primitive types, simple boolean value expressions may be written as either string "true" / "false", or primitive true / false.
  • All conditions except boolean support OR-expressions.
  • Some condition types (numeric values, and the health/hunger divisions) also support range expressions.
Curuquesta Speech Conditions
Condition name Description Data type Possible values Supports range expressions?

>= > = < <= together with & and |

day_or_night The current half of the ingame day-night cycle. enum day, night no
daytime Smaller meaningful divisions of the ingame day-night cycle, according to English convention.

(Speechbanks in other languages may wish to represent different divisions of time - for this case, the two below variables can be used.)

enum dawn, morning, afternoon, evening, dusk, night no
daytime_phase The current progress through the day-night cycle, from 0.0 at midnight, through to 0.25 at sunrise, 0.5 at high noon, 0.75 at sunset, and returning through 0.999 to 0.0 the following midnight.

Defined by the angle of the sun as it moves around the world.

float [0.0, 1.0) interval yes
hour The above value multiplied by 24, to correspond to the 24-hour clock, as this may be more natural to work with than 0-1. float [0.0, 24.0) interval yes
weather The current ingame weather type. Incorporates vanilla and mod weather types. Note that this also depends on the current biome and y-coordinate. enum clear, rain, snow, ash, sandstorm no
thunder Whether the weather is also thundering.

(Note - mechanically, the game handles thunder separately from rain/snow etc. Thunder is not a basic weather type, but a separate state - a 'worsening' of the weather that can turn off or on. Therefore it is a separate condition from the basic weather types.)

boolean true, false no
moon_phase The current ingame phase of the moon.

Note that this is independent from whether the moon is actually visible right now.

enum full, waning_gibbous, third_quarter, waning_crescent, new, waxing_crescent, first_quarter, waxing_gibbous no
lunar_eclipse Whether the current date corresponds to a lunar eclipse ("blood moon").

Note that this too is independent from whether the moon is actually visible right now.

boolean true, false no
align The player's alignment with the NPC's faction. float Any decimal value (technically, IEEE 754 single-precision) yes
rank The player's rank with the NPC's faction. This is essentially a division of the above align variable into more useful ranges. string enemy, neutral, or any of the ranks defined by the faction yes
relation The player's current relation, broadly speaking, with the NPC. Incorporates alignment as well as whether the NPC is currently attacking.

- friendly if alignment is positive or zero AND the NPC is not attacking the player;

- hostile if alignment is negative OR the NPC is attacking the player.

enum friendly, hostile no
pledge The name of the specific faction to which the player is currently pledged, if any - or null otherwise. nullable string Any faction name, or null no
pledge_relation A broad description of how the player's current pledge relates to the NPC's faction. The player may be pledged to the same faction (this); an ally or friend (good); a neutral relation (neutral); an enemy or mortal enemy (bad); or no pledge (none). enum this, good, neutral, bad, none no
player_gender_pref The player's gender preference, as indicated by their selection of preferred rank title gender through the Middle-earth Factions menu.

Useful to let the NPC change the form of their address (e.g. sir/madam, lord/lady) based on what the player prefers.

This is especially useful for translations into languages which make use of linguistic gender. When translating the speechbanks, you can split gendered lines into pairs of entries - one with the additional condition "player_gender_pref": "m", for the male-gendered lines, and one with "player_gender_pref": "f", for the female-gendered lines.

enum m, f no
gender The gender of the NPC itself.

This too is especially useful for languages which make use of linguistic gender.

enum m, f no
can_trade Whether the NPC can trade with the player.

This has no meaning in Renewed yet, but once traders are added it will incorporate the relation condition plus a particular alignment requirement, as in Legacy. This will essentially correspond to the 'neutral' speechbanks in Legacy.

boolean true, false no
in_combat Whether the NPC is currently in combat (with anyone / anything). boolean true, false no
in_conversation Whether the NPC was already in conversation when you clicked on them - and if so, whether they were already talking to you, or to someone else (player or fellow NPC). enum none, same, other no
stargazing Whether the NPC is currently looking at the stars. boolean true, false no
sun_admiring Whether the NPC is currently watching a sunrise or sunset. (Can be used in combination with the daytime condition to determine which of the above, specifically, if desired.) boolean true, false no
fleeing Whether the NPC is currently fleeing from another entity, e.g. hobbits fleeing from orcs. boolean true, false no
biome The current biome - strictly speaking, at the NPC's position, not the player's although these will usually be the same biome.

#home can be used to as a quick reference capture any of the biomes tagged as such within the NPC's faction data file. #foreign, then, refers to all other biomes.

string #home, #foreign, or the resource ID of a particular biome(s) e.g. lotr:anorien, minecraft:plains no
underground Whether the NPC is currently underground, as defined by the NPC being both 1) below sea level and 2) occluded from the sky. boolean true, false no
drunk Whether the NPC is drunk, i.e. has the Nausea status effect. boolean true, false no
player_drunk Whether the player is drunk, i.e. has the Nausea status effect. boolean true, false no
hired Whether the NPC is a hired unit, and if so, whether they are hired by the current player or by someone else.

This has no meaning in Renewed yet, but will once unit hiring is ported.

enum own, other, none no
mounted Whether the NPC is riding a mount. boolean true, false no
player_mounted Whether the player is riding a mount. boolean true, false no
health The health of the NPC as a proportion of their maximum health, either full or divided into one of four useful intervals.

- danger is health below 25%

- low is [25%, 50%)

- medium is [50%, 75%)

- high is [75%, 100%)

- full is = 100%

enum danger, low, medium, high, full yes
player_health The health of the player, following the same schema as above. For a player, therefore,

- danger is health below 5 ( 5 )

- low is health greater than or equal to 5 ( 5 ) and below 10 ( 10 )

- medium is health greater than or equal to 10 ( 10 ) and below 15 ( 15  15 )

- high is health greater than or equal to 15 ( 15  15 ) and below full health of 20 ( 20  20 )

- full is full health at 20 ( 20  20 )

enum danger, low, medium, high, full yes
player_hunger The hunger of the player, following the same classification schema as above. enum danger, low, medium, high, full yes
personality Any of various personality traits that are randomly assigned upon an NPC's creation. There are 10 independent pairs of mutually exclusive traits, listed to the right. All NPCs are assigned one trait from each pair with a 50-50 chance of each.


This condition effectively allows you to define speechbanks-within-speechbanks, with certain NPCs using a certain subset of lines consistently.


As of Renewed 5.1, none of the mod's default speechbanks use this condition. It was added in by request, mainly to offer speechbank resource pack creators the ability to further diversify speechbanks within a particular NPC type. The mod will likely make use of this in the future for some banks, but it will not be used extensively.


There is no restriction on which NPC types can be assigned which traits; for example, a "polite" Orc may be understood as "polite" relative to the normal Orcish standard!


Note this condition supports AND-expressions of traits (&) as well as OR-expressions.

enum proud | modest

brave | nervous

calm | anxious

polite | rough

friendly | rude

strange | normal

loud | quiet

clever | stupid

wise | insane

excitable | dull

no

Replaceable variables[]

The other pillar of the Curuquesta engine is the definition of replaceable variables, which are essentially template markers in a speech line that tell the engine to replace them with a meaningful value. All variables must begin with # and have both a short and long alias. All are case-insensitive in both long and short form.

At present there is only one replaceable variable - the player name. Any occurrences of #p or #player in a speechbank will be automatically replaced with the username of the current player when played ingame. Since this is case-insensitive, #P or #PLAYER, or even #Player, etc. will also work. For consistency the mod uses the short alias in all cases.

In the future this system will be expanded to support some of the other elements from Legacy speechbanks, like tavern name and mini-quest objective. It is a more extensible system, built to be able to handle these cases cleanly instead of adding each one as an individual workaround.

Curuquesta Replaceable Variables (Note - the # prefix is essential!)
Variable aliases Description Possible values
#p

#player

The username of the current player. Whatever the player's username is!

Translations and resources[]

The development of Curuquesta includes some other advantages over the Legacy speechbank system, which have long been requested by fans.

These features are 1) support for translated speechbanks, and 2) support for custom speechbanks in resource packs.

While these are not strictly related to conditional speech itself, and could have been added to the Legacy system without developing the whole conditional framework, it was an appropriate time to add these upgrades alongside the complete redevelopment of the speech system.

Translations[]

Renewed speechbanks support translation! Each speechbank points not to a single file, but to a folder, which can contain a file for each language code. This allows the community translators to provide translations of the speechbanks into their languages.

A speechbank folder, say, assets/lotr/speech/hobbits/hobbit, will contain a JSON file at /en_us.json for the speech lines in English, the mod's default language. And this folder can also contain a fr_fr.json, pl_pl.json, cs_cz.json, zh_cn.json... and so on, which define the speech lines for those languages respectively.

The speech engine will attempt to load the speechbank language file that corresponds to the game's current language. Should the file for the current language not exist, it will default to the English-language speechbank at en_us.json.

For some advice and guidance on translating the mod's speechbanks, read here.

Resource packs[]

Speechbank files can be replaced by resource packs in the same manner as all other resources (textures, models, blockstates, quotes...) This also applies to the translated speechbanks, which in this respect are no different from /[speechbank path]/en_us.json.

Datapack speech definitions[]

Like many other features in Renewed, speechbank usage is defined in the mod's datapack files. This is the definition of which entity type uses which speechbank.

These definitions are located in the mod's NPC entity type settings files, under /data/lotr/npcs/entity_settings/[entity type name].json.

For example, the Mordor Orc's entity settings file, which lives at /data/lotr/npcs/entity_settings/mordor_orc.json:

{
  "faction": "lotr:mordor",
  "speechbank": "lotr:mordor/orc",
  "kill_bonus": 1
}

This defines that Mordor Orcs use the speechbank "lotr:mordor/orc". The speechbank paths in these files are a kind of shorthand, which the mod converts into a full client-side resource path at /assets/lotr/speech/mordor/orc.json.

Defining the speechbanks for entity types in this way means that they can be modified by datapacks. So, for example, you could create a datapack which altered the "speechbank": in the above file to point to "lotr:rohan/warrior" instead - and when installed, this datapack would cause all Mordor Orcs to use the speechbank of the Rohirrim Warrior.

There is an important distinction to note here:

  • the speechbanks themselves (lines, conditions etc.) are defined in client-side resource files (/assets/lotr/speech...)
  • the mappings of which entity type uses which speechbank are defined in server-side data files (/data/lotr/npcs/entity_settings...)

Therefore, to change which entity type uses which speechbank definition you would make a data pack to install in the server-side world's datapacks folder, but to actually change the speech lines in existing speechbanks - or define the lines for new speechbanks, for that matter - you would make a resource pack to install in the client-side game's resourcepacks folder.

SpeechbankOverride[]

It is also possible to use commands to change the speechbank that a specific NPC uses (as opposed to redefining the speechbank for all NPCs of that type via a data pack).

NPCs have a new NBT-tag, "SpeechbankOverride" (string type). This is normally empty. But if set to a value, it will cause the NPC to use that speechbank instead of its default. This tag is part of the NBT data saved with the entity so it persists once set.

For example, if you run the following command in the vicinity of a hobbit:

/data modify entity @e[distance=..4,limit=1,type=lotr:hobbit] "SpeechbankOverride" set value "lotr:mordor/orc"

it will cause that particular hobbit to use the lotr:mordor/orc speechbank from now on, which is the speechbank of the Mordor Orcs. Your hobbit will now talk, grumble, and beg for maggoty bread just like an Orc!

Note the elements of this command:

  • distance=..4 to search for entities within a distance of 4 blocks to modify;
  • type=lotr:hobbit to select only entities of lotr:hobbit type (the basic Hobbit NPC);
  • and set value "lotr:mordor/orc" to set the hobbit's "SpeechbankOverride" to the Mordor Orc speechbank.

You can change all of these values to suit whatever entity type and speechbank you want to work with. (The limit=1 part is necessary to limit the entity selector to only one entity, because this command can only modify one entity at a time.)

Note also that this tag, like other NBT-data, can be set in an entity's /summon command.

Custom speechbanks for entities[]

The really exciting part of this NBT tag is that you aren't limited to the speechbanks that come with the mod. You can set SpeechbankOverride to anything at all, and then, as long as your client-side resourcepacks have a speechbank file defined under the correct path, the NPC will use that speechbank! Using this method you can essentially define completely custom speechbanks and have custom NPCs say anything you like. The only limit is your imagination.

History[]

TBC

Advertisement