(svn r17612) -Feature: possibility to choose (randomise or enter custom) town name before its creation (original patch by Terkhen)

This commit is contained in:
smatz 2009-09-22 13:54:54 +00:00
parent 665864e5b0
commit 1da745c9ad
12 changed files with 281 additions and 132 deletions

View File

@ -1527,6 +1527,10 @@
RelativePath=".\..\src\townname_func.h"
>
</File>
<File
RelativePath=".\..\src\townname_type.h"
>
</File>
<File
RelativePath=".\..\src\town_type.h"
>

View File

@ -1524,6 +1524,10 @@
RelativePath=".\..\src\townname_func.h"
>
</File>
<File
RelativePath=".\..\src\townname_type.h"
>
</File>
<File
RelativePath=".\..\src\town_type.h"
>

View File

@ -287,6 +287,7 @@ timetable.h
toolbar_gui.h
town.h
townname_func.h
townname_type.h
town_type.h
track_func.h
track_type.h

View File

@ -2033,6 +2033,12 @@ STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP :{BLACK}Found to
STR_FOUND_TOWN_MANY_RANDOM_TOWNS :{BLACK}Many random towns
STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP :{BLACK}Cover the map with randomly placed towns
STR_FOUND_TOWN_NAME_TITLE :{YELLOW}Town name:
STR_FOUND_TOWN_NAME_EDITOR_TITLE :{BLACK}Enter town name
STR_FOUND_TOWN_NAME_EDITOR_HELP :{BLACK}Click to enter town name
STR_FOUND_TOWN_NAME_RANDOM_BUTTON :{BLACK}Random name
STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP :{BLACK}Generate new random name
STR_FOUND_TOWN_INITIAL_SIZE_TITLE :{YELLOW}Town size:
STR_FOUND_TOWN_INITIAL_SIZE_SMALL_BUTTON :{BLACK}Small
STR_FOUND_TOWN_INITIAL_SIZE_MEDIUM_BUTTON :{BLACK}Medium

View File

@ -38,6 +38,7 @@
#include "engine_base.h"
#include "strgen/strgen.h"
#include "gfx_func.h"
#include "townname_func.h"
#include "table/strings.h"
#include "table/control_codes.h"
@ -105,7 +106,7 @@ const char *GetStringPtr(StringID string)
* @param last
* @return a formatted string of char
*/
static char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const char *last)
char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const char *last)
{
if (GB(string, 0, 16) == 0) return GetStringWithArgs(buffr, STR_UNDEFINED, argv, last);
@ -913,27 +914,13 @@ static char *FormatString(char *buff, const char *str, int64 *argv, uint casei,
case SCC_TOWN_NAME: { // {TOWN}
const Town *t = Town::Get(GetInt32(&argv));
int64 temp[1];
assert(t != NULL);
temp[0] = t->townnameparts;
uint32 grfid = t->townnamegrfid;
if (t->name != NULL) {
buff = strecpy(buff, t->name, last);
} else if (grfid == 0) {
/* Original town name */
buff = GetStringWithArgs(buff, t->townnametype, temp, last);
} else {
/* Newgrf town name */
if (GetGRFTownName(grfid) != NULL) {
/* The grf is loaded */
buff = GRFTownNameGenerate(buff, t->townnamegrfid, t->townnametype, t->townnameparts, last);
} else {
/* Fallback to english original */
buff = GetStringWithArgs(buff, SPECSTR_TOWNNAME_ENGLISH, temp, last);
}
buff = GetTownName(buff, t, last);
}
break;
}

View File

@ -16,6 +16,7 @@
char *InlineString(char *buf, StringID string);
char *GetString(char *buffr, StringID string, const char *last);
char *GetStringWithArgs(char *buffr, uint string, int64 *argv, const char *last);
const char *GetStringPtr(StringID string);
void InjectDParam(uint amount);

View File

@ -212,7 +212,6 @@ HouseZonesBits GetTownRadiusGroup(const Town *t, TileIndex tile);
void SetTownRatingTestMode(bool mode);
uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t);
bool GenerateTowns(TownLayout layout);
bool GenerateTownName(uint32 *townnameparts);
/** Town actions of a company. */

View File

@ -50,6 +50,8 @@
#include "core/smallmap_type.hpp"
#include "core/pool_func.hpp"
#include "town.h"
#include "townname_func.h"
#include "townname_type.h"
#include "table/strings.h"
#include "table/town_land.h"
@ -1392,90 +1394,6 @@ void UpdateTownRadius(Town *t)
}
}
extern int _nb_orig_names;
/**
* Struct holding a parameters used to generate town name.
* Speeds things up a bit because these values are computed only once per name generation.
*/
struct TownNameParams {
uint32 grfid; ///< newgrf ID
uint16 townnametype; ///< town name style
bool grf; ///< true iff a newgrf is used to generate town name
TownNameParams(byte town_name)
{
this->grf = town_name >= _nb_orig_names;
this->grfid = this->grf ? GetGRFTownNameId(town_name - _nb_orig_names) : 0;
this->townnametype = this->grf ? GetGRFTownNameType(town_name - _nb_orig_names) : SPECSTR_TOWNNAME_START + town_name;
}
};
/**
* Verifies the town name is valid and unique.
* @param r random bits
* @param par town name parameters
* @return true iff name is valid and unique
*/
static bool VerifyTownName(uint32 r, const TownNameParams *par)
{
/* Reserve space for extra unicode character. We need to do this to be able
* to detect too long town name. */
char buf1[MAX_LENGTH_TOWN_NAME_BYTES + MAX_CHAR_LENGTH];
char buf2[MAX_LENGTH_TOWN_NAME_BYTES + MAX_CHAR_LENGTH];
SetDParam(0, r);
if (par->grf && par->grfid != 0) {
GRFTownNameGenerate(buf1, par->grfid, par->townnametype, r, lastof(buf1));
} else {
GetString(buf1, par->townnametype, lastof(buf1));
}
/* Check size and width */
if (strlen(buf1) >= MAX_LENGTH_TOWN_NAME_BYTES) return false;
const Town *t;
FOR_ALL_TOWNS(t) {
/* We can't just compare the numbers since
* several numbers may map to a single name. */
SetDParam(0, t->index);
GetString(buf2, STR_TOWN_NAME, lastof(buf2));
if (strcmp(buf1, buf2) == 0) return false;
}
return true;
}
/**
* Generates valid town name.
* @param townnameparts if a name is generated, it's stored there
* @return true iff a name was generated
*/
bool GenerateTownName(uint32 *townnameparts)
{
/* Do not set too low tries, since when we run out of names, we loop
* for #tries only one time anyway - then we stop generating more
* towns. Do not show it too high neither, since looping through all
* the other towns may take considerable amount of time (10000 is
* too much). */
int tries = 1000;
TownNameParams par(_settings_game.game_creation.town_name);
assert(townnameparts != NULL);
for (;;) {
uint32 r = InteractiveRandom();
if (!VerifyTownName(r, &par)) {
if (tries-- < 0) return false;
continue;
}
*townnameparts = r;
return true;
}
}
void UpdateTownMaxPass(Town *t)
{
t->max_pass = t->population >> 3;
@ -1526,6 +1444,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize
t->exclusive_counter = 0;
t->statues = 0;
extern int _nb_orig_names;
if (_settings_game.game_creation.town_name < _nb_orig_names) {
/* Original town name */
t->townnamegrfid = 0;
@ -1587,6 +1506,22 @@ static CommandCost TownCanBePlacedHere(TileIndex tile)
return CommandCost(EXPENSES_OTHER);
}
/**
* Verifies this custom name is unique. Only custom names are checked.
* @param name name to check
* @return is this name unique?
*/
static bool IsUniqueTownName(const char *name)
{
const Town *t;
FOR_ALL_TOWNS(t) {
if (t->name != NULL && strcmp(t->name, name) == 0) return false;
}
return true;
}
/** Create a new town.
* This obviously only works in the scenario editor. Function not removed
* as it might be possible in the future to fund your own town :)
@ -1615,7 +1550,14 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (size > TS_RANDOM) return CMD_ERROR;
if (layout > TL_RANDOM) return CMD_ERROR;
if (!VerifyTownName(townnameparts, &par)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
if (StrEmpty(text)) {
/* If supplied name is empty, townnameparts has to generate unique automatic name */
if (!VerifyTownName(townnameparts, &par)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
} else {
/* If name is not empty, it has to be unique custom name */
if (strlen(text) >= MAX_LENGTH_TOWN_NAME_BYTES) return CMD_ERROR;
if (!IsUniqueTownName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
}
/* Allocate town struct */
if (!Town::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_TOWNS);
@ -1630,19 +1572,21 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (flags & DC_EXEC) {
_generating_world = true;
UpdateNearestTownForRoadTiles(true);
Town *t;
if (random) {
const Town *t = CreateRandomTown(20, townnameparts, size, city, layout);
t = CreateRandomTown(20, townnameparts, size, city, layout);
if (t == NULL) {
cost = CommandCost(STR_ERROR_NO_SPACE_FOR_TOWN);
} else {
_new_town_id = t->index;
}
} else {
Town *t = new Town(tile);
t = new Town(tile);
DoCreateTown(t, tile, townnameparts, size, city, layout);
}
UpdateNearestTownForRoadTiles(false);
_generating_world = false;
if (t != NULL && !StrEmpty(text)) t->name = strdup(text);
}
return cost;
}
@ -2312,17 +2256,6 @@ void ClearTownHouse(Town *t, TileIndex tile)
if (eflags & BUILDING_HAS_4_TILES) DoClearTownHouseHelper(tile + TileDiffXY(1, 1), t, ++house);
}
static bool IsUniqueTownName(const char *name)
{
const Town *t;
FOR_ALL_TOWNS(t) {
if (t->name != NULL && strcmp(t->name, name) == 0) return false;
}
return true;
}
/** Rename a town (server-only).
* @param tile unused
* @param flags type of operation

View File

@ -34,6 +34,11 @@
#include "landscape.h"
#include "cargotype.h"
#include "tile_map.h"
#include "querystring_gui.h"
#include "window_func.h"
#include "string_func.h"
#include "townname_func.h"
#include "townname_type.h"
#include "table/sprites.h"
#include "table/strings.h"
@ -850,11 +855,7 @@ void CcFoundTown(bool success, TileIndex tile, uint32 p1, uint32 p2)
void CcFoundRandomTown(bool success, TileIndex tile, uint32 p1, uint32 p2)
{
if (success) {
tile = Town::Get(_new_town_id)->xy;
SndPlayTileFx(SND_1F_SPLAT, tile);
ScrollMainWindowToTile(tile);
}
if (success) ScrollMainWindowToTile(Town::Get(_new_town_id)->xy);
}
/** Widget numbers of town scenario editor window. */
@ -866,6 +867,9 @@ enum TownScenarioEditorWidgets {
TSEW_NEWTOWN,
TSEW_RANDOMTOWN,
TSEW_MANYRANDOMTOWNS,
TSEW_TOWNNAME_TEXT,
TSEW_TOWNNAME_EDITBOX,
TSEW_TOWNNAME_RANDOM,
TSEW_TOWNSIZE,
TSEW_SIZE_SMALL,
TSEW_SIZE_MEDIUM,
@ -895,6 +899,14 @@ static const NWidgetPart _nested_found_town_widgets[] = {
SetDataTip(STR_FOUND_TOWN_RANDOM_TOWN_BUTTON, STR_FOUND_TOWN_RANDOM_TOWN_TOOLTIP), SetPadding(0, 2, 1, 2),
NWidget(WWT_TEXTBTN, COLOUR_GREY, TSEW_MANYRANDOMTOWNS), SetMinimalSize(156, 12), SetFill(true, false),
SetDataTip(STR_FOUND_TOWN_MANY_RANDOM_TOWNS, STR_FOUND_TOWN_RANDOM_TOWNS_TOOLTIP), SetPadding(0, 2, 0, 2),
/* Town name selection. */
NWidget(NWID_VERTICAL),
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, TSEW_TOWNSIZE), SetMinimalSize(156, 14), SetDataTip(STR_FOUND_TOWN_NAME_TITLE, STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_WHITE, TSEW_TOWNNAME_EDITBOX), SetMinimalSize(156, 12), SetDataTip(STR_FOUND_TOWN_NAME_EDITOR_TITLE, STR_FOUND_TOWN_NAME_EDITOR_HELP),
NWidget(NWID_SPACER), SetMinimalSize(0, 3),
NWidget(WWT_TEXTBTN, COLOUR_GREY, TSEW_TOWNNAME_RANDOM), SetMinimalSize(78, 12), SetFill(true, false),
SetDataTip(STR_FOUND_TOWN_NAME_RANDOM_BUTTON, STR_FOUND_TOWN_NAME_RANDOM_TOOLTIP),
EndContainer(),
/* Town size selection. */
NWidget(NWID_HORIZONTAL),
NWidget(NWID_SPACER), SetFill(true, false),
@ -940,23 +952,41 @@ static const NWidgetPart _nested_found_town_widgets[] = {
};
/** Found a town window class. */
struct FoundTownWindow : Window {
struct FoundTownWindow : QueryStringBaseWindow {
private:
TownSize town_size; ///< Selected town size
TownLayout town_layout; ///< Selected town layout
bool city; ///< Are we building a city?
bool townnamevalid; ///< Is generated town name valid?
uint32 townnameparts; ///< Generated town name
TownNameParams params; ///< Town name parameters
public:
FoundTownWindow(const WindowDesc *desc, WindowNumber window_number) :
Window(),
QueryStringBaseWindow(MAX_LENGTH_TOWN_NAME_BYTES),
town_size(TS_MEDIUM),
town_layout(_settings_game.economy.town_layout),
city(false)
params(_settings_game.game_creation.town_name)
{
this->InitNested(desc, window_number);
this->RandomTownName();
this->UpdateButtons();
}
void RandomTownName()
{
this->townnamevalid = GenerateTownName(&this->townnameparts);
if (!this->townnamevalid) {
this->edit_str_buf[0] = '\0';
} else {
GetTownName(this->edit_str_buf, &this->params, this->townnameparts, &this->edit_str_buf[this->edit_str_size - 1]);
}
InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, MAX_LENGTH_TOWN_NAME_PIXELS);
this->SetFocusedWidget(TSEW_TOWNNAME_EDITBOX);
}
void UpdateButtons()
{
for (int i = TSEW_SIZE_SMALL; i <= TSEW_SIZE_RANDOM; i++) {
@ -974,19 +1004,27 @@ public:
void ExecuteFoundTownCommand(TileIndex tile, bool random, StringID errstr, CommandCallback cc)
{
uint32 townnameparts;
if (!GenerateTownName(&townnameparts)) {
ShowErrorMessage(STR_ERROR_TOO_MANY_TOWNS, errstr, 0, 0);
return;
const char *name = NULL;
if (!this->townnamevalid) {
name = this->edit_str_buf;
} else {
/* If user changed the name, send it */
char buf[MAX_LENGTH_TOWN_NAME_BYTES];
GetTownName(buf, &this->params, this->townnameparts, lastof(buf));
if (strcmp(buf, this->edit_str_buf) != 0) name = this->edit_str_buf;
}
DoCommandP(tile, this->town_size | this->city << 2 | this->town_layout << 3 | random << 6,
townnameparts, CMD_FOUND_TOWN | CMD_MSG(errstr), cc);
bool success = DoCommandP(tile, this->town_size | this->city << 2 | this->town_layout << 3 | random << 6,
townnameparts, CMD_FOUND_TOWN | CMD_MSG(errstr), cc, name);
if (success) this->RandomTownName();
}
virtual void OnPaint()
{
this->DrawWidgets();
this->DrawEditBox(TSEW_TOWNNAME_EDITBOX);
}
virtual void OnClick(Point pt, int widget)
@ -1001,6 +1039,10 @@ public:
this->ExecuteFoundTownCommand(0, true, STR_ERROR_CAN_T_GENERATE_TOWN, CcFoundRandomTown);
break;
case TSEW_TOWNNAME_RANDOM:
this->RandomTownName();
break;
case TSEW_MANYRANDOMTOWNS:
this->HandleButtonClick(TSEW_MANYRANDOMTOWNS);
@ -1039,6 +1081,18 @@ public:
this->SetDirty();
}
virtual void OnMouseLoop()
{
this->HandleEditBox(TSEW_TOWNNAME_EDITBOX);
}
virtual EventState OnKeyPress(uint16 key, uint16 keycode)
{
EventState state;
this->HandleEditBoxKey(TSEW_TOWNNAME_EDITBOX, key, keycode, state);
return state;
}
virtual void OnPlaceObject(Point pt, TileIndex tile)
{
this->ExecuteFoundTownCommand(tile, false, STR_ERROR_CAN_T_FOUND_TOWN_HERE, CcFoundTown);

View File

@ -12,11 +12,125 @@
#include "stdafx.h"
#include "townname_func.h"
#include "string_func.h"
#include "townname_func.h"
#include "townname_type.h"
#include "town.h"
#include "core/alloc_func.hpp"
#include "strings_func.h"
#include "table/townname.h"
/**
* Initializes this struct from town data
* @param t town for which we will be printing name later
*/
TownNameParams::TownNameParams(const Town *t) :
grfid(t->townnamegrfid), // by default, use supplied data
type(t->townnametype)
{
if (t->townnamegrfid != 0 && GetGRFTownName(t->townnamegrfid) == NULL) {
/* Fallback to english original */
this->grfid = 0;
this->type = SPECSTR_TOWNNAME_ENGLISH;
return;
}
}
/**
* Fills buffer with specified town name
* @param buff buffer start
* @param par town name parameters
* @param townnameparts 'encoded' town name
* @param last end of buffer
* @return pointer to terminating '\0'
*/
char *GetTownName(char *buff, const TownNameParams *par, uint32 townnameparts, const char *last)
{
if (par->grfid == 0) {
int64 temp[1] = { townnameparts };
return GetStringWithArgs(buff, par->type, temp, last);
}
return GRFTownNameGenerate(buff, par->grfid, par->type, townnameparts, last);
}
/**
* Fills buffer with town's name
* @param buff buffer start
* @param t we want to get name of this town
* @param last end of buffer
* @return pointer to terminating '\0'
*/
char *GetTownName(char *buff, const Town *t, const char *last)
{
TownNameParams par(t);
return GetTownName(buff, &par, t->townnameparts, last);
}
/**
* Verifies the town name is valid and unique.
* @param r random bits
* @param par town name parameters
* @return true iff name is valid and unique
*/
bool VerifyTownName(uint32 r, const TownNameParams *par)
{
/* reserve space for extra unicode character and terminating '\0' */
char buf1[MAX_LENGTH_TOWN_NAME_BYTES + MAX_CHAR_LENGTH];
char buf2[MAX_LENGTH_TOWN_NAME_BYTES + MAX_CHAR_LENGTH];
GetTownName(buf1, par, r, lastof(buf1));
/* Check size and width */
if (strlen(buf1) >= MAX_LENGTH_TOWN_NAME_BYTES) return false;
const Town *t;
FOR_ALL_TOWNS(t) {
/* We can't just compare the numbers since
* several numbers may map to a single name. */
const char *buf = t->name;
if (buf == NULL) {
GetTownName(buf2, t, lastof(buf2));
buf = buf2;
}
if (strcmp(buf1, buf2) == 0) return false;
}
return true;
}
/**
* Generates valid town name.
* @param townnameparts if a name is generated, it's stored there
* @return true iff a name was generated
*/
bool GenerateTownName(uint32 *townnameparts)
{
/* Do not set too low tries, since when we run out of names, we loop
* for #tries only one time anyway - then we stop generating more
* towns. Do not show it too high neither, since looping through all
* the other towns may take considerable amount of time (10000 is
* too much). */
TownNameParams par(_settings_game.game_creation.town_name);
for (int i = 1000; i != 0; i--) {
uint32 r = InteractiveRandom();
if (!VerifyTownName(r, &par)) continue;
*townnameparts = r;
return true;
}
return false;
}
/**
* Generates a number from given seed.
* @param shift_by number of bits seed is shifted to the right

View File

@ -9,9 +9,13 @@
/** @file townname_func.h Town name generator stuff. */
#ifndef NAMEGEN_FUNC_H
#define NAMEGEN_FUNC_H
#ifndef TOWNNAME_FUNC_H
#define TOWNNAME_FUNC_H
char *GenerateTownNameString(char *buf, const char *last, size_t lang, uint32 seed);
char *GetTownName(char *buff, const struct TownNameParams *par, uint32 townnameparts, const char *last);
char *GetTownName(char *buff, const struct Town *t, const char *last);
bool VerifyTownName(uint32 r, const struct TownNameParams *par);
bool GenerateTownName(uint32 *townnameparts);
#endif /* NAMEGEN_FUNC_H */
#endif /* TOWNNAME_FUNC_H */

42
src/townname_type.h Normal file
View File

@ -0,0 +1,42 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file townname_type.h
* Definition of structures used for generating town names.
*/
#ifndef TOWNNAME_TYPE_H
#define TOWNNAME_TYPE_H
#include "newgrf_townname.h"
/**
* Struct holding a parameters used to generate town name.
* Speeds things up a bit because these values are computed only once per name generation.
*/
struct TownNameParams {
uint32 grfid; ///< newgrf ID (0 if not used)
uint16 type; ///< town name style
/**
* Initializes this struct from language ID
* @param town_name town name 'language' ID
*/
TownNameParams(byte town_name)
{
extern int _nb_orig_names;
bool grf = town_name >= _nb_orig_names;
this->grfid = grf ? GetGRFTownNameId(town_name - _nb_orig_names) : 0;
this->type = grf ? GetGRFTownNameType(town_name - _nb_orig_names) : SPECSTR_TOWNNAME_START + town_name;
}
TownNameParams(const struct Town *t);
};
#endif /* TOWNNAME_TYPE_H */