OpenTTDDevBlackBook/Map/Town Names

From OpenTTD
Jump to: navigation, search
HAL (Hardware Abstraction Layer)

Audio
Music
Graphic

Window System

Using the Window System
Colour codes that exist in OpenTTD
Adding a text box
Understanding the widget focus system

Settings

Add a setting
Add a squirrel function
Do a savegame BUMP

The Map / Scenario

Understanding the Dynamic Landscape Array
Understanding the SaveGame Handler
HOWTO - Create good Scenarios
HOWTO - Add New Town Name Generators

The actual simulation

Vehicles
Using Orders
Pathfinding
Ratings
Train Acceleration

Language and Strings

Format of langfiles
Using OpenTTD Strings
List of Special Strings

Multiplayer

The Core Interface

Starting a Server
Connecting to a Server
Using the list of LAN/Internet Games

The OpenTTD TCP Protocol
The OpenTTD UDP Protocol
HOWTO - Debug desyncs

Ingame Console

The Console Window
Using Console Scripting
HOWTO - Add Functions/Commands to the Console
HOWTO - Add Variables to the Console
HOWTO - Direct Variable Access using ICONSOLE_VAR_POINTER
OpenTTD Console Commands
OpenTTD Console Variables
Development History

This guide will show you how to add new town names to OpenTTD.

Recommended way

Do this via NewGRF: See here: Town names.

Old/alternative way

Content.png

Historic content
This page or section contains content that is no longer relevant for the current version of OpenTTD. Please keep it intact because it shows some of the history of this wiki as documentation.

Contents


This requires a code change and therefore needs recompilation in order to work. Check out this page if you are not sure how to do this.

It is highly unlikely that new town names which cannot be generated (i.e. a static list of names) will be accepted into the code base. There are severe issues regarding this because the game requires that each town has a unique name. If the list of possible names is exhausted, then the game will hang (as of r1863).

This is a generic guide intended to encourage and help people who wish to know more about this feature. General points of contact are lucaspiller (author of the guide), Jango (guy who rewrote the code into the new format), truelight (who committed Jango's changes), tron (who corrected all Jango's changes :P) and Darkvater (who knows a lot about this area).

Files to Edit

There are 4 files you need to edit:

  1. strings_type.h
  2. table/namegen.h
  3. namegen.c
  4. lang/english.txt

Be aware that before r1307, the namegen code was quite different, so make sure you are using the new style.

How the Town Name Generator Works

"table/namegen.h"

All data concerned with town name generation is stored in the header file "table/namegen.h". This file contains all the character arrays which are used to form the resulting names. The length of these strings, and the length of the array, are all computed at compile time, and we no longer need the #DEFINE numbers which used to be used in the determination of which strings were available to the generation code. Another change is the intended separation of types of word. In the old system, words were sometimes placed in the same array, even if they were not interchangeable. Recommended behaviour now is to ALWAYS aim to use arrays which contain strings that are interchangeable. This may mean that some arrays only have 1 element.

"namegen.c"

The actual generation is performed within "namegen.c". For any given language, we need a function like this:

static byte MakeFinnishTownName(char *buf, uint32 seed, const char *last)
{
	//null terminates the string for strcat
	strecpy(buf, "", last);

	// Select randomly if town name should consists of one or two parts.
	if (SeedChance(0, 15, seed) >= 10) {
		strecat(buf, name_finnish_1[SeedChance( 2, lengthof(name_finnish_1), seed)], last);
	} else {
		strecat(buf, name_finnish_2a[SeedChance( 2, lengthof(name_finnish_2a), seed)], last);
		strecat(buf, name_finnish_2b[SeedChance(10, lengthof(name_finnish_2b), seed)], last);
	}

	return 0;
}

Exactly how to write this function is explained below. We do, however, need to call this method somehow. It's no good just adding this and then expecting OpenTTD to automagically know that it must give the user the option of your additional names. Find the array towards the end of "namegen.c" which looks somewhat like this:

TownNameGenerator * const _town_name_generators[] =
{
	MakeEnglishOriginalTownName,
	MakeFrenchTownName,
	MakeGermanTownName,
	MakeEnglishAdditionalTownName,
	MakeSpanishTownName,
	MakeSillyTownName,
	MakeSwedishTownName,
	MakeDutchTownName,
	MakeFinnishTownName,
	MakePolishTownName,
	MakeSlovakTownName,
	MakeNorwegianTownName,
	MakeHungarianTownName,
	MakeAustrianTownName,
	MakeRomanianTownName,
	MakeCzechTownName,
	MakeSwissTownName,
};

The function call must be placed at the bottom of this list. The reason for this is so that save game compatibility is not broken. Suppose you were to have a save game where you were using Dutch names. If you added a function call in this list before Dutch names, then your save game would suddenly have either your new names, or Swedish!

"lang/english.txt"

Find the townname section (i.e. do a find on "STR_TOWNNAME"). Add a line which follows the form STR_TOWNNAME_YOUR_LANGUAGE : Language. This will become evident as you look at the file. Ensure that you add this line underneath the others.

"strings_type.h"

The next change you need to make is in "openttd.h". Find the enum "SpecialStrings" and specifically the assignment "SPECSTR_TOWNNAME_LAST". This points to the last town name string, and needs to be adjusted so that it points to the string name that you set within "english.txt" with "SPEC" prefixed. You also need to add a line immediately above which defines your string within the enum. Follow the convention: "SPECSTR_TOWNNAME_YOUR_LANGUAGE".

An Example

English town names are generated by having an optional first segment, four mandatory middle segments and an optional final segment:

# optional
name_original_english_1 - "Great "

# mandatory
name_original_english_2 - "Wr"
name_original_english_3 - "ar"
name_original_english_4 - "ning"
name_original_english_5 - "ville"

#optional
name_original_english_6 - " Market"

Combining these gives: "Great Wrarningville Market".

In fact, the English town names then contain a filter so that certain unlikely or offensive are generated. "Wrar" is deemed awkward and is filtered to become "Inve". Therefore, the final generated name is "Great Inveningville Market". Perhaps even more unlikely!

Creating Your New Town Names

Essentially, it is important to invent some likely segments which can be used to piece together a word. I warn you that this is much more difficult than it sounds if you want realistic names to be generated (you may wish to consult an Atlas).

Add your segments to separate arrays within "table/namegen.h". Don't get this file mixed up with the "namegen.h" that exists in the root directory of the project. This is a separate file altogether. Copy the definition of the existing arrays, and add as many new arrays as you feel is right. If you end up with less than 3 arrays, perhaps your generation is too simple, and conversely, more than 8 is probably too complex. As described above, the English Original names use 6, and English Additional uses 8. The only way to find out whether it is or not is to test it though so don't just assume it is. As such before creating these arrays fully, you may want to skip to the next section and write the generation code.

You segments added to the arrays in "table/namegen.h" should look something like this (from English Original):

static const char *name_original_english_1[] = {
	"Great ",
	...
	"Fort ",
};

static const char *name_original_english_2[] = {
	"Wr",	
	...
	"W"
};

static const char *name_original_english_3[] = {
	"ar",
	...
	"en"
};

static const char *name_original_english_4[] = {
	"n",
	...
	"fing"
};

static const char *name_original_english_5[] = {
	"ville",
	...
	"burg",
};

static const char *name_original_english_6[] = {
	"-on-sea",
	...
	" Springs",
};

If you are going to make English based town names here are a few rules that you may find useful, based on my experiences of creating my own town names.

  1. The first part of the main word (in the name_original_english_2 array above) should end with a constanant. I ended up without 40 groups of letters in this array. Also if it has a vowel in the middle it can usually be stripped and this bit put in the next array.
  2. The second part (name_original_english_3 above) should start with a vowel, and then optionally have a constanant after it. I found that to get realistic English names there were mainly a? groups. Altogether I had around 25 groups of letters in this array.
  3. The third part (name_original_english_4 above) I am not really sure about, but mainly there are groups of three letters: a constanant, vowel and another constanant. Altogether I had around 15 groups here - I also added a blank entry ("",) because this bit isn't really needed.
  4. The last part doesn't really have a rule again, but if you look at an atlas for your area (Wikipedia may have a list of place names in your area) you should see what sort of endings are used.

The Generation Function

This is the core of the system. There are several functions available to assist in generation.

The first thing you need to do is null-terminate the "buf" character array. This is easily done by:

strecpy(buf, "", last);

If this step is omitted, openttd will hang/crash on the strecat function as this relies on being able to find the end of the string by searching for a "\0" which it will never find.

The SeedChance Function

This function takes 3 arguments. The first determines how many bits the "seed" integer should be shifted by. This seed is unique to a town and functions as a random number in this context. The bit-shifting allows for different parts of the seed to be used. The second argument determines what the maximum number generated should be. If this argument is 15 for example, then the number generated will be between 0 and 15. The third argument is the seed itself.

Suppose you wanted a 1 in 6 chance that a prefix was to be added to the string, you'd perhaps call this function like:

if (SeedChance(0,6,seed) == 0) { ... }

If you wanted to add any string in the array name_finnish_1[] then:

strecat(buf, name_finnish_1[SeedChance( 2, lengthof(name_finnish_1), seed)], last);

This concatenates any string with the index 0 - lengthof(name_finnish_1) to the buffer.

Filtering Words

The names are generated at random so there is a fairly good chance that an offensive word could be generated. As such I suggest you add some sort of filtering. There is a function in "namegen.c" called "ReplaceWords" that allows you to filter out certain words. If you want to replace any occurances of "A" with "B" you would use the following code:

ReplaceWords("A", "B", buf);

You should note that this function is case sensitive!

At this point, I suggest any readers should go read namegen.c and have a go at hacking it themselves!

Content.png

Warning
Do not pass strings that are not length 4 to the ReplaceWords function. There is currently a limitation in the code to prevent anything other than:

ReplaceWords("xxxx","yyyy",buf);

Also, it will only currently replace them if they are the first 4 characters of the entire name. This can be fixed if necessary, but currently we are going by the "If it aint broke, don't fix it" rule.

Personal tools