(svn r5946) -Add: merged the TGP branch to mainline. TGP adds:

- New optional landscape generator (TerraGenesis Perlin)
  - Load heightmaps (either BMP or PNG)
  - Progress dialog while generating worlds (no longer a 'hanging' screen)
  - New dialogs for NewGame, Create Scenario and Play Heightmap
    - Easier to configure your landscape
    - More things to configure (tree-placer, ..)
  - Speedup of world generation
  - New console command 'restart': restart the map EXACTLY as it was when you
      first started it (needs a game made after or with this commit)
  - New console command 'getseed': get the seed of your map and share it with
      others (of course only works with generated maps)
  - Many new, world generation related, things
  - Many internal cleanups and rewrites
  Many tnx to those people who helped making this:
     Belugas, DaleStan, glx, KUDr, RichK67, Rubidium, and TrueLight (alfabetic)
  Many tnx to those who helped testing:
     Arnau, Bjarni, and tokai (alfabetic)
  And to all other people who helped testing and sending comments / bugs
  Stats: 673 lines changed, 3534 new lines, 79 new strings
This commit is contained in:
truelight 2006-08-19 10:00:30 +00:00
parent 83d56d6d79
commit 10b842bddc
57 changed files with 4072 additions and 616 deletions

View File

@ -639,6 +639,7 @@ SRCS += aircraft_gui.c
SRCS += airport.c
SRCS += airport_gui.c
SRCS += aystar.c
SRCS += bmp.c
SRCS += bridge_gui.c
SRCS += bridge_map.c
SRCS += callback_table.c
@ -661,9 +662,12 @@ SRCS += engine.c
SRCS += engine_gui.c
SRCS += fileio.c
SRCS += fios.c
SRCS += genworld.c
SRCS += genworld_gui.c
SRCS += gfx.c
SRCS += gfxinit.c
SRCS += graph_gui.c
SRCS += heightmap.c
SRCS += industry_cmd.c
SRCS += industry_gui.c
SRCS += intro_gui.c
@ -732,6 +736,7 @@ SRCS += strings.c
SRCS += subsidy_gui.c
SRCS += terraform_gui.c
SRCS += texteff.c
SRCS += tgp.c
SRCS += thread.c
SRCS += tile.c
SRCS += town_cmd.c

378
bmp.c Normal file
View File

@ -0,0 +1,378 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "gfx.h"
#include "bmp.h"
#include "macros.h"
void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) {
buffer->pos = -1;
buffer->file = file;
buffer->read = 0;
buffer->real_pos = ftell(file);
}
static inline void AdvanceBuffer(BmpBuffer *buffer)
{
buffer->read = fread(buffer->data, 1, BMP_BUFFER_SIZE, buffer->file);
buffer->pos = 0;
}
static inline bool EndOfBuffer(BmpBuffer *buffer)
{
if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
return buffer->pos == buffer->read;
}
static inline byte ReadByte(BmpBuffer *buffer)
{
if (buffer->pos == buffer->read || buffer->pos < 0) AdvanceBuffer(buffer);
buffer->real_pos++;
return buffer->data[buffer->pos++];
}
static inline uint16 ReadWord(BmpBuffer *buffer)
{
uint16 var = ReadByte(buffer);
return var | (ReadByte(buffer) << 8);
}
static inline uint32 ReadDword(BmpBuffer *buffer)
{
uint32 var = ReadWord(buffer);
return var | (ReadWord(buffer) << 16);
}
static inline void SkipBytes(BmpBuffer *buffer, int bytes)
{
int i;
for (i = 0; i < bytes; i++) ReadByte(buffer);
}
static inline void SetStreamOffset(BmpBuffer *buffer, int offset)
{
fseek(buffer->file, offset, SEEK_SET);
buffer->pos = -1;
buffer->real_pos = offset;
AdvanceBuffer(buffer);
}
/**
* Reads a 1 bpp uncompressed bitmap
* The bitmap is converted to a 8 bpp bitmap
*/
static inline bool BmpRead1(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint x, y, i;
byte pad = GB(4 - info->width / 8, 0, 2);
byte *pixel_row;
byte b;
for (y = info->height; y > 0; y--) {
x = 0;
pixel_row = &data->bitmap[(y - 1) * info->width];
while (x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
b = ReadByte(buffer);
for (i = 8; i > 0; i--) {
if (x < info->width) *pixel_row++ = GB(b, i - 1, 1);
x++;
}
}
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
}
return true;
}
/**
* Reads a 4 bpp uncompressed bitmap
* The bitmap is converted to a 8 bpp bitmap
*/
static inline bool BmpRead4(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint x, y;
byte pad = GB(4 - info->width / 2, 0, 2);
byte *pixel_row;
byte b;
for (y = info->height; y > 0; y--) {
x = 0;
pixel_row = &data->bitmap[(y - 1) * info->width];
while (x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
b = ReadByte(buffer);
*pixel_row++ = GB(b, 4, 4);
x++;
if (x < info->width) {
*pixel_row++ = GB(b, 0, 4);
x++;
}
}
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
}
return true;
}
/**
* Reads a 4-bit RLE compressed bitmap
* The bitmap is converted to a 8 bpp bitmap
*/
static inline bool BmpRead4Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint i;
uint x = 0;
uint y = info->height - 1;
byte n, c, b;
byte *pixel = &data->bitmap[y * info->width];
while (y != 0 || x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
n = ReadByte(buffer);
c = ReadByte(buffer);
if (n == 0) {
switch (c) {
case 0: // end of line
x = 0;
pixel = &data->bitmap[--y * info->width];
break;
case 1: // end of bitmap
x = info->width;
y = 0;
pixel = NULL;
break;
case 2: // delta
x += ReadByte(buffer);
i = ReadByte(buffer);
if (x >= info->width || (y == 0 && i > 0)) return false;
y -= i;
pixel = &data->bitmap[y * info->width + x];
break;
default: // uncompressed
i = 0;
while (i++ < c) {
if (EndOfBuffer(buffer) || x >= info->width) return false;
b = ReadByte(buffer);
*pixel++ = GB(b, 4, 4);
x++;
if (x < info->width && i++ < c) {
*pixel++ = GB(b, 0, 4);
x++;
}
}
/* Padding for 16 bit align */
SkipBytes(buffer, ((c + 1) / 2) % 2);
break;
}
} else {
i = 0;
while (i++ < n) {
if (EndOfBuffer(buffer) || x >= info->width) return false;
*pixel++ = GB(c, 4, 4);
x++;
if (x < info->width && i++ < n) {
*pixel++ = GB(c, 0, 4);
x++;
}
}
}
}
return true;
}
/**
* Reads a 8 bpp bitmap
*/
static inline bool BmpRead8(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint i;
uint y;
byte pad = GB(4 - info->width, 0, 2);
byte *pixel;
for (y = info->height; y > 0; y--) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
pixel = &data->bitmap[(y - 1) * info->width];
for (i = 0; i < info->width; i++) *pixel++ = ReadByte(buffer);
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
}
return true;
}
/**
* Reads a 8-bit RLE compressed bpp bitmap
*/
static inline bool BmpRead8Rle(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint i;
uint x = 0;
uint y = info->height - 1;
byte n, c;
byte *pixel = &data->bitmap[y * info->width];
while (y != 0 || x < info->width) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
n = ReadByte(buffer);
c = ReadByte(buffer);
if (n == 0) {
switch (c) {
case 0: // end of line
x = 0;
pixel = &data->bitmap[--y * info->width];
break;
case 1: // end of bitmap
x = info->width;
y = 0;
pixel = NULL;
break;
case 2: // delta
x += ReadByte(buffer);
i = ReadByte(buffer);
if (x >= info->width || (y == 0 && i > 0)) return false;
y -= i;
pixel = &data->bitmap[y * info->width + x];
break;
default: // uncompressed
if ((x += c) > info->width) return false;
for (i = 0; i < c; i++) *pixel++ = ReadByte(buffer);
/* Padding for 16 bit align */
SkipBytes(buffer, c % 2);
break;
}
} else {
for (i = 0; i < n; i++) {
if (x >= info->width) return false;
*pixel++ = c;
x++;
}
}
}
return true;
}
/**
* Reads a 24 bpp uncompressed bitmap
*/
static inline bool BmpRead24(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint x, y;
byte pad = GB(4 - info->width * 3, 0, 2);
byte *pixel_row;
for (y = info->height; y > 0; y--) {
pixel_row = &data->bitmap[(y - 1) * info->width * 3];
for (x = 0; x < info->width; x++) {
if (EndOfBuffer(buffer)) return false; // the file is shorter than expected
*(pixel_row + 2) = ReadByte(buffer); // green
*(pixel_row + 1) = ReadByte(buffer); // blue
*pixel_row = ReadByte(buffer); // red
pixel_row += 3;
}
/* Padding for 32 bit align */
SkipBytes(buffer, pad);
}
return true;
}
/*
* Reads bitmap headers, and palette (if any)
*/
bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
uint32 header_size;
assert(info != NULL);
/* Reading BMP header */
if (ReadWord(buffer) != 0x4D42) return false; // signature should be 'BM'
SkipBytes(buffer, 8); // skip file size and reserved
info->offset = ReadDword(buffer);
/* Reading info header */
header_size = ReadDword(buffer);
if (header_size < 12) return false; // info header should be at least 12 bytes long
info->os2_bmp = (header_size == 12); // OS/2 1.x or windows 2.x info header is 12 bytes long
if (info->os2_bmp) {
info->width = ReadWord(buffer);
info->height = ReadWord(buffer);
header_size -= 8;
} else {
info->width = ReadDword(buffer);
info->height = ReadDword(buffer);
header_size -= 12;
}
if (ReadWord(buffer) != 1) return false; // BMP can have only 1 plane
info->bpp = ReadWord(buffer);
if (info->bpp != 1 && info->bpp != 4 && info->bpp != 8 && info->bpp != 24) {
/* Only 1 bpp, 4 bpp, 8bpp and 24 bpp bitmaps are supported */
return false;
}
/* Reads compression method if available in info header*/
if ((header_size -= 4) >= 4) {
info->compression = ReadDword(buffer);
header_size -= 4;
}
/* Only 4-bit and 8-bit rle compression is supported */
if (info->compression > 2 || (info->compression > 0 && !(info->bpp == 4 || info->bpp == 8))) return false;
if (info->bpp <= 8) {
uint i;
/* Reads number of colors if available in info header */
if (header_size >= 16) {
SkipBytes(buffer, 12); // skip image size and resolution
info->palette_size = ReadDword(buffer); // number of colors in palette
SkipBytes(buffer, header_size - 16); // skip the end of info header
}
if (info->palette_size == 0) info->palette_size = 1 << info->bpp;
data->palette = calloc(info->palette_size, sizeof(*(data->palette)));
if (data->palette == NULL) return false;
for (i = 0; i < info->palette_size; i++) {
data->palette[i].b = ReadByte(buffer);
data->palette[i].g = ReadByte(buffer);
data->palette[i].r = ReadByte(buffer);
if (!info->os2_bmp) SkipBytes(buffer, 1); // unused
}
}
return buffer->real_pos <= info->offset;
}
/*
* Reads the bitmap
* 1 bpp and 4 bpp bitmaps are converted to 8 bpp bitmaps
*/
bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data)
{
assert(info != NULL && data != NULL);
data->bitmap = calloc(info->width * info->height, ((info->bpp == 24) ? 3 : 1) * sizeof(byte));
if (data->bitmap == NULL) return false;
/* Load image */
SetStreamOffset(buffer, info->offset);
switch (info->compression) {
case 0: // no compression
switch (info->bpp) {
case 1: return BmpRead1(buffer, info, data);
case 4: return BmpRead4(buffer, info, data);
case 8: return BmpRead8(buffer, info, data);
case 24: return BmpRead24(buffer, info, data);
default: NOT_REACHED(); return false;
}
case 1: return BmpRead8Rle(buffer, info, data); // 8-bit RLE compression
case 2: return BmpRead4Rle(buffer, info, data); // 4-bit RLE compression
default: NOT_REACHED(); return false;
}
}
void BmpDestroyData(BmpData *data)
{
assert(data != NULL);
free(data->palette);
free(data->bitmap);
}

36
bmp.h Normal file
View File

@ -0,0 +1,36 @@
/* $Id$ */
#ifndef BMP_H
#define BMP_H
typedef struct {
uint32 offset; ///< offset of bitmap data from .bmp file begining
uint32 width; ///< bitmap width
uint32 height; ///< bitmap height
bool os2_bmp; ///< true if OS/2 1.x or windows 2.x bitmap
uint16 bpp; ///< bits per pixel
uint32 compression; ///< compression method (0 = none, 1 = 8-bit RLE, 2 = 4-bit RLE)
uint32 palette_size; ///< number of colors in palette
} BmpInfo;
typedef struct {
Colour *palette;
byte *bitmap;
} BmpData;
#define BMP_BUFFER_SIZE 1024
typedef struct {
byte data[BMP_BUFFER_SIZE];
int pos;
int read;
FILE *file;
uint real_pos;
} BmpBuffer;
void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file);
bool BmpReadHeader(BmpBuffer *buffer, BmpInfo *info, BmpData *data);
bool BmpReadBitmap(BmpBuffer *buffer, BmpInfo *info, BmpData *data);
void BmpDestroyData(BmpData *data);
#endif /* BMP_H */

View File

@ -15,6 +15,7 @@
#include "variables.h"
#include "table/sprites.h"
#include "unmovable_map.h"
#include "genworld.h"
typedef struct TerraformerHeightMod {
TileIndex tile;
@ -693,22 +694,28 @@ static void TileLoop_Clear(TileIndex tile)
void GenerateClearTile(void)
{
uint i;
uint i, gi;
TileIndex tile;
/* add hills */
/* add rough tiles */
i = ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
gi = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
SetGeneratingWorldProgress(GWP_ROUGH_ROCKY, gi + i);
do {
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
tile = RandomTile();
if (IsTileType(tile, MP_CLEAR)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
} while (--i);
/* add grey squares */
i = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
/* add rocky tiles */
i = gi;
do {
uint32 r = Random();
tile = RandomTileSeed(r);
if (IsTileType(tile, MP_CLEAR)) {
IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) {
uint j = GB(r, 16, 4) + 5;
for (;;) {
TileIndex tile_new;
@ -717,7 +724,7 @@ void GenerateClearTile(void)
do {
if (--j == 0) goto get_out;
tile_new = tile + TileOffsByDir(GB(Random(), 0, 2));
} while (!IsTileType(tile_new, MP_CLEAR));
} while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT));
tile = tile_new;
}
get_out:;

View File

@ -10,6 +10,7 @@
#include "player.h"
#include "network.h"
#include "variables.h"
#include "genworld.h"
const char* _cmd_text = NULL;
@ -456,7 +457,7 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback,
_docommand_recursive = 1;
// cost estimation only?
if (_shift_pressed && IsLocalPlayer() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR))) {
if (!IsGeneratingWorld() && _shift_pressed && IsLocalPlayer() && !(cmd & (CMD_NETWORK_COMMAND | CMD_SHOW_NO_ERROR))) {
// estimate the cost.
res = proc(tile, flags, p1, p2);
if (CmdFailed(res)) {

View File

@ -20,6 +20,7 @@
#include "station.h"
#include "strings.h"
#include "screenshot.h"
#include "genworld.h"
#include "date.h"
#ifdef ENABLE_NETWORK
@ -871,17 +872,44 @@ DEF_CONSOLE_CMD(ConEchoC)
return true;
}
extern void SwitchMode(int new_mode);
DEF_CONSOLE_CMD(ConNewGame)
{
if (argc == 0) {
IConsoleHelp("Start a new game. Usage: 'newgame'");
IConsoleHelp("The server can force a new game using 'newgame', any client using it will part and start a single-player game");
IConsoleHelp("Start a new game. Usage: 'newgame [seed]'");
IConsoleHelp("The server can force a new game using 'newgame'; any client joined will rejoin after the server is done generating the new game.");
return true;
}
GenRandomNewGame(Random(), InteractiveRandom());
StartNewGameWithoutGUI((argc == 2) ? (uint)atoi(argv[1]) : GENERATE_NEW_SEED);
return true;
}
extern void SwitchMode(int new_mode);
DEF_CONSOLE_CMD(ConRestart)
{
if (argc == 0) {
IConsoleHelp("Restart game. Usage: 'restart'");
IConsoleHelp("Restarts a game. It tries to reproduce the exact same map as the game started with.");
return true;
}
/* Don't copy the _newgame pointers to the real pointers, so call SwitchMode directly */
_patches.map_x = MapLogX();
_patches.map_y = FindFirstBit(MapSizeY());
SwitchMode(SM_NEWGAME);
return true;
}
DEF_CONSOLE_CMD(ConGetSeed)
{
if (argc == 0) {
IConsoleHelp("Returns the seed used to create this game. Usage: 'getseed'");
IConsoleHelp("The seed can be used to reproduce the exact same map as the game started with.");
return true;
}
IConsolePrintF(_icolour_def, "Generation Seed: %u", _patches.generation_seed);
return true;
}
@ -1413,6 +1441,8 @@ void IConsoleStdLibRegister(void)
IConsoleCmdRegister("list_vars", ConListVariables);
IConsoleCmdRegister("list_aliases", ConListAliases);
IConsoleCmdRegister("newgame", ConNewGame);
IConsoleCmdRegister("restart", ConRestart);
IConsoleCmdRegister("getseed", ConGetSeed);
IConsoleCmdRegister("quit", ConExit);
IConsoleCmdRegister("resetengines", ConResetEngines);
IConsoleCmdRegister("return", ConReturn);

View File

@ -30,6 +30,10 @@ enum {
ENGINE_PREVIEWING = 4,
};
enum {
YEAR_ENGINE_AGING_STOPS = 2050,
};
/** Bitmasked values of what type of cargo is refittable for the given vehicle-type.
* This coupled with the landscape information (_landscape_global_cargo_mask) gives
* us exactly what is refittable and what is not */
@ -135,6 +139,8 @@ void StartupEngines(void)
{
Engine *e;
const EngineInfo *ei;
/* Aging of vehicles stops, so account for that when starting late */
const uint16 aging_date = min(_date, ConvertYMDToDate(YEAR_ENGINE_AGING_STOPS, 0, 1));
SetupEngineNames();
@ -152,7 +158,7 @@ void StartupEngines(void)
r = Random();
e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
if (e->intro_date <= _date) {
e->age = (_date - e->intro_date) >> 5;
e->age = (aging_date - e->intro_date) >> 5;
e->player_avail = (byte)-1;
e->flags |= ENGINE_AVAILABLE;
}
@ -237,7 +243,7 @@ void EnginesDailyLoop(void)
{
EngineID i;
if (_cur_year >= 2050) return;
if (_cur_year >= YEAR_ENGINE_AGING_STOPS) return;
for (i = 0; i != lengthof(_engines); i++) {
Engine *e = &_engines[i];
@ -358,7 +364,7 @@ void EnginesMonthlyLoop(void)
{
Engine *e;
if (_cur_year < 2050) {
if (_cur_year < YEAR_ENGINE_AGING_STOPS) {
for (e = _engines; e != endof(_engines); e++) {
// Age the vehicle
if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {

48
fios.c
View File

@ -10,6 +10,7 @@
#include "string.h"
#include "variables.h"
#include "functions.h"
#include "heightmap.h"
#include "table/strings.h"
#include "fios.h"
#include <sys/types.h>
@ -135,7 +136,10 @@ char *FiosBrowseTo(const FiosItem *item)
case FIOS_TYPE_FILE:
case FIOS_TYPE_OLDFILE:
case FIOS_TYPE_SCENARIO:
case FIOS_TYPE_OLD_SCENARIO: {
case FIOS_TYPE_OLD_SCENARIO:
case FIOS_TYPE_PNG:
case FIOS_TYPE_BMP:
{
static char str_buffr[512];
#if defined(__MORPHOS__) || defined(__AMIGAOS__)
@ -357,15 +361,45 @@ static byte FiosGetScenarioListCallback(int mode, const char *file, const char *
*/
FiosItem *FiosGetScenarioList(int mode)
{
static char *fios_scn_path = NULL;
static char *_fios_scn_path = NULL;
/* Copy the default path on first run or on 'New Game' */
if (mode == SLD_NEW_GAME || fios_scn_path == NULL) {
if (fios_scn_path == NULL) fios_scn_path = malloc(MAX_PATH);
ttd_strlcpy(fios_scn_path, _path.scenario_dir, MAX_PATH);
if (_fios_scn_path == NULL) {
_fios_scn_path = malloc(MAX_PATH);
ttd_strlcpy(_fios_scn_path, _path.scenario_dir, MAX_PATH);
}
_fios_path = fios_scn_path;
_fios_path = _fios_scn_path;
return FiosGetFileList(mode, &FiosGetScenarioListCallback);
}
static byte FiosGetHeightmapListCallback(int mode, const char *file, const char *ext, char *title)
{
/* Show heightmap files
* .PNG PNG Based heightmap files
* .BMP BMP Based heightmap files
*/
#ifdef WITH_PNG
if (strcasecmp(ext, ".png") == 0) return FIOS_TYPE_PNG;
#endif /* WITH_PNG */
if (strcasecmp(ext, ".bmp") == 0) return FIOS_TYPE_BMP;
return FIOS_TYPE_INVALID;
}
// Get a list of Heightmaps
FiosItem *FiosGetHeightmapList(int mode)
{
static char *_fios_hmap_path = NULL;
if (_fios_hmap_path == NULL) {
_fios_hmap_path = malloc(MAX_PATH);
strcpy(_fios_hmap_path, _path.heightmap_dir);
}
_fios_path = _fios_hmap_path;
return FiosGetFileList(mode, &FiosGetHeightmapListCallback);
}

4
fios.h
View File

@ -20,6 +20,8 @@ enum {
FIOS_TYPE_SCENARIO = 5,
FIOS_TYPE_OLD_SCENARIO = 6,
FIOS_TYPE_DIRECT = 7,
FIOS_TYPE_PNG = 8,
FIOS_TYPE_BMP = 9,
FIOS_TYPE_INVALID = 255,
};
@ -32,6 +34,8 @@ extern int _saveload_mode; // defined in misc_gui.c
FiosItem *FiosGetSavegameList(int mode);
// Get a list of scenarios
FiosItem *FiosGetScenarioList(int mode);
// Get a list of Heightmaps
FiosItem *FiosGetHeightmapList(int mode);
// Free the list of savegames
void FiosFreeSavegameList(void);
// Browse to. Returns a filename w/path if we reached a file.

View File

@ -204,8 +204,6 @@ TileIndex AdjustTileCoordRandomly(TileIndex a, byte rng);
void AfterLoadTown(void);
void UpdatePatches(void);
void GenRandomNewGame(uint32 rnd1, uint32 rnd2);
void StartScenarioEditor(uint32 rnd1, uint32 rnd2);
void AskExitGame(void);
void AskExitToGameMenu(void);
@ -216,11 +214,12 @@ StringID RemapOldStringID(StringID s);
void UpdateViewportSignPos(ViewportSign *sign, int left, int top, StringID str);
enum {
SLD_LOAD_GAME = 0,
SLD_LOAD_SCENARIO = 1,
SLD_SAVE_GAME = 2,
SLD_SAVE_SCENARIO = 3,
SLD_NEW_GAME = 4,
SLD_LOAD_GAME,
SLD_LOAD_SCENARIO,
SLD_SAVE_GAME,
SLD_SAVE_SCENARIO,
SLD_LOAD_HEIGHTMAP,
SLD_NEW_GAME,
};
void ShowSaveLoadDialog(int mode);

273
genworld.c Normal file
View File

@ -0,0 +1,273 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "functions.h"
#include "player.h"
#include "table/sprites.h"
#include "variables.h"
#include "thread.h"
#include "genworld.h"
#include "gfx.h"
#include "gfxinit.h"
#include "gui.h"
#include "network.h"
#include "debug.h"
#include "settings.h"
#include "heightmap.h"
void GenerateLandscape(byte mode);
void GenerateClearTile(void);
void GenerateIndustries(void);
void GenerateUnmovables(void);
bool GenerateTowns(void);
void GenerateTrees(void);
void StartupEconomy(void);
void StartupPlayers(void);
void StartupDisasters(void);
void InitializeGame(int mode, uint size_x, uint size_y);
void ConvertGroundTilesIntoWaterTiles(void);
/* Please only use this variable in genworld.h and genworld.c and
* nowhere else. For speed improvements we need it to be global, but
* in no way the meaning of it is to use it anywhere else besides
* in the genworld.h and genworld.c! -- TrueLight */
gw_info _gw;
/**
* Set the status of the Paint flag.
* If it is true, the thread will hold with any futher generating till
* the drawing of the screen is done. This is handled by
* SetGeneratingWorldProgress(), so calling that function will stall
* from time to time.
*/
void SetGeneratingWorldPaintStatus(bool status)
{
_gw.wait_for_draw = status;
}
/**
* Returns true if the thread wants the main program to do a (full) paint.
* If this returns false, please do not update the screen. Because we are
* writing in a thread, it can cause damaged data (reading and writing the
* same tile at the same time).
*/
bool IsGeneratingWorldReadyForPaint(void)
{
/* If we are in quit_thread mode, ignore this and always return false. This
* forces the screen to not be drawn, and the GUI not to wait for a draw. */
if (!_gw.active || _gw.quit_thread || !_gw.threaded) return false;
return _gw.wait_for_draw;
}
/**
* Tells if the world generation is done in a thread or not.
*/
bool IsGenerateWorldThreaded(void)
{
return _gw.threaded && !_gw.quit_thread;
}
/**
* The internal, real, generate function.
*/
static void *_GenerateWorld(void *arg)
{
_generating_world = true;
if (_network_dedicated) DEBUG(net, 0)("Generating map, please wait...");
/* Set the Random() seed to generation_seed so we produce the same map with the same seed */
if (_patches.generation_seed == GENERATE_NEW_SEED) _patches.generation_seed = _patches_newgame.generation_seed = InteractiveRandom();
_random_seeds[0][0] = _random_seeds[0][1] = _patches.generation_seed;
SetGeneratingWorldProgress(GWP_MAP_INIT, 2);
SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
// Must start economy early because of the costs.
StartupEconomy();
// Don't generate landscape items when in the scenario editor.
if (_gw.mode == GW_EMPTY) {
SetGeneratingWorldProgress(GWP_UNMOVABLE, 1);
/* Make the map the height of the patch setting */
if (_game_mode != GM_MENU) FlatEmptyWorld(_patches.se_flat_world_height);
ConvertGroundTilesIntoWaterTiles();
IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
} else {
GenerateLandscape(_gw.mode);
GenerateClearTile();
// only generate towns, tree and industries in newgame mode.
if (_game_mode != GM_EDITOR) {
GenerateTowns();
GenerateIndustries();
GenerateUnmovables();
GenerateTrees();
}
}
// These are probably pointless when inside the scenario editor.
SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
StartupPlayers();
IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
StartupEngines();
IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
StartupDisasters();
_generating_world = false;
// No need to run the tile loop in the scenario editor.
if (_gw.mode != GW_EMPTY) {
uint i;
SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500);
for (i = 0; i < 0x500; i++) {
RunTileLoop();
IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP);
}
}
ResetObjectToPlace();
_local_player = _gw.lp;
SetGeneratingWorldProgress(GWP_GAME_START, 1);
/* Call any callback */
if (_gw.proc != NULL) _gw.proc();
IncreaseGeneratingWorldProgress(GWP_GAME_START);
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
/* Show all vital windows again, because we have hidden them */
if (_gw.threaded) ShowVitalWindows();
_gw.active = false;
_gw.thread = NULL;
_gw.proc = NULL;
_gw.threaded = false;
DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
MarkWholeScreenDirty();
if (_network_dedicated) DEBUG(net, 0)("Map generated, starting game");
return NULL;
}
/**
* Set here the function, if any, that you want to be called when landscape
* generation is done.
*/
void GenerateWorldSetCallback(gw_done_proc *proc)
{
_gw.proc = proc;
}
/**
* This will wait for the thread to finish up his work. It will not continue
* till the work is done.
*/
void WaitTillGeneratedWorld(void)
{
if (_gw.thread == NULL) return;
_gw.quit_thread = true;
OTTDJoinThread(_gw.thread);
_gw.thread = NULL;
_gw.threaded = false;
}
/**
* Initializes the abortion process
*/
void AbortGeneratingWorld(void)
{
_gw.abort = true;
}
/**
* Is the generation being aborted?
*/
bool IsGeneratingWorldAborted(void)
{
return _gw.abort;
}
/**
* Really handle the abortion, i.e. clean up some of the mess
*/
void HandleGeneratingWorldAbortion(void)
{
/* Clean up - in SE create an empty map, otherwise, go to intro menu */
_switch_mode = (_game_mode == GM_EDITOR) ? SM_EDITOR : SM_MENU;
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
/* Show all vital windows again, because we have hidden them */
if (_gw.threaded) ShowVitalWindows();
_gw.active = false;
_gw.thread = NULL;
_gw.proc = NULL;
_gw.threaded = false;
DeleteWindowById(WC_GENERATE_PROGRESS_WINDOW, 0);
MarkWholeScreenDirty();
OTTDExitThread();
}
/**
* Generate a world.
* @param mode The mode of world generation (@see GenerateWorldModes).
* @param size_x The X-size of the map.
* @param size_y The Y-size of the map.
*/
void GenerateWorld(int mode, uint size_x, uint size_y)
{
if (_gw.active) return;
_gw.mode = mode;
_gw.size_x = size_x;
_gw.size_y = size_y;
_gw.active = true;
_gw.abort = false;
_gw.lp = _local_player;
_gw.wait_for_draw = false;
_gw.quit_thread = false;
_gw.threaded = true;
/* This disables some commands and stuff */
_local_player = OWNER_SPECTATOR;
/* Make sure everything is done via OWNER_NONE */
_current_player = OWNER_NONE;
InitializeGame(IG_DATE_RESET, _gw.size_x, _gw.size_y);
PrepareGenerateWorldProgress();
/* Re-init the windowing system */
ResetWindowSystem();
LoadStringWidthTable();
/* Create toolbars */
SetupColorsAndInitialWindow();
if (_network_dedicated || (_gw.thread = OTTDCreateThread(&_GenerateWorld, (void *)"")) == NULL) {
_gw.threaded = false;
_GenerateWorld(NULL);
} else {
/* Remove any open window */
DeleteAllNonVitalWindows();
/* Don't show the dialog if we don't have a thread */
ShowGenerateWorldProgress();
}
/* Zoom out and center on the map (is pretty ;)) */
if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) {
while (DoZoomInOutWindow(ZOOM_OUT, FindWindowById(WC_MAIN_WINDOW, 0) ) ) {}
ScrollMainWindowToTile(TileXY(MapSizeX() / 2, MapSizeY() / 2));
}
/* Hide vital windows, because we don't allow to use them */
/* XXX -- Ideal it is done after ShowGenerateWorldProgress, but stupid
* enough, DoZoomInOutWindow _needs_ the toolbar to exist... */
if (_gw.thread != NULL) HideVitalWindows();
}

91
genworld.h Normal file
View File

@ -0,0 +1,91 @@
/* $Id$ */
#ifndef GENWORLD_H
#define GENWORLD_H
/* If OTTDThread isn't defined, define it to a void, but make sure to undefine
* it after this include. This makes including genworld.h easier, as you
* don't need to include thread.h before it, while it stays possible to
* include it after it, and still work.
*/
#ifndef OTTDThread
#define TEMPORARY_OTTDTHREAD_DEFINITION
#define OTTDThread void
#endif
/*
* Order of these enums has to be the same as in lang/english.txt
* Otherwise you will get inconsistent behaviour.
*/
enum {
LG_ORIGINAL = 0, //! The original landscape generator
LG_TERRAGENESIS = 1, //! TerraGenesis Perlin landscape generator
GENERATE_NEW_SEED = (uint)-1, //! Create a new random seed
};
typedef void gw_done_proc(void);
typedef struct gw_info {
bool active; //! Is generating world active
bool abort; //! Whether to abort the thread ASAP
bool wait_for_draw; //! Are we waiting on a draw event
bool quit_thread; //! Do we want to quit the active thread
bool threaded; //! Whether we run _GenerateWorld threaded
int mode; //! What mode are we making a world in
byte lp; //! The local_player before generating
uint size_x; //! X-size of the map
uint size_y; //! Y-size of the map
gw_done_proc *proc; //! Proc that is called when done (can be NULL)
OTTDThread *thread; //! The thread we are in (can be NULL)
} gw_info;
#ifdef TEMPORARY_OTTDTHREAD_DEFINITION
#undef OTTDThread
#undef TEMPORARY_OTTDTHREAD_DEFINITION
#endif
typedef enum gwp_classes {
GWP_MAP_INIT, /* Initialize/allocate the map, start economy */
GWP_LANDSCAPE, /* Create the landscape */
GWP_ROUGH_ROCKY, /* Make rough and rocky areas */
GWP_TOWN, /* Generate towns */
GWP_INDUSTRY, /* Generate industries */
GWP_UNMOVABLE, /* Generate unmovables (radio tower, light houses) */
GWP_TREE, /* Generate trees */
GWP_GAME_INIT, /* Initialize the game */
GWP_RUNTILELOOP, /* Runs the tile loop 1280 times to make snow etc */
GWP_GAME_START, /* Really prepare to start the game */
GWP_CLASS_COUNT
} gwp_class;
/**
* Check if we are currently in the process of generating a world.
*/
static inline bool IsGeneratingWorld(void)
{
extern gw_info _gw;
return _gw.active;
}
/* genworld.c */
void SetGeneratingWorldPaintStatus(bool status);
bool IsGeneratingWorldReadyForPaint(void);
bool IsGenerateWorldThreaded(void);
void GenerateWorldSetCallback(gw_done_proc *proc);
void WaitTillGeneratedWorld(void);
void GenerateWorld(int mode, uint size_x, uint size_y);
void AbortGeneratingWorld(void);
bool IsGeneratingWorldAborted(void);
void HandleGeneratingWorldAbortion(void);
/* genworld_gui.c */
void SetGeneratingWorldProgress(gwp_class class, uint total);
void IncreaseGeneratingWorldProgress(gwp_class class);
void PrepareGenerateWorldProgress(void);
void ShowGenerateWorldProgress(void);
void StartNewGameWithoutGUI(uint seed);
void ShowCreateScenario(void);
#endif /* GENWORLD_H */

912
genworld_gui.c Normal file
View File

@ -0,0 +1,912 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "heightmap.h"
#include "functions.h"
#include "table/strings.h"
#include "window.h"
#include "gui.h"
#include "gfx.h"
#include "strings.h"
#include "gfxinit.h"
#include "player.h"
#include "command.h"
#include "sound.h"
#include "variables.h"
#include "string.h"
#include "settings.h"
#include "debug.h"
#include "genworld.h"
#include "network.h"
#include "thread.h"
#include "date.h"
enum {
START_DATE_QUERY,
SNOW_LINE_QUERY,
FLAT_WORLD_HEIGHT_QUERY,
LEN_RND_SEED = 11,
SEED_EDIT = 15,
};
/**
* In what 'mode' the GenerateLandscapeWindowProc is.
*/
typedef enum glwp_modes {
GLWP_GENERATE,
GLWP_HEIGHTMAP,
GLWP_SCENARIO,
GLWP_END
} glwp_modes;
static char _edit_str_buf[LEN_RND_SEED];
static uint _heightmap_x = 0;
static uint _heightmap_y = 0;
static StringID _heightmap_str = STR_NULL;
static bool _goto_editor = false;
extern void SwitchMode(int new_mode);
static inline void SetNewLandscapeType(byte landscape)
{
_opt_newgame.landscape = landscape;
InvalidateWindowClasses(WC_SELECT_GAME);
InvalidateWindowClasses(WC_GENERATE_LANDSCAPE);
}
// no longer static to allow calling from outside module
const Widget _generate_landscape_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_WORLD_GENERATION_CAPTION,STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 13, 0, 337, 14, 267, STR_NULL, STR_NULL},
{ WWT_PANEL_2, RESIZE_NONE, 12, 10, 86, 24, 78, 0x1312, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 90, 166, 24, 78, 0x1314, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 170, 246, 24, 78, 0x1316, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 250, 326, 24, 78, 0x1318, STR_0311_SELECT_TOYLAND_LANDSCAPE},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 149, 90, 101, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161, 90, 101, STR_0225, STR_NULL}, // Mapsize X
{ WWT_PANEL, RESIZE_NONE, 12, 180, 215, 90, 101, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227, 90, 101, STR_0225, STR_NULL}, // Mapsize Y
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 112, 123, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 112, 123, STR_0225, STR_NULL}, // Number of towns
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 130, 141, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 130, 141, STR_0225, STR_NULL}, // Number of industries
{ WWT_IMGBTN, RESIZE_NONE, 15, 114, 194, 152, 163, STR_NULL, STR_RANDOM_SEED_HELP}, // Edit box for seed
{ WWT_TEXTBTN, RESIZE_NONE, 12, 203, 285, 152, 163, STR_RANDOM, STR_RANDOM_HELP},
{ WWT_TEXTBTN, RESIZE_NONE, 6, 243, 326, 228, 257, STR_GENERATE, STR_NULL}, // Generate button
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 112, 123, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 112, 123, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 112, 123, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 130, 141, SPR_ARROW_DOWN, STR_SNOW_LINE_DOWN},
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 130, 141, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 130, 141, SPR_ARROW_UP, STR_SNOW_LINE_UP},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 192, 203, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 192, 203, STR_0225, STR_NULL}, // Tree placer
{ WWT_EMPTY, RESIZE_NONE, 12, 114, 231, 174, 185, STR_NULL, STR_NULL},
//{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 174, 185, STR_NULL, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 231, 174, 185, STR_NULL, STR_NULL},
//{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 174, 185, STR_0225, STR_NULL}, // Landscape generator
//{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 210, 221, STR_NULL, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 231, 210, 221, STR_NULL, STR_NULL},
//{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 210, 221, STR_0225, STR_NULL}, // Terrain type
//{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 228, 239, STR_NULL, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 231, 228, 239, STR_NULL, STR_NULL},
//{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 228, 239, STR_0225, STR_NULL}, // Water quantity
{ WWT_PANEL, RESIZE_NONE, 12, 113, 219, 246, 257, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 246, 257, STR_0225, STR_NULL}, // Map smoothness
{ WIDGETS_END},
};
const Widget _heightmap_load_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_WORLD_GENERATION_CAPTION,STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 13, 0, 337, 14, 235, STR_NULL, STR_NULL},
{ WWT_PANEL_2, RESIZE_NONE, 12, 10, 86, 24, 78, 0x1312, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 90, 166, 24, 78, 0x1314, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 170, 246, 24, 78, 0x1316, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 250, 326, 24, 78, 0x1318, STR_0311_SELECT_TOYLAND_LANDSCAPE},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 149, 112, 123, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 150, 161, 112, 123, STR_0225, STR_NULL}, // Mapsize X
{ WWT_PANEL, RESIZE_NONE, 12, 180, 215, 112, 123, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 216, 227, 112, 123, STR_0225, STR_NULL}, // Mapsize Y
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 134, 145, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 134, 145, STR_0225, STR_NULL}, // Number of towns
{ WWT_PANEL, RESIZE_NONE, 12, 114, 163, 152, 163, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 164, 175, 152, 163, STR_0225, STR_NULL}, // Number of industries
{ WWT_IMGBTN, RESIZE_NONE, 15, 114, 194, 174, 185, STR_NULL, STR_RANDOM_SEED_HELP}, // Edit box for seed
{ WWT_TEXTBTN, RESIZE_NONE, 12, 203, 285, 174, 185, STR_RANDOM, STR_RANDOM_HELP},
{ WWT_TEXTBTN, RESIZE_NONE, 6, 243, 326, 196, 225, STR_GENERATE, STR_NULL}, // Generate button
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 134, 145, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 134, 145, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 134, 145, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 152, 163, SPR_ARROW_DOWN, STR_SNOW_LINE_DOWN},
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 152, 163, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 152, 163, SPR_ARROW_UP, STR_SNOW_LINE_UP},
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 196, 207, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 196, 207, STR_0225, STR_NULL}, // Tree placer
{ WWT_PANEL, RESIZE_NONE, 12, 114, 219, 214, 225, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 220, 231, 214, 225, STR_0225, STR_NULL}, // Heightmap rotation
{ WIDGETS_END},
};
static void StartGeneratingLandscape(glwp_modes mode)
{
/* If we want to go to the editor, and aren't yet, we need to delay
* it as long as possible, else it gives nasty side-effects (aborting
* results in ending up in the SE, which you don't want. Therefor we
* use this switch to do it at the very end.
*/
if (_goto_editor) _game_mode = GM_EDITOR;
DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
DeleteWindowByClass(WC_INDUSTRY_VIEW);
DeleteWindowByClass(WC_TOWN_VIEW);
DeleteWindowByClass(WC_LAND_INFO);
/* Copy all XXX_newgame to XXX */
UpdatePatches();
_opt_ptr = &_opt;
memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
/* Load the right landscape stuff */
GfxLoadSprites();
SndPlayFx(SND_15_BEEP);
switch (mode) {
case GLWP_GENERATE: _switch_mode = (_game_mode == GM_EDITOR) ? SM_GENRANDLAND : SM_NEWGAME; break;
case GLWP_HEIGHTMAP: _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP; break;
case GLWP_SCENARIO: _switch_mode = SM_EDITOR; break;
default: NOT_REACHED(); return;
}
}
static void HeightmapScaledTooMuchCallback(bool ok_clicked)
{
if (ok_clicked) {
Window *w;
glwp_modes mode = 0;
for (mode = 0; mode < GLWP_END; mode++) {
w = FindWindowById(WC_GENERATE_LANDSCAPE, mode);
if (w != NULL) StartGeneratingLandscape(mode);
}
}
}
void GenerateLandscapeWndProc(Window *w, WindowEvent *e)
{
static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
static const StringID elevations[] = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID};
static const StringID sea_lakes[] = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID};
static const StringID smoothness[] = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID};
static const StringID tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID};
static const StringID rotation[] = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
static const StringID landscape[] = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
static const StringID num_towns[] = {STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
static const StringID num_inds[] = {STR_26816_NONE, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
uint mode = w->window_number;
uint y;
switch (e->event) {
case WE_PAINT:
w->disabled_state = 0;
/* TODO -- Above and below you see some lines commented out with '//' in
* front of it. This is because currently the widget system can't handle
* more than 32 widgets per window, and we need 34. Therefor we draw
* parts of the widgets manually below, reducing the number to 32.
* Of course someone is already hard working to replace the system with
* one that can scale past the 32 limit. When this is done you should
* re-enable the lines and remove the ones that came instead. Better,
* revert revision 5817 (from TGP branch), and you should be just fine.
* If you have any questions about it, bug TrueLight.
*/
/* You can't select smoothness if not terragenesis */
// if (_patches_newgame.land_generator == 0) w->disabled_state |= (1 << 32 | 1 << 33);
if (_patches_newgame.land_generator == 0) w->disabled_state |= (1 << 30 | 1 << 31);
/* Disable snowline if not hilly */
if (_opt_newgame.landscape != LT_HILLY) w->disabled_state |= (1 << 21 | 1 << 22 | 1 << 23);
/* Disable town and industry in SE */
if (_game_mode == GM_EDITOR) w->disabled_state |= (1 << 11 | 1 << 12 | 1 << 13 | 1 << 14 | 1 << 24 | 1 << 25);
if (_patches_newgame.starting_year <= MIN_YEAR) SETBIT(w->disabled_state, 18);
if (_patches_newgame.starting_year >= MAX_YEAR) SETBIT(w->disabled_state, 20);
if (_patches_newgame.snow_line_height <= 2 ) SETBIT(w->disabled_state, 21);
if (_patches_newgame.snow_line_height >= 13) SETBIT(w->disabled_state, 23);
w->click_state = (w->click_state & ~(0xF << 3)) | (1 << (_opt_newgame.landscape + 3));
DrawWindowWidgets(w);
y = (mode == GLWP_HEIGHTMAP) ? 22 : 0;
DrawString( 12, 91 + y, STR_MAPSIZE, 0);
DrawString(119, 91 + y, mapsizes[_patches_newgame.map_x - 6], 0x10);
DrawString(168, 91 + y, STR_BY, 0);
DrawString(182, 91 + y, mapsizes[_patches_newgame.map_y - 6], 0x10);
DrawString( 12, 113 + y, STR_NUMBER_OF_TOWNS, 0);
DrawString( 12, 131 + y, STR_NUMBER_OF_INDUSTRIES, 0);
if (_game_mode == GM_EDITOR) {
DrawString(118, 113 + y, STR_6836_OFF, 0x10);
DrawString(118, 131 + y, STR_6836_OFF, 0x10);
} else {
DrawString(118, 113 + y, num_towns[_opt_newgame.diff.number_towns], 0x10);
DrawString(118, 131 + y, num_inds[_opt_newgame.diff.number_industries], 0x10);
}
DrawString( 12, 153 + y, STR_RANDOM_SEED, 0);
DrawEditBox(w, &WP(w, querystr_d), SEED_EDIT);
DrawString(182, 113 + y, STR_DATE, 0);
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
DrawStringCentered(271, 113 + y, STR_GENERATE_DATE, 0);
DrawString(182, 131 + y, STR_SNOW_LINE_HEIGHT, 0);
SetDParam(0, _patches_newgame.snow_line_height);
DrawStringCentered(303, 131 + y, STR_SNOW_LINE_HEIGHT_NUM, 0x10);
if (mode == GLWP_GENERATE) {
/* TODO -- Remove next 2 lines if 32 widget limit is removed */
DrawFrameRect(114, 174, 219, 185, 12, 0);
DrawString(222, 175, STR_0225, 0x10);
DrawString( 12, 175, STR_LAND_GENERATOR, 0);
DrawString(118, 175, landscape[_patches_newgame.land_generator], 0x10);
DrawString( 12, 193, STR_TREE_PLACER, 0);
DrawString(118, 193, tree_placer[_patches_newgame.tree_placer], 0x10);
/* TODO -- Remove next 2 lines if 32 widget limit is removed */
DrawFrameRect(114, 210, 219, 221, 12, 0);
DrawString(222, 211, STR_0225, 0x10);
DrawString( 12, 211, STR_TERRAIN_TYPE, 0);
DrawString(118, 211, elevations[_opt_newgame.diff.terrain_type], 0x10);
/* TODO -- Remove next 2 lines if 32 widget limit is removed */
DrawFrameRect(114, 228, 219, 239, 12, 0);
DrawString(222, 229, STR_0225, 0x10);
DrawString( 12, 229, STR_QUANTITY_OF_SEA_LAKES, 0);
DrawString(118, 229, sea_lakes[_opt_newgame.diff.quantity_sea_lakes], 0x10);
DrawString( 12, 247, STR_SMOOTHNESS, 0);
DrawString(118, 247, smoothness[_patches_newgame.tgen_smoothness], 0x10);
} else {
char buffer[512];
if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) {
SetDParam(0, _heightmap_y);
SetDParam(1, _heightmap_x);
} else {
SetDParam(0, _heightmap_x);
SetDParam(1, _heightmap_y);
}
GetString(buffer, STR_HEIGHTMAP_SIZE);
DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, 0x10);
DrawString( 12, 91, STR_HEIGHTMAP_NAME, 0x10);
SetDParam(0, _heightmap_str);
DrawStringTruncated(114, 91, STR_ORANGE, 0x10, 326 - 114 - GetStringWidth(buffer) - 5);
/* TODO -- Remove next 2 lines if 32 widget limit is removed */
DrawFrameRect(114, 196, 219, 207, 12, 0);
DrawString(222, 197, STR_0225, 0x10);
DrawString( 12, 197, STR_TREE_PLACER, 0);
DrawString(118, 197, tree_placer[_patches_newgame.tree_placer], 0x10);
DrawString( 12, 215, STR_HEIGHTMAP_ROTATION, 0);
DrawString(118, 215, rotation[_patches_newgame.heightmap_rotation], 0x10);
}
break;
case WE_CLICK:
switch (e->click.widget) {
case 0: DeleteWindow(w); break;
case 3: case 4: case 5: case 6:
SetNewLandscapeType(e->click.widget - 3);
break;
case 7: case 8: // Mapsize X
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
break;
case 9: case 10: // Mapsize Y
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
break;
case 11: case 12: // Number of towns
ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, 12, 0, 0);
break;
case 13: case 14: // Number of industries
ShowDropDownMenu(w, num_inds, _opt_newgame.diff.number_industries, 14, 0, 0);
break;
case 16: // Random seed
_patches_newgame.generation_seed = InteractiveRandom();
ttd_strlcpy(_edit_str_buf, str_fmt("%u", _patches_newgame.generation_seed), lengthof(_edit_str_buf));
UpdateTextBufferSize(&((querystr_d *)&WP(w, querystr_d))->text);
SetWindowDirty(w);
break;
case 17: // Generate
if (mode == GLWP_HEIGHTMAP && (
_heightmap_x * 2 < (1U << _patches_newgame.map_x) || _heightmap_x / 2 > (1U << _patches_newgame.map_x) ||
_heightmap_y * 2 < (1U << _patches_newgame.map_y) || _heightmap_y / 2 > (1U << _patches_newgame.map_y))) {
ShowQuery(STR_HEIGHTMAP_SCALE_WARNING_CAPTION, STR_HEIGHTMAP_SCALE_WARNING_MESSAGE, HeightmapScaledTooMuchCallback, WC_GENERATE_LANDSCAPE, mode);
} else {
StartGeneratingLandscape(mode);
}
break;
case 18: case 20: // Year buttons
/* Don't allow too fast scrolling */
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, e->click.widget);
SetWindowDirty(w);
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->click.widget - 19, MIN_YEAR, MAX_YEAR);
}
_left_button_clicked = false;
break;
case 19: // Year text
WP(w, def_d).data_3 = START_DATE_QUERY;
SetDParam(0, _patches_newgame.starting_year);
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 5, 100, WC_GENERATE_LANDSCAPE, mode, CS_NUMERAL);
break;
case 21: case 23: // Snow line buttons
/* Don't allow too fast scrolling */
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, e->click.widget);
SetWindowDirty(w);
_patches_newgame.snow_line_height = clamp(_patches_newgame.snow_line_height + e->click.widget - 22, 2, 13);
}
_left_button_clicked = false;
break;
case 22: // Snow line text
WP(w, def_d).data_3 = SNOW_LINE_QUERY;
SetDParam(0, _patches_newgame.snow_line_height);
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, WC_GENERATE_LANDSCAPE, mode, CS_NUMERAL);
break;
case 24: case 25: // Tree placer
ShowDropDownMenu(w, tree_placer, _patches_newgame.tree_placer, 25, 0, 0);
break;
// case 26: case 27: // Landscape generator OR Heightmap rotation
case 27:
if (mode == GLWP_HEIGHTMAP) {
ShowDropDownMenu(w, rotation, _patches_newgame.heightmap_rotation, 27, 0, 0);
} else {
ShowDropDownMenu(w, landscape, _patches_newgame.land_generator, 27, 0, 0);
}
break;
// case 28: case 29: // Terrain type
case 28:
// ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, 29, 0, 0);
ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, 28, 0, 0);
break;
// case 30: case 31: // Water quantity
case 29:
// ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, 31, 0, 0);
ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, 29, 0, 0);
break;
// case 32: case 33: // Map smoothness
case 30: case 31:
// ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, 33, 0, 0);
ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, 31, 0, 0);
break;
}
break;
case WE_MESSAGE:
ttd_strlcpy(_edit_str_buf, str_fmt("%u", _patches_newgame.generation_seed), lengthof(_edit_str_buf));
DrawEditBox(w, &WP(w, querystr_d), SEED_EDIT);
break;
case WE_MOUSELOOP:
HandleEditBox(w, &WP(w, querystr_d), SEED_EDIT);
break;
case WE_KEYPRESS:
HandleEditBoxKey(w, &WP(w, querystr_d), SEED_EDIT, e, CS_NUMERAL);
_patches_newgame.generation_seed = atoi(_edit_str_buf);
break;
case WE_DROPDOWN_SELECT:
switch (e->dropdown.button) {
case 8: _patches_newgame.map_x = e->dropdown.index + 6; break;
case 10: _patches_newgame.map_y = e->dropdown.index + 6; break;
case 12:
_opt_newgame.diff.number_towns = e->dropdown.index;
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
break;
case 14:
_opt_newgame.diff.number_industries = e->dropdown.index;
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
break;
case 25:
_patches_newgame.tree_placer = e->dropdown.index;
break;
case 27:
if (mode == GLWP_HEIGHTMAP) {
_patches_newgame.heightmap_rotation = e->dropdown.index;
} else {
_patches_newgame.land_generator = e->dropdown.index;
}
break;
// case 29:
case 28:
_opt_newgame.diff.terrain_type = e->dropdown.index;
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
break;
// case 31:
case 29:
_opt_newgame.diff.quantity_sea_lakes = e->dropdown.index;
if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
break;
// case 33:
case 31:
_patches_newgame.tgen_smoothness = e->dropdown.index;
break;
}
SetWindowDirty(w);
break;
case WE_ON_EDIT_TEXT: {
if (e->edittext.str != NULL) {
int32 value = atoi(e->edittext.str);
switch (WP(w, def_d).data_3) {
case START_DATE_QUERY:
InvalidateWidget(w, 19);
_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
break;
case SNOW_LINE_QUERY:
InvalidateWidget(w, 22);
_patches_newgame.snow_line_height = clamp(value, 2, 13);
break;
}
SetWindowDirty(w);
}
break;
}
}
}
const WindowDesc _generate_landscape_desc = {
WDP_CENTER, WDP_CENTER, 338, 268,
WC_GENERATE_LANDSCAPE, 0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_generate_landscape_widgets,
GenerateLandscapeWndProc,
};
const WindowDesc _heightmap_load_desc = {
WDP_CENTER, WDP_CENTER, 338, 236,
WC_GENERATE_LANDSCAPE, 0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_heightmap_load_widgets,
GenerateLandscapeWndProc,
};
static void _ShowGenerateLandscape(glwp_modes mode)
{
Window *w;
/* Don't kill WC_GENERATE_LANDSCAPE:GLWP_SCENARIO, because it resets
* _goto_editor, which we maybe need later on. */
DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_GENERATE);
DeleteWindowById(WC_GENERATE_LANDSCAPE, GLWP_HEIGHTMAP);
/* Always give a new seed if not editor */
if (_game_mode != GM_EDITOR) _patches_newgame.generation_seed = InteractiveRandom();
if (mode == GLWP_HEIGHTMAP) {
if (_heightmap_str != STR_NULL) DeleteName(_heightmap_str);
_heightmap_x = 0;
_heightmap_y = 0;
_heightmap_str = AllocateName(_file_to_saveload.title, 0);
/* If the function returns negative, it means there was a problem loading the heightmap */
if (!GetHeightmapDimensions(_file_to_saveload.name, &_heightmap_x, &_heightmap_y))
return;
}
w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
if (w != NULL) {
querystr_d *querystr = &WP(w, querystr_d);
ttd_strlcpy(_edit_str_buf, str_fmt("%u", _patches_newgame.generation_seed), lengthof(_edit_str_buf));
querystr->text.caret = true;
querystr->text.maxlength = lengthof(_edit_str_buf);
querystr->text.maxwidth = 120;
querystr->text.buf = _edit_str_buf;
querystr->caption = STR_NULL;
UpdateTextBufferSize(&querystr->text);
InvalidateWindow(WC_GENERATE_LANDSCAPE, mode);
}
}
void ShowGenerateLandscape(void)
{
_ShowGenerateLandscape(GLWP_GENERATE);
}
void ShowHeightmapLoad(void)
{
_ShowGenerateLandscape(GLWP_HEIGHTMAP);
}
void StartNewGameWithoutGUI(uint seed)
{
/* GenerateWorld takes care of the possible GENERATE_NEW_SEED value in 'seed' */
_patches_newgame.generation_seed = seed;
StartGeneratingLandscape(GLWP_GENERATE);
}
void CreateScenarioWndProc(Window *w, WindowEvent *e)
{
static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
switch (e->event) {
case WE_PAINT:
w->disabled_state = 0;
if (_patches_newgame.starting_year <= MIN_YEAR) SETBIT(w->disabled_state, 14);
if (_patches_newgame.starting_year >= MAX_YEAR) SETBIT(w->disabled_state, 16);
if (_patches_newgame.se_flat_world_height <= 0) SETBIT(w->disabled_state, 17);
if (_patches_newgame.se_flat_world_height >= 15) SETBIT(w->disabled_state, 19);
w->click_state = (w->click_state & ~(0xF << 3)) | (1 << (_opt_newgame.landscape + 3));
DrawWindowWidgets(w);
DrawString( 12, 96, STR_MAPSIZE, 0);
DrawString( 89, 96, mapsizes[_patches_newgame.map_x - 6], 0x10);
DrawString(138, 96, STR_BY, 0);
DrawString(152, 96, mapsizes[_patches_newgame.map_y - 6], 0x10);
DrawString(162, 118, STR_DATE, 0);
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
DrawStringCentered(271, 118, STR_GENERATE_DATE, 0);
DrawString(162, 136, STR_FLAT_WORLD_HEIGHT, 0);
SetDParam(0, _patches_newgame.se_flat_world_height);
DrawStringCentered(303, 136, STR_FLAT_WORLD_HEIGHT_NUM, 0x10);
break;
case WE_CLICK:
switch (e->click.widget) {
case 0: DeleteWindow(w); break;
case 3: case 4: case 5: case 6:
SetNewLandscapeType(e->click.widget - 3);
break;
case 7: case 8: // Mapsize X
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 8, 0, 0);
break;
case 9: case 10: // Mapsize Y
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 10, 0, 0);
break;
case 11: // Empty world / flat world
StartGeneratingLandscape(GLWP_SCENARIO);
break;
case 12: // Generate
_goto_editor = true;
ShowGenerateLandscape();
break;
case 13: // Heightmap
_goto_editor = true;
ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP);
break;
case 14: case 16: // Year buttons
/* Don't allow too fast scrolling */
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, e->click.widget);
SetWindowDirty(w);
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + e->click.widget - 15, MIN_YEAR, MAX_YEAR);
}
_left_button_clicked = false;
break;
case 15: // Year text
WP(w, def_d).data_3 = START_DATE_QUERY;
SetDParam(0, _patches_newgame.starting_year);
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 5, 100, WC_GENERATE_LANDSCAPE, GLWP_SCENARIO, CS_NUMERAL);
break;
case 17: case 19: // Height level buttons
/* Don't allow too fast scrolling */
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, e->click.widget);
SetWindowDirty(w);
_patches_newgame.se_flat_world_height = clamp(_patches_newgame.se_flat_world_height + e->click.widget - 18, 0, 15);
}
_left_button_clicked = false;
break;
case 18: // Height level text
WP(w, def_d).data_3 = FLAT_WORLD_HEIGHT_QUERY;
SetDParam(0, _patches_newgame.se_flat_world_height);
ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, WC_GENERATE_LANDSCAPE, GLWP_SCENARIO, CS_NUMERAL);
break;
}
break;
case WE_DROPDOWN_SELECT:
switch (e->dropdown.button) {
case 8: _patches_newgame.map_x = e->dropdown.index + 6; break;
case 10: _patches_newgame.map_y = e->dropdown.index + 6; break;
}
SetWindowDirty(w);
break;
case WE_DESTROY:
_goto_editor = false;
break;
case WE_ON_EDIT_TEXT: {
if (e->edittext.str != NULL) {
int32 value = atoi(e->edittext.str);
switch (WP(w, def_d).data_3) {
case START_DATE_QUERY:
InvalidateWidget(w, 15);
_patches_newgame.starting_year = clamp(value, MIN_YEAR, MAX_YEAR);
break;
case FLAT_WORLD_HEIGHT_QUERY:
InvalidateWidget(w, 18);
_patches_newgame.se_flat_world_height = clamp(value, 0, 15);
break;
}
SetWindowDirty(w);
}
break;
}
}
}
const Widget _create_scenario_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 13, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 13, 11, 337, 0, 13, STR_SE_CAPTION, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 13, 0, 337, 14, 179, STR_NULL, STR_NULL},
{ WWT_PANEL_2, RESIZE_NONE, 12, 10, 86, 24, 78, 0x1312, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 90, 166, 24, 78, 0x1314, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 170, 246, 24, 78, 0x1316, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 250, 326, 24, 78, 0x1318, STR_0311_SELECT_TOYLAND_LANDSCAPE},
{ WWT_PANEL, RESIZE_NONE, 12, 84, 119, 95, 106, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 120, 131, 95, 106, STR_0225, STR_NULL}, // Mapsize X
{ WWT_PANEL, RESIZE_NONE, 12, 150, 185, 95, 106, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 186, 197, 95, 106, STR_0225, STR_NULL}, // Mapsize Y
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 117, 128, STR_SE_FLAT_WORLD, STR_SE_FLAT_WORLD}, // Empty (sea-level) map
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 135, 146, STR_SE_RANDOM_LAND, STR_022A_GENERATE_RANDOM_LAND}, // Generate
{ WWT_TEXTBTN, RESIZE_NONE, 6, 12, 145, 153, 164, STR_LOAD_GAME_HEIGHTMAP, STR_LOAD_SCEN_HEIGHTMAP}, // Heightmap
{ WWT_IMGBTN, RESIZE_NONE, 12, 216, 227, 117, 128, SPR_ARROW_DOWN, STR_029E_MOVE_THE_STARTING_DATE},
{ WWT_PANEL, RESIZE_NONE, 12, 228, 314, 117, 128, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 117, 128, SPR_ARROW_UP, STR_029F_MOVE_THE_STARTING_DATE},
{ WWT_IMGBTN, RESIZE_NONE, 12, 282, 293, 135, 146, SPR_ARROW_DOWN, STR_FLAT_WORLD_HEIGHT_DOWN},
{ WWT_PANEL, RESIZE_NONE, 12, 294, 314, 135, 146, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 12, 315, 326, 135, 146, SPR_ARROW_UP, STR_FLAT_WORLD_HEIGHT_UP},
{ WIDGETS_END},
};
const WindowDesc _create_scenario_desc = {
WDP_CENTER, WDP_CENTER, 338, 180,
WC_GENERATE_LANDSCAPE, 0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_create_scenario_widgets,
CreateScenarioWndProc,
};
void ShowCreateScenario(void)
{
DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
AllocateWindowDescFront(&_create_scenario_desc, GLWP_SCENARIO);
}
static const Widget _show_terrain_progress_widgets[] = {
{ WWT_CAPTION, RESIZE_NONE, 14, 0, 180, 0, 13, STR_GENERATION_WORLD, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_IMGBTN, RESIZE_NONE, 14, 0, 180, 14, 96, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 15, 20, 161, 74, 85, STR_GENERATION_ABORT, STR_NULL}, // Abort button
{ WIDGETS_END},
};
typedef struct tp_info {
uint percent;
StringID class;
uint current;
uint total;
int timer;
} tp_info;
static tp_info _tp;
static void AbortGeneratingWorldCallback(bool ok_clicked)
{
if (ok_clicked) AbortGeneratingWorld();
else if (IsGeneratingWorld() && !IsGeneratingWorldAborted()) SetMouseCursor(SPR_CURSOR_ZZZ);
}
static void ShowTerrainProgressProc(Window* w, WindowEvent* e)
{
switch (e->event) {
case WE_CLICK:
switch (e->click.widget) {
case 2:
if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE);
ShowQuery(STR_GENERATION_ABORT_CAPTION, STR_GENERATION_ABORT_MESSAGE, AbortGeneratingWorldCallback, WC_GENERATE_PROGRESS_WINDOW, 0);
break;
}
break;
case WE_PAINT:
DrawWindowWidgets(w);
/* Draw the % complete with a bar and a text */
DrawFrameRect(19, 20, (w->width - 18), 37, 14, FR_BORDERONLY);
DrawFrameRect(20, 21, (int)((w->width - 40) * _tp.percent / 100) + 20, 36, 10, 0);
SetDParam(0, _tp.percent);
DrawStringCentered(90, 25, STR_PROGRESS, 0);
/* Tell which class we are generating */
DrawStringCentered(90, 46, _tp.class, 0);
/* And say where we are in that class */
SetDParam(0, _tp.current);
SetDParam(1, _tp.total);
DrawStringCentered(90, 58, STR_GENERATION_PROGRESS, 0);
SetWindowDirty(w);
break;
}
}
static const WindowDesc _show_terrain_progress_desc = {
WDP_CENTER, WDP_CENTER, 181, 97,
WC_GENERATE_PROGRESS_WINDOW, 0,
WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_show_terrain_progress_widgets,
ShowTerrainProgressProc
};
/**
* Initializes the progress counters to the starting point.
*/
void PrepareGenerateWorldProgress(void)
{
_tp.class = STR_WORLD_GENERATION;
_tp.current = 0;
_tp.total = 0;
_tp.percent = 0;
_tp.timer = 0; // Forces to paint the progress window immediatelly
}
/**
* Show the window where a user can follow the process of the map generation.
*/
void ShowGenerateWorldProgress(void)
{
AllocateWindowDescFront(&_show_terrain_progress_desc, 0);
}
static void _SetGeneratingWorldProgress(gwp_class class, uint progress, uint total)
{
static const int percent_table[GWP_CLASS_COUNT + 1] = {0, 5, 15, 20, 40, 60, 65, 80, 85, 99, 100 };
static const StringID class_table[GWP_CLASS_COUNT] = {
STR_WORLD_GENERATION,
STR_022E_LANDSCAPE_GENERATION,
STR_CLEARING_TILES,
STR_022F_TOWN_GENERATION,
STR_0230_INDUSTRY_GENERATION,
STR_UNMOVABLE_GENERATION,
STR_TREE_GENERATION,
STR_SETTINGUP_GAME,
STR_PREPARING_TILELOOP,
STR_PREPARING_GAME
};
assert(class < GWP_CLASS_COUNT);
/* Do not run this function if we aren't in a thread */
if (!IsGenerateWorldThreaded() && !_network_dedicated) return;
if (IsGeneratingWorldAborted()) HandleGeneratingWorldAbortion();
if (total == 0) {
assert(_tp.class == class_table[class]);
_tp.current += progress;
} else {
_tp.class = class_table[class];
_tp.current = progress;
_tp.total = total;
_tp.percent = percent_table[class];
}
/* Don't update the screen too often. So update it once in the
* _patches.progress_update_interval. However, the _tick_counter
* increases with 8 every 30ms, so compensate for that. */
if (!_network_dedicated && _tp.timer != 0 && _timer_counter - _tp.timer < (_patches.progress_update_interval * 8 / 30)) return;
/* Percentage is about the number of completed tasks, so 'current - 1' */
_tp.percent = percent_table[class] + (percent_table[class + 1] - percent_table[class]) * (_tp.current == 0 ? 0 : _tp.current - 1) / _tp.total;
_tp.timer = _timer_counter;
if (_network_dedicated) {
static uint last_percent = 0;
/* Never display 0% */
if (_tp.percent == 0) return;
/* Reset if percent is lower then the last recorded */
if (_tp.percent < last_percent) last_percent = 0;
/* Display every 5%, but 6% is also very valid.. just not smaller steps then 5% */
if (_tp.percent % 5 != 0 && _tp.percent <= last_percent + 5) return;
/* Never show steps smaller then 2%, even if it is a mod 5% */
if (_tp.percent <= last_percent + 2) return;
DEBUG(net, 1)("Percent complete: %d", _tp.percent);
last_percent = _tp.percent;
/* Don't continue as dedicated never has a thread running */
return;
}
InvalidateWindow(WC_GENERATE_PROGRESS_WINDOW, 0);
MarkWholeScreenDirty();
SetGeneratingWorldPaintStatus(true);
/* We wait here till the paint is done, so we don't read and write
* on the same tile at the same moment. Nasty hack, but that happens
* if you implement threading afterwards */
while (IsGeneratingWorldReadyForPaint()) { CSleep(10); }
}
/**
* Set the total of a stage of the world generation.
* @param class the current class we are in.
* @param total Set the total expected items for this class.
*
* Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
* Also, progress works if total is zero, total works if progress is zero.
*/
void SetGeneratingWorldProgress(gwp_class class, uint total)
{
if (total == 0) return;
_SetGeneratingWorldProgress(class, 0, total);
}
/**
* Increases the current stage of the world generation with one.
* @param class the current class we are in.
*
* Warning: this function isn't clever. Don't go from class 4 to 3. Go upwards, always.
* Also, progress works if total is zero, total works if progress is zero.
*/
void IncreaseGeneratingWorldProgress(gwp_class class)
{
/* In fact the param 'class' isn't needed.. but for some security reasons, we want it around */
_SetGeneratingWorldProgress(class, 1, 0);
}

9
gfx.c
View File

@ -12,6 +12,7 @@
#include "table/sprites.h"
#include "hal.h"
#include "variables.h"
#include "genworld.h"
#ifdef _DEBUG
bool _dbg_screen_rect;
@ -1753,6 +1754,8 @@ void DrawDirtyBlocks(void)
int x;
int y;
if (IsGeneratingWorld() && !IsGeneratingWorldReadyForPaint()) return;
y = 0;
do {
x = 0;
@ -1819,6 +1822,12 @@ void DrawDirtyBlocks(void)
_invalid_rect.top = h;
_invalid_rect.right = 0;
_invalid_rect.bottom = 0;
/* If we are generating a world, and waiting for a paint run, mark it here
* as done painting, so we can continue generating. */
if (IsGeneratingWorld() && IsGeneratingWorldReadyForPaint()) {
SetGeneratingWorldPaintStatus(false);
}
}

6
gui.h
View File

@ -64,6 +64,10 @@ void ShowPlayerAircraft(PlayerID player, StationID station);
/* terraform_gui.c */
void ShowTerraformToolbar(void);
/* tgp_gui.c */
void ShowGenerateLandscape(void);
void ShowHeightmapLoad(void);
void PlaceProc_DemolishArea(TileIndex tile);
void PlaceProc_LevelLand(TileIndex tile);
bool GUIPlaceProcDragXY(const WindowEvent *we);
@ -103,7 +107,6 @@ void SetVScroll2Count(Window *w, int num);
void SetHScrollCount(Window *w, int num);
void ShowCheatWindow(void);
void AskForNewGameToStart(void);
void DrawEditBox(Window *w, querystr_d *string, int wid);
void HandleEditBox(Window *w, querystr_d *string, int wid);
@ -138,6 +141,7 @@ enum {
bool DoZoomInOutWindow(int how, Window *w);
void ShowBuildIndustryWindow(void);
void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number, CharSetFilter afilter);
void ShowQuery(StringID caption, StringID message, void (*ok_cancel_callback)(bool ok_clicked), WindowClass window_class, WindowNumber window_number);
void ShowMusicWindow(void);
/* main_gui.c */

459
heightmap.c Normal file
View File

@ -0,0 +1,459 @@
/* $Id$ */
#include "stdafx.h"
#include "openttd.h"
#include "variables.h"
#include "functions.h"
#include "heightmap.h"
#include "clear_map.h"
#include "table/strings.h"
#include "void_map.h"
#include "debug.h"
#include "gfx.h"
#include "gui.h"
#include "saveload.h"
#include "bmp.h"
/**
* Convert RGB colors to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
* (average luminosity formula) -- Dalestan
* This in fact is the NTSC Color Space -- TrueLight
*/
static inline byte RGBToGrayscale(byte red, byte green, byte blue)
{
/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
* divide by it to normalize the value to a byte again. */
return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
}
#ifdef WITH_PNG
#include "png.h"
/**
* The PNG Heightmap loader.
*/
static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
{
uint x, y;
byte gray_palette[256];
png_bytep *row_pointers = NULL;
/* Get palette and convert it to grayscale */
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
int i;
int palette_size;
png_color *palette;
bool all_gray = true;
png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
}
/**
* For a non-gray palette of size 16 we assume that
* the order of the palette determines the height;
* the first entry is the sea (level 0), the second one
* level 1, etc.
*/
if (palette_size == 16 && !all_gray) {
for (i = 0; i < palette_size; i++) {
gray_palette[i] = 256 * i / palette_size;
}
}
}
row_pointers = png_get_rows(png_ptr, info_ptr);
/* Read the raw image data and convert in 8-bit grayscale */
for (x = 0; x < info_ptr->width; x++) {
for (y = 0; y < info_ptr->height; y++) {
byte *pixel = &map[y * info_ptr->width + x];
uint x_offset = x * info_ptr->channels;
if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
*pixel = gray_palette[row_pointers[y][x_offset]];
} else if (info_ptr->channels == 3) {
*pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
} else {
*pixel = row_pointers[y][x_offset];
}
}
}
}
/**
* Reads the heightmap and/or size of the heightmap from a PNG file.
* If map == NULL only the size of the PNG is read, otherwise a map
* with grayscale pixels is allocated and assigned to *map.
*/
static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
{
FILE *fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
fp = fopen(filename, "rb");
if (fp == NULL) {
ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_PNGMAP_ERROR, 0, 0);
return false;
}
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
fclose(fp);
return false;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
fclose(fp);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
png_init_io(png_ptr, fp);
/* Allocate memory and read image, without alpha or 16-bit samples
* (result is either 8-bit indexed/grayscale or 24-bit RGB) */
png_set_packing(png_ptr);
png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
/* Maps of wrong color-depth are not used.
* (this should have been taken care of by stripping alpha and 16-bit samples on load) */
if ((info_ptr->channels != 1) && (info_ptr->channels != 3) && (info_ptr->bit_depth != 8)) {
ShowErrorMessage(STR_PNGMAP_ERR_IMAGE_TYPE, STR_PNGMAP_ERROR, 0, 0);
fclose(fp);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
if (map != NULL) {
*map = malloc(info_ptr->width * info_ptr->height * sizeof(byte));
if (*map == NULL) {
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
fclose(fp);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return false;
}
ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
}
*x = info_ptr->width;
*y = info_ptr->height;
fclose(fp);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return true;
}
#endif /* WITH_PNG */
/**
* The BMP Heightmap loader.
*/
static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
{
uint x, y;
byte gray_palette[256];
if (data->palette != NULL) {
uint i;
bool all_gray = true;
if (info->palette_size != 2) {
for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
}
/**
* For a non-gray palette of size 16 we assume that
* the order of the palette determines the height;
* the first entry is the sea (level 0), the second one
* level 1, etc.
*/
if (info->palette_size == 16 && !all_gray) {
for (i = 0; i < info->palette_size; i++) {
gray_palette[i] = 256 * i / info->palette_size;
}
}
} else {
/**
* For a palette of size 2 we assume that the order of the palette determines the height;
* the first entry is the sea (level 0), the second one is the land (level 1)
*/
gray_palette[0] = 0;
gray_palette[1] = 16;
}
}
/* Read the raw image data and convert in 8-bit grayscale */
for (y = 0; y < info->height; y++) {
byte *pixel = &map[y * info->width];
byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
for (x = 0; x < info->width; x++) {
if (info->bpp != 24) {
*pixel++ = gray_palette[*bitmap++];
} else {
*pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
bitmap += 3;
}
}
}
}
/**
* Reads the heightmap and/or size of the heightmap from a BMP file.
* If map == NULL only the size of the BMP is read, otherwise a map
* with grayscale pixels is allocated and assigned to *map.
*/
static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
{
FILE *f;
BmpInfo info;
BmpData data;
BmpBuffer buffer;
f = fopen(filename, "rb");
if (f == NULL) {
ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_BMPMAP_ERROR, 0, 0);
return false;
}
BmpInitializeBuffer(&buffer, f);
if (!BmpReadHeader(&buffer, &info, &data)) {
ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
fclose(f);
BmpDestroyData(&data);
return false;
}
if (map != NULL) {
if (!BmpReadBitmap(&buffer, &info, &data)) {
ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
fclose(f);
BmpDestroyData(&data);
return false;
}
*map = malloc(info.width * info.height * sizeof(byte));
if (*map == NULL) {
ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_BMPMAP_ERROR, 0, 0);
fclose(f);
BmpDestroyData(&data);
return false;
}
ReadHeightmapBMPImageData(*map, &info, &data);
}
BmpDestroyData(&data);
*x = info.width;
*y = info.height;
fclose(f);
return true;
}
static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
{
/* Defines the detail of the aspect ratio (to avoid doubles) */
const uint num_div = 16384;
uint width, height;
uint row, col;
uint row_pad = 0, col_pad = 0;
uint img_scale;
uint img_row, img_col;
TileIndex tile;
/* Get map size and calculate scale and padding values */
switch (_patches.heightmap_rotation) {
case HM_COUNTER_CLOCKWISE:
width = MapSizeX();
height = MapSizeY();
break;
case HM_CLOCKWISE:
width = MapSizeY();
height = MapSizeX();
break;
default:
NOT_REACHED();
/* Avoids compiler warnings */
return;
}
if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
/* Image is wider than map - center vertically */
img_scale = (width * num_div) / img_width;
row_pad = (height - ((img_height * img_scale) / num_div)) / 2;
} else {
/* Image is taller than map - center horizontally */
img_scale = (height * num_div) / img_height;
col_pad = (width - ((img_width * img_scale) / num_div)) / 2;
}
/* Form the landscape */
for (row = 0; row < height - 1; row++) {
for (col = 0; col < width - 1; col++) {
switch (_patches.heightmap_rotation) {
case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
case HM_CLOCKWISE: tile = TileXY(row, col); break;
default: NOT_REACHED(); return;
}
/* Check if current tile is within the 1-pixel map edge or padding regions */
if ((DistanceFromEdge(tile) <= 1) ||
(row < row_pad) || (row >= (height - row_pad)) ||
(col < col_pad) || (col >= (width - col_pad))) {
SetTileHeight(tile, 0);
} else {
/* Use nearest neighbor resizing to scale map data.
* We rotate the map 45 degrees (counter)clockwise */
img_row = (((row - row_pad) * num_div) / img_scale);
switch (_patches.heightmap_rotation) {
case HM_COUNTER_CLOCKWISE:
img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
break;
case HM_CLOCKWISE:
img_col = (((col - col_pad) * num_div) / img_scale);
break;
default:
NOT_REACHED();
/* Avoids compiler warnings */
return;
}
assert(img_row < img_height);
assert(img_col < img_width);
/* Color scales from 0 to 255, OpenTTD height scales from 0 to 15 */
SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
}
MakeClear(tile, CLEAR_GRASS, 3);
}
}
}
/**
* This function takes care of the fact that land in OpenTTD can never differ
* more than 1 in height
*/
static void FixSlopes(void)
{
uint width, height;
uint row, col;
byte current_tile;
/* Adjust height difference to maximum one horizontal/vertical change. */
width = MapSizeX();
height = MapSizeY();
/* Top and left edge */
for (row = 1; row < height - 2; row++) {
for (col = 1; col < width - 2; col++) {
/* Find lowest tile; either the top or left one */
current_tile = TileHeight(TileXY(col - 1, row)); // top edge
if (TileHeight(TileXY(col, row - 1)) < current_tile) {
current_tile = TileHeight(TileXY(col, row - 1)); // left edge
}
/* Does the height differ more than one? */
if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
/* Then change the height to be no more than one */
SetTileHeight(TileXY(col, row), current_tile + 1);
}
}
}
/* Bottom and right edge */
for (row = height - 2; row > 0; row--) {
for (col = width - 2; col > 0; col--) {
/* Find lowest tile; either the bottom and right one */
current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
if (TileHeight(TileXY(col, row + 1)) < current_tile) {
current_tile = TileHeight(TileXY(col, row + 1)); // right edge
}
/* Does the height differ more than one? */
if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
/* Then change the height to be no more than one */
SetTileHeight(TileXY(col, row), current_tile + 1);
}
}
}
}
/**
* Reads the heightmap with the correct file reader
*/
static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
{
switch (_file_to_saveload.mode) {
#ifdef WITH_PNG
case SL_PNG:
return ReadHeightmapPNG(filename, x, y, map);
#endif /* WITH_PNG */
case SL_BMP:
return ReadHeightmapBMP(filename, x, y, map);
default:
NOT_REACHED();
/* Avoids compiler warnings */
return false;
}
}
bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
{
return ReadHeightMap(filename, x, y, NULL);
}
void LoadHeightmap(char *filename)
{
uint x, y;
byte *map = NULL;
if (!ReadHeightMap(filename, &x, &y, &map)) {
free(map);
return;
}
GrayscaleToMapHeights(x, y, map);
free(map);
FixSlopes();
MarkWholeScreenDirty();
}
void FlatEmptyWorld(byte tile_height)
{
uint width, height;
uint row, col;
width = MapSizeX();
height = MapSizeY();
for (row = 2; row < height - 2; row++) {
for (col = 2; col < width - 2; col++) {
SetTileHeight(TileXY(col, row), tile_height);
}
}
FixSlopes();
MarkWholeScreenDirty();
}

33
heightmap.h Normal file
View File

@ -0,0 +1,33 @@
/* $Id$ */
#ifndef HEIGHTMAP_H
#define HEIGHTMAP_H
/*
* Order of these enums has to be the same as in lang/english.txt
* Otherwise you will get inconsistent behaviour.
*/
enum {
HM_COUNTER_CLOCKWISE, //! Rotate the map counter clockwise 45 degrees
HM_CLOCKWISE, //! Rotate the map clockwise 45 degrees
};
/**
* Get the dimensions of a heightmap.
* @return Returns false if loading of the image failed.
*/
bool GetHeightmapDimensions(char *filename, uint *x, uint *y);
/**
* Load a heightmap from file and change the map in his current dimensions
* to a landscape representing the heightmap.
* It converts pixels to height. The brighter, the higher.
*/
void LoadHeightmap(char *filename);
/**
* Make an empty world where all tiles are of height 'tile_height'.
*/
void FlatEmptyWorld(byte tile_height);
#endif /* HEIGHTMAP_H */

View File

@ -22,6 +22,7 @@
#include "variables.h"
#include "table/industry_land.h"
#include "table/build_industry.h"
#include "genworld.h"
#include "date.h"
enum {
@ -1026,7 +1027,7 @@ static bool CheckNewIndustry_Forest(TileIndex tile)
static bool CheckNewIndustry_OilRefinery(TileIndex tile)
{
if (_game_mode == GM_EDITOR) return true;
if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < 16) return true;
if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true;
_error_message = STR_483B_CAN_ONLY_BE_POSITIONED;
return false;
@ -1038,7 +1039,7 @@ static bool CheckNewIndustry_OilRig(TileIndex tile)
{
if (_game_mode == GM_EDITOR && _ignore_restrictions) return true;
if (TileHeight(tile) == 0 &&
DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < 16) return true;
DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true;
_error_message = STR_483B_CAN_ONLY_BE_POSITIONED;
return false;
@ -1161,7 +1162,7 @@ static const byte _industry_section_bits[] = {
16, 16, 16, 16, 16, 16, 16,
};
static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, int type, const Town *t)
static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, int type)
{
_error_message = STR_0239_SITE_UNSUITABLE;
@ -1191,22 +1192,27 @@ static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable
tileh = GetTileSlope(cur_tile, NULL);
if (IsSteepSlope(tileh)) return false;
if (tileh != SLOPE_FLAT) {
Slope t;
byte bits = _industry_section_bits[it->gfx];
if (_patches.land_generator == LG_TERRAGENESIS || !_generating_world) {
/* It is almost impossible to have a fully flat land in TG, so what we
* do is that we check if we can make the land flat later on. See
* CheckIfCanLevelIndustryPlatform(). */
if (tileh != SLOPE_FLAT) {
Slope t;
byte bits = _industry_section_bits[it->gfx];
if (bits & 0x10) return false;
if (bits & 0x10) return false;
t = ComplementSlope(tileh);
t = ComplementSlope(tileh);
if (bits & 1 && (t & SLOPE_NW)) return false;
if (bits & 2 && (t & SLOPE_NE)) return false;
if (bits & 4 && (t & SLOPE_SW)) return false;
if (bits & 8 && (t & SLOPE_SE)) return false;
if (bits & 1 && (t & SLOPE_NW)) return false;
if (bits & 2 && (t & SLOPE_NE)) return false;
if (bits & 4 && (t & SLOPE_SW)) return false;
if (bits & 8 && (t & SLOPE_SE)) return false;
}
}
if (type == IT_BANK_TEMP) {
if (!IsTileType(cur_tile, MP_HOUSE) || t->population < 1200) {
if (!IsTileType(cur_tile, MP_HOUSE)) {
_error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS;
return false;
}
@ -1216,7 +1222,6 @@ static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable
return false;
}
} else if (type == IT_TOY_SHOP) {
if (DistanceMax(t->xy, cur_tile) > 9) return false;
if (!IsTileType(cur_tile, MP_HOUSE)) goto do_clear;
} else if (type == IT_WATER_TOWER) {
if (!IsTileType(cur_tile, MP_HOUSE)) {
@ -1235,6 +1240,115 @@ do_clear:
return true;
}
static bool CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town *t)
{
if (type == IT_BANK_TEMP && t->population < 1200) {
_error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS;
return false;
}
if (type == IT_TOY_SHOP && DistanceMax(t->xy, tile) > 9) {
_error_message = STR_0239_SITE_UNSUITABLE;
return false;
}
return true;
}
static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal)
{
int size_x, size_y;
uint curh;
size_x = 2;
size_y = 2;
/* Check if we don't leave the map */
if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false;
tile += TileDiffXY(-1, -1);
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, tile) {
curh = TileHeight(tile_walk);
/* Is the tile clear? */
if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES))
return false;
/* Don't allow too big of a change if this is the sub-tile check */
if (internal != 0 && myabs(curh - height) > 1) return false;
/* Different height, so the surrounding tiles of this tile
* has to be correct too (in level, or almost in level)
* else you get a chain-reaction of terraforming. */
if (internal == 0 && curh != height) {
if (!CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1))
return false;
}
} END_TILE_LOOP(tile_walk, size_x, size_y, tile);
return true;
}
/**
* This function tries to flatten out the land below an industry, without
* damaging the surroundings too much.
*/
static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, uint32 flags, const IndustryTileTable* it, int type)
{
const int MKEND = -0x80; // used for last element in an IndustryTileTable (see build_industry.h)
int max_x = 0;
int max_y = 0;
TileIndex cur_tile;
uint size_x, size_y;
uint h, curh;
/* Finds dimensions of largest variant of this industry */
do {
if (it->ti.x > max_x) max_x = it->ti.x;
if (it->ti.y > max_y) max_y = it->ti.y;
} while ((++it)->ti.x != MKEND);
/* Remember level height */
h = TileHeight(tile);
/* Check that all tiles in area and surrounding are clear
* this determines that there are no obstructing items */
cur_tile = tile + TileDiffXY(-1, -1);
size_x = max_x + 4;
size_y = max_y + 4;
/* Check if we don't leave the map */
if (TileX(cur_tile) == 0 || TileY(cur_tile) == 0 || GetTileType(cur_tile) == MP_VOID) return false;
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
curh = TileHeight(tile_walk);
if (curh != h) {
/* This tile needs terraforming. Check if we can do that without
* damaging the surroundings too much. */
if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) return false;
/* This is not 100% correct check, but the best we can do without modifying the map.
* What is missing, is if the difference in height is more than 1.. */
if (CmdFailed(DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) return false;
}
} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
if (flags & DC_EXEC) {
/* Terraform the land under the industry */
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
curh = TileHeight(tile_walk);
while (curh != h) {
/* We give the terraforming for free here, because we can't calculate
* exact cost in the test-round, and as we all know, that will cause
* a nice assert if they don't match ;) */
DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
curh += (curh > h) ? -1 : 1;
}
} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
}
return true;
}
static bool CheckIfTooCloseToIndustry(TileIndex tile, int type)
{
const IndustrySpec *indspec = GetIndustrySpec(type);
@ -1373,6 +1487,33 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const Ind
InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0);
}
static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, const IndustryTileTable *it)
{
const Town *t;
Industry *i;
if (!CheckIfIndustryTilesAreFree(tile, it, type)) return NULL;
if (_patches.land_generator == LG_TERRAGENESIS && _generating_world && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL;
if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL;
if (!CheckIfTooCloseToIndustry(tile, type)) return NULL;
t = CheckMultipleIndustryInTown(tile, type);
if (t == NULL) return NULL;
if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL;
if (!CheckSuitableIndustryPos(tile)) return NULL;
i = AllocateIndustry();
if (i == NULL) return NULL;
if (flags & DC_EXEC) {
CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type);
DoCreateNewIndustry(i, tile, type, it, t, OWNER_NONE);
}
return i;
}
/** Build/Fund an industry
* @param tile tile where industry is built
* @param p1 industry type @see build_industry.h and @see industry.h
@ -1380,8 +1521,6 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const Ind
*/
int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
{
const Town *t;
Industry *i;
int num;
const IndustryTileTable * const *itt;
const IndustryTileTable *it;
@ -1389,8 +1528,6 @@ int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
SET_EXPENSES_TYPE(EXPENSES_OTHER);
if (!CheckSuitableIndustryPos(tile)) return CMD_ERROR;
/* Check if the to-be built/founded industry is available for this climate.
* Unfortunately we have no easy way of checking, except for looping the table */
{
@ -1418,25 +1555,14 @@ int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
return CMD_ERROR;
}
if (!_check_new_industry_procs[indspec->check_proc](tile)) return CMD_ERROR;
t = CheckMultipleIndustryInTown(tile, p1);
if (t == NULL) return CMD_ERROR;
num = indspec->num_table;
itt = indspec->table;
do {
if (--num < 0) return_cmd_error(STR_0239_SITE_UNSUITABLE);
} while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1, t));
} while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1));
if (!CheckIfTooCloseToIndustry(tile, p1)) return CMD_ERROR;
i = AllocateIndustry();
if (i == NULL) return CMD_ERROR;
if (flags & DC_EXEC) DoCreateNewIndustry(i, tile, p1, it, t, OWNER_NONE);
if (CreateNewIndustryHelper(tile, p1, flags, indspec, it) == NULL) return CMD_ERROR;
return (_price.build_industry >> 5) * indspec->cost_multiplier;
}
@ -1444,33 +1570,10 @@ int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
Industry *CreateNewIndustry(TileIndex tile, IndustryType type)
{
const Town *t;
const IndustryTileTable *it;
Industry *i;
const IndustrySpec *indspec = GetIndustrySpec(type);
const IndustryTileTable *it = indspec->table[RandomRange(indspec->num_table)];
const IndustrySpec *indspec;
if (!CheckSuitableIndustryPos(tile)) return NULL;
indspec = GetIndustrySpec(type);
if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL;
t = CheckMultipleIndustryInTown(tile, type);
if (t == NULL) return NULL;
/* pick a random layout */
it = indspec->table[RandomRange(indspec->num_table)];
if (!CheckIfIndustryTilesAreFree(tile, it, type, t)) return NULL;
if (!CheckIfTooCloseToIndustry(tile, type)) return NULL;
i = AllocateIndustry();
if (i == NULL) return NULL;
DoCreateNewIndustry(i, tile, type, it, t, OWNER_NONE);
return i;
return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, it);
}
static const byte _numof_industry_table[4][12] = {
@ -1500,6 +1603,8 @@ static void PlaceInitialIndustry(IndustryType type, int amount)
do {
uint i;
IncreaseGeneratingWorldProgress(GWP_INDUSTRY);
for (i = 0; i < 2000; i++) {
if (CreateNewIndustry(RandomTile(), type) != NULL) break;
}
@ -1512,6 +1617,23 @@ static void PlaceInitialIndustry(IndustryType type, int amount)
void GenerateIndustries(void)
{
const byte *b;
uint i = 0;
/* Find the total amount of industries */
b = _industry_create_table[_opt.landscape];
do {
int num = _numof_industry_table[_opt.diff.number_industries][b[0]];
if (b[1] == IT_OIL_REFINERY || b[1] == IT_OIL_RIG) {
/* These are always placed next to the coastline, so we scale by the perimeter instead. */
num = ScaleByMapSize1D(num);
} else {
num = ScaleByMapSize(num);
}
i += num;
} while ( (b+=2)[0] != 0);
SetGeneratingWorldProgress(GWP_INDUSTRY, i);
b = _industry_create_table[_opt.landscape];
do {

View File

@ -11,40 +11,34 @@
#include "network.h"
#include "variables.h"
#include "settings.h"
extern void SwitchMode(int new_mode);
#include "heightmap.h"
#include "genworld.h"
static const Widget _select_game_widgets[] = {
{ WWT_CAPTION, RESIZE_NONE, 13, 0, 335, 0, 13, STR_0307_OPENTTD, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 13, 0, 335, 14, 196, STR_NULL, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 13, 0, 335, 14, 176, STR_NULL, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 22, 33, STR_0140_NEW_GAME, STR_02FB_START_A_NEW_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 22, 33, STR_0141_LOAD_GAME, STR_02FC_LOAD_A_SAVED_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 40, 51, STR_0220_CREATE_SCENARIO,STR_02FE_CREATE_A_CUSTOMIZED_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 40, 51, STR_029A_PLAY_SCENARIO, STR_0303_START_A_NEW_GAME_USING},
{ WWT_PANEL_2, RESIZE_NONE, 12, 10, 86, 59, 113, 0x1312, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 90, 166, 59, 113, 0x1314, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 170, 246, 59, 113, 0x1316, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 250, 326, 59, 113, 0x1318, STR_0311_SELECT_TOYLAND_LANDSCAPE},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 40, 51, STR_029A_PLAY_SCENARIO, STR_0303_START_A_NEW_GAME_USING},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 40, 51, STR_PLAY_HEIGHTMAP, STR_PLAY_HEIGHTMAP_HINT},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 58, 69, STR_0220_CREATE_SCENARIO,STR_02FE_CREATE_A_CUSTOMIZED_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 58, 69, STR_MULTIPLAYER, STR_0300_SELECT_MULTIPLAYER_GAME},
{ WWT_PANEL, RESIZE_NONE, 12, 219, 254, 120, 131, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 255, 266, 120, 131, STR_0225, STR_NULL},
{ WWT_PANEL, RESIZE_NONE, 12, 279, 314, 120, 131, STR_NULL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 315, 326, 120, 131, STR_0225, STR_NULL},
{ WWT_PANEL_2, RESIZE_NONE, 12, 10, 86, 77, 131, 0x1312, STR_030E_SELECT_TEMPERATE_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 90, 166, 77, 131, 0x1314, STR_030F_SELECT_SUB_ARCTIC_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 170, 246, 77, 131, 0x1316, STR_0310_SELECT_SUB_TROPICAL_LANDSCAPE},
{ WWT_PANEL_2, RESIZE_NONE, 12, 250, 326, 77, 131, 0x1318, STR_0311_SELECT_TOYLAND_LANDSCAPE},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 138, 149, STR_SINGLE_PLAYER, STR_02FF_SELECT_SINGLE_PLAYER_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 138, 149, STR_MULTIPLAYER, STR_0300_SELECT_MULTIPLAYER_GAME},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 159, 170, STR_0148_GAME_OPTIONS, STR_0301_DISPLAY_GAME_OPTIONS},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 159, 170, STR_01FE_DIFFICULTY, STR_0302_DISPLAY_DIFFICULTY_OPTIONS},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 177, 188, STR_CONFIG_PATCHES, STR_CONFIG_PATCHES_TIP},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 177, 188, STR_0304_QUIT, STR_0305_QUIT_OPENTTD},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 139, 150, STR_0148_GAME_OPTIONS, STR_0301_DISPLAY_GAME_OPTIONS},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 139, 150, STR_01FE_DIFFICULTY, STR_0302_DISPLAY_DIFFICULTY_OPTIONS},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 10, 167, 157, 168, STR_CONFIG_PATCHES, STR_CONFIG_PATCHES_TIP},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 12, 168, 325, 157, 168, STR_0304_QUIT, STR_0305_QUIT_OPENTTD},
{ WIDGETS_END },
};
extern void HandleOnEditText(WindowEvent *e);
extern void HandleOnEditTextCancel(void);
static inline void CreateScenario(void) {_switch_mode = SM_EDITOR;}
static inline void SetNewLandscapeType(byte landscape)
{
_opt_newgame.landscape = landscape;
@ -53,37 +47,21 @@ static inline void SetNewLandscapeType(byte landscape)
static void SelectGameWndProc(Window *w, WindowEvent *e)
{
/* We do +/- 6 for the map_xy because 64 is 2^6, but it is the lowest available element */
static const StringID mapsizes[] = {STR_64, STR_128, STR_256, STR_512, STR_1024, STR_2048, INVALID_STRING_ID};
switch (e->event) {
case WE_PAINT:
w->click_state = (w->click_state & ~(1 << 14) & ~(0xF << 6)) | (1 << (_opt_newgame.landscape + 6)) | (1 << 14);
w->click_state = (w->click_state & ~(0xF << 8)) | (1 << (_opt_newgame.landscape + 8));
SetDParam(0, STR_6801_EASY + _opt_newgame.diff_level);
DrawWindowWidgets(w);
DrawStringRightAligned(216, 121, STR_MAPSIZE, 0);
DrawString(223, 121, mapsizes[_patches_newgame.map_x - 6], 0x10);
DrawString(270, 121, STR_BY, 0);
DrawString(283, 121, mapsizes[_patches_newgame.map_y - 6], 0x10);
break;
case WE_CLICK:
switch (e->click.widget) {
case 2: AskForNewGameToStart(); break;
case 2: ShowGenerateLandscape(); break;
case 3: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
case 4: CreateScenario(); break;
case 5: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
case 6: case 7: case 8: case 9:
SetNewLandscapeType(e->click.widget - 6);
break;
case 10: case 11: /* Mapsize X */
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_x - 6, 11, 0, 0);
break;
case 12: case 13: /* Mapsize Y */
ShowDropDownMenu(w, mapsizes, _patches_newgame.map_y - 6, 13, 0, 0);
break;
case 15:
case 4: ShowSaveLoadDialog(SLD_LOAD_SCENARIO); break;
case 5: ShowSaveLoadDialog(SLD_LOAD_HEIGHTMAP); break;
case 6: ShowCreateScenario(); break;
case 7:
#ifdef ENABLE_NETWORK
if (!_network_available) {
ShowErrorMessage(INVALID_STRING_ID, STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
@ -94,32 +72,20 @@ static void SelectGameWndProc(Window *w, WindowEvent *e)
ShowErrorMessage(INVALID_STRING_ID ,STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
#endif
break;
case 16: ShowGameOptions(); break;
case 17: ShowGameDifficulty(); break;
case 18: ShowPatchesSelection(); break;
case 19: AskExitGame(); break;
case 8: case 9: case 10: case 11:
SetNewLandscapeType(e->click.widget - 8);
break;
case 12: ShowGameOptions(); break;
case 13: ShowGameDifficulty(); break;
case 14: ShowPatchesSelection(); break;
case 15: AskExitGame(); break;
}
break;
case WE_ON_EDIT_TEXT: HandleOnEditText(e); break;
case WE_ON_EDIT_TEXT_CANCEL: HandleOnEditTextCancel(); break;
case WE_DROPDOWN_SELECT: /* Mapsize selection */
switch (e->dropdown.button) {
/* We need a *hacky* here because generateworld is called with _patches
* but it only gets the new value INSIDE generateworld so not setting it would
* break generating a new game on the run (eg MP) */
case 11: _patches.map_x = _patches_newgame.map_x = e->dropdown.index + 6; break;
case 13: _patches.map_y = _patches_newgame.map_y = e->dropdown.index + 6; break;
}
SetWindowDirty(w);
break;
}
}
static const WindowDesc _select_game_desc = {
WDP_CENTER, WDP_CENTER, 336, 197,
WDP_CENTER, WDP_CENTER, 336, 177,
WC_SELECT_GAME,0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
_select_game_widgets,
@ -131,22 +97,6 @@ void ShowSelectGameWindow(void)
AllocateWindowDesc(&_select_game_desc);
}
void GenRandomNewGame(uint32 rnd1, uint32 rnd2)
{
_random_seeds[0][0] = rnd1;
_random_seeds[0][1] = rnd2;
SwitchMode(SM_NEWGAME);
}
void StartScenarioEditor(uint32 rnd1, uint32 rnd2)
{
_random_seeds[0][0] = rnd1;
_random_seeds[0][1] = rnd2;
SwitchMode(SM_START_SCENARIO);
}
static const Widget _ask_abandon_game_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 4, 11, 179, 0, 13, STR_00C7_QUIT, STR_NULL},

View File

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "openttd.h"
#include "heightmap.h"
#include "clear_map.h"
#include "functions.h"
#include "map.h"
@ -16,6 +17,9 @@
#include "variables.h"
#include "void_map.h"
#include "water_map.h"
#include "tgp.h"
#include "genworld.h"
#include "heightmap.h"
extern const TileTypeProcs
_tile_type_clear_procs,
@ -409,10 +413,34 @@ void InitializeLandscape(void)
void ConvertGroundTilesIntoWaterTiles(void)
{
TileIndex tile;
uint z;
Slope slope;
for (tile = 0; tile < MapSize(); ++tile) {
if (IsTileType(tile, MP_CLEAR) && GetTileMaxZ(tile) == 0) {
MakeWater(tile);
slope = GetTileSlope(tile, &z);
if (IsTileType(tile, MP_CLEAR) && z == 0) {
/* Make both water for tiles at level 0
* and make shore, as that looks much better
* during the generation. */
switch (slope) {
case SLOPE_FLAT:
MakeWater(tile);
break;
case SLOPE_N:
case SLOPE_E:
case SLOPE_S:
case SLOPE_W:
case SLOPE_NW:
case SLOPE_SW:
case SLOPE_SE:
case SLOPE_NE:
MakeShore(tile);
break;
default:
break;
}
}
}
}
@ -547,10 +575,13 @@ static void GenerateTerrain(int type, int flag)
static void CreateDesertOrRainForest(void)
{
TileIndex tile;
TileIndex update_freq = MapSize() / 4;
const TileIndexDiffC *data;
uint i;
for (tile = 0; tile != MapSize(); ++tile) {
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
for (data = _make_desert_or_rainforest_data;
data != endof(_make_desert_or_rainforest_data); ++data) {
TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
@ -560,10 +591,15 @@ static void CreateDesertOrRainForest(void)
SetTropicZone(tile, TROPICZONE_DESERT);
}
for (i = 0; i != 256; i++)
for (i = 0; i != 256; i++) {
if ((i % 64) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
RunTileLoop();
}
for (tile = 0; tile != MapSize(); ++tile) {
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
for (data = _make_desert_or_rainforest_data;
data != endof(_make_desert_or_rainforest_data); ++data) {
TileIndex t = TILE_MASK(tile + ToTileIndexDiff(*data));
@ -574,49 +610,71 @@ static void CreateDesertOrRainForest(void)
}
}
void GenerateLandscape(void)
void GenerateLandscape(byte mode)
{
const int gwp_desert_amount = 4 + 8;
uint i;
uint flag;
uint32 r;
switch (_opt.landscape) {
case LT_HILLY:
for (i = ScaleByMapSize((Random() & 0x7F) + 950); i != 0; --i) {
GenerateTerrain(2, 0);
}
if (mode == GW_HEIGHTMAP) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 1 + gwp_desert_amount : 1);
LoadHeightmap(_file_to_saveload.name);
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
} else if (_patches.land_generator == LG_TERRAGENESIS) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, (_opt.landscape == LT_DESERT) ? 3 + gwp_desert_amount : 3);
GenerateTerrainPerlin();
} else {
switch (_opt.landscape) {
case LT_HILLY:
SetGeneratingWorldProgress(GWP_LANDSCAPE, 2);
r = Random();
flag = GB(r, 0, 2) | 4;
for (i = ScaleByMapSize(GB(r, 16, 7) + 450); i != 0; --i) {
GenerateTerrain(4, flag);
}
break;
for (i = ScaleByMapSize((Random() & 0x7F) + 950); i != 0; --i) {
GenerateTerrain(2, 0);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
case LT_DESERT:
for (i = ScaleByMapSize((Random() & 0x7F) + 170); i != 0; --i) {
GenerateTerrain(0, 0);
}
r = Random();
flag = GB(r, 0, 2) | 4;
for (i = ScaleByMapSize(GB(r, 16, 7) + 450); i != 0; --i) {
GenerateTerrain(4, flag);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
break;
r = Random();
flag = GB(r, 0, 2) | 4;
for (i = ScaleByMapSize(GB(r, 16, 8) + 1700); i != 0; --i) {
GenerateTerrain(0, flag);
}
case LT_DESERT:
SetGeneratingWorldProgress(GWP_LANDSCAPE, 3 + gwp_desert_amount);
flag ^= 2;
for (i = ScaleByMapSize((Random() & 0x7F) + 170); i != 0; --i) {
GenerateTerrain(0, 0);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
for (i = ScaleByMapSize((Random() & 0x7F) + 410); i != 0; --i) {
GenerateTerrain(3, flag);
}
break;
r = Random();
flag = GB(r, 0, 2) | 4;
for (i = ScaleByMapSize(GB(r, 16, 8) + 1700); i != 0; --i) {
GenerateTerrain(0, flag);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
default:
i = ScaleByMapSize((Random() & 0x7F) + (3 - _opt.diff.quantity_sea_lakes) * 256 + 100);
for (; i != 0; --i) {
GenerateTerrain(_opt.diff.terrain_type, 0);
}
break;
flag ^= 2;
for (i = ScaleByMapSize((Random() & 0x7F) + 410); i != 0; --i) {
GenerateTerrain(3, flag);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
break;
default:
SetGeneratingWorldProgress(GWP_LANDSCAPE, 1);
i = ScaleByMapSize((Random() & 0x7F) + (3 - _opt.diff.quantity_sea_lakes) * 256 + 100);
for (; i != 0; --i) {
GenerateTerrain(_opt.diff.terrain_type, 0);
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
break;
}
}
ConvertGroundTilesIntoWaterTiles();

View File

@ -604,6 +604,8 @@ STR_0229_DECREASE_SIZE_OF_LAND_AREA :{BLACK}Decrease
STR_022A_GENERATE_RANDOM_LAND :{BLACK}Generate random land
STR_022B_RESET_LANDSCAPE :{BLACK}Reset landscape
STR_022C_RESET_LANDSCAPE :{WHITE}Reset Landscape
STR_LOAD_GAME_HEIGHTMAP :{WHITE}Use Heightmap
STR_LOAD_SCEN_HEIGHTMAP :{BLACK}Use Heightmap
STR_022D_ARE_YOU_SURE_YOU_WANT_TO :{WHITE}Are you sure you want to reset the landscape?
STR_022E_LANDSCAPE_GENERATION :{BLACK}Landscape generation
STR_022F_TOWN_GENERATION :{BLACK}Town generation
@ -716,6 +718,8 @@ STR_0297_SAVE_SCENARIO_LOAD_SCENARIO :{BLACK}Save sce
STR_0298_LOAD_SCENARIO :{WHITE}Load Scenario
STR_0299_SAVE_SCENARIO :{WHITE}Save Scenario
STR_029A_PLAY_SCENARIO :{BLACK}Play Scenario
STR_PLAY_HEIGHTMAP :{BLACK}Play Heightmap
STR_PLAY_HEIGHTMAP_HINT :{BLACK}Start a new game, using a heightmap as landscape
STR_029B_ARE_YOU_SURE_YOU_WANT_TO :{YELLOW}Are you sure you want to quit this scenario ?
STR_029C_QUIT_EDITOR :{WHITE}Quit Editor
STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS :{WHITE}...can only be built in towns with a population of at least 1200
@ -1039,7 +1043,27 @@ STR_CONFIG_PATCHES_AUTORENEW_MONEY :{LTBLUE}Autoren
STR_CONFIG_PATCHES_ERRMSG_DURATION :{LTBLUE}Duration of error message: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_POPULATION_IN_LABEL :{LTBLUE}Show town population in the town name label: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_INVISIBLE_TREES :{LTBLUE}Invisible trees (with transparent buildings): {ORANGE}{STRING1}
STR_CONFIG_PATCHES_LAND_GENERATOR :{LTBLUE}Land generator: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL :Original
STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis
STR_CONFIG_PATCHES_OIL_REF_EDGE_DISTANCE :{LTBLUE}Max distance from edge for Oil Refineries {ORANGE}{STRING1}
STR_CONFIG_PATCHES_SNOWLINE_HEIGHT :{LTBLUE}Snow line height: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN :{LTBLUE}Roughness of terrain (TerraGenesis only) : {ORANGE}{STRING1}
STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH :Very Smooth
STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH :Smooth
STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH :Rough
STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH :Very Rough
STR_CONFIG_PATCHES_TREE_PLACER :{LTBLUE}Tree placer algorithm: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_TREE_PLACER_NONE :None
STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL :Original
STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED :Improved
STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION :{LTBLUE}Heightmap rotation: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE :Counter clockwise
STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE :Clockwise
STR_CONFIG_PATCHES_PROGRESS_UPDATE_INTERVAL :{LTBLUE}Progress update interval: {ORANGE}{STRING1} ms
STR_CONFIG_PATCHES_SE_FLAT_WORLD_HEIGHT :{LTBLUE}The height level a flat scenario map gets: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_STATION_SPREAD :{LTBLUE}Max station spread: {ORANGE}{STRING1} {RED}Warning: High setting slows game
STR_CONFIG_PATCHES_SERVICEATHELIPAD :{LTBLUE}Service helicopters at helipads automatically: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_LINK_TERRAFORM_TOOLBAR :{LTBLUE}Link landscape toolbar to rail/road/water/airport toolbars: {ORANGE}{STRING1}
@ -1434,6 +1458,17 @@ STR_NETWORK_SEND :{BLACK}Send
STR_CONFIG_PATCHES_MAP_X :{LTBLUE}X-size of map: {ORANGE}{STRING1}
STR_CONFIG_PATCHES_MAP_Y :{LTBLUE}Y-size of map: {ORANGE}{STRING1}
##### PNG-MAP-Loader
STR_PNGMAP_ERROR :{WHITE}Cannot load landscape from PNG...
STR_PNGMAP_ERR_FILE_NOT_FOUND :{WHITE}...file not found.
STR_PNGMAP_ERR_IMAGE_TYPE :{WHITE}...could not convert image type. 8 or 24-bit PNG image needed.
STR_PNGMAP_ERR_MISC :{WHITE}...something just went wrong. Sorry. (probably corrupted file)
STR_BMPMAP_ERROR :{WHITE}Cannot load landscape from BMP...
STR_BMPMAP_ERR_IMAGE_TYPE :{WHITE}...could not convert image type.
##id 0x0800
STR_0800_COST :{TINYFONT}{RED}Cost: {CURRENCY}
STR_0801_COST :{RED}Cost: {CURRENCY}
@ -1757,6 +1792,7 @@ STR_400D_SAVE_THE_CURRENT_GAME_USING :{BLACK}Save the
STR_400E_SELECT_NEW_GAME_TYPE :{WHITE}Select New Game Type
STR_400F_SELECT_SCENARIO_GREEN_PRE :{BLACK}Select scenario (green), pre-set game (blue), or random new game
STR_4010_GENERATE_RANDOM_NEW_GAME :Generate random new game
STR_4011_LOAD_HEIGHTMAP :{WHITE}Load Heightmap
##id 0x4800
STR_4800_IN_THE_WAY :{WHITE}{STRING} in the way
@ -2863,6 +2899,58 @@ STR_PURCHASE_INFO_COST_SPEED :{BLACK}Cost: {G
STR_PURCHASE_INFO_AIRCRAFT_CAPACITY :{BLACK}Capacity: {GOLD}{COMMA} passengers, {COMMA} bags of mail
STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT :{BLACK}Powered Wagons: {GOLD}+{POWER}{BLACK} Weight: {GOLD}+{WEIGHT_S}
########### String for New Landscape Generator
STR_GENERATE :{WHITE}Generate
STR_RANDOM :{BLACK}Randomise
STR_RANDOM_HELP :{BLACK}Change the random seed used for Terrain Generation
STR_WORLD_GENERATION_CAPTION :{WHITE}World generation
STR_RANDOM_SEED :{BLACK}Random Seed:
STR_RANDOM_SEED_HELP :{BLACK}Click to enter a random seed
STR_LAND_GENERATOR :{BLACK}Land generator:
STR_TREE_PLACER :{BLACK}Tree algorithm:
STR_HEIGHTMAP_ROTATION :{BLACK}Heightmap rotation:
STR_TERRAIN_TYPE :{BLACK}Terrain type:
STR_QUANTITY_OF_SEA_LAKES :{BLACK}Sea level:
STR_SMOOTHNESS :{BLACK}Smoothness:
STR_SNOW_LINE_HEIGHT :{BLACK}Snow line height:
STR_DATE :{BLACK}Date:
STR_NUMBER_OF_TOWNS :{BLACK}No. of towns:
STR_NUMBER_OF_INDUSTRIES :{BLACK}No. of industries:
STR_GENERATE_DATE :{BLACK}{DATE_LONG}
STR_SNOW_LINE_UP :{BLACK}Move the snow line height one up
STR_SNOW_LINE_DOWN :{BLACK}Move the snow line height one down
STR_SNOW_LINE_QUERY_CAPT :{WHITE}Change snow line height
STR_START_DATE_QUERY_CAPT :{WHITE}Change starting year
STR_HEIGHTMAP_SCALE_WARNING_CAPTION :{WHITE}Scale warning
STR_HEIGHTMAP_SCALE_WARNING_MESSAGE :{YELLOW}Resizing source map too much is not recommended. Continue with the generation?
STR_SNOW_LINE_HEIGHT_NUM :{NUM}
STR_HEIGHTMAP_NAME :{BLACK}Heightmap name:
STR_HEIGHTMAP_SIZE :{BLACK}Size: {ORANGE}{NUM} x {NUM}
STR_GENERATION_WORLD :{WHITE}Generating world...
STR_GENERATION_ABORT :{BLACK}Abort
STR_GENERATION_ABORT_CAPTION :{WHITE}Abort World Generation
STR_GENERATION_ABORT_MESSAGE :{YELLOW}Do you really want to abort the generation?
STR_PROGRESS :{WHITE}{NUM}% complete
STR_GENERATION_PROGRESS :{BLACK}{NUM} / {NUM}
STR_WORLD_GENERATION :{BLACK}World generation
STR_TREE_GENERATION :{BLACK}Tree generation
STR_UNMOVABLE_GENERATION :{BLACK}Unmovable generation
STR_CLEARING_TILES :{BLACK}Rough and rocky area generation
STR_SETTINGUP_GAME :{BLACK}Setting up game
STR_PREPARING_TILELOOP :{BLACK}Running tile-loop
STR_PREPARING_GAME :{BLACK}Preparing game
STR_DIFFICULTY_TO_CUSTOM :{WHITE}This action changed the difficulty level to custom
STR_SE_FLAT_WORLD :{WHITE}Flat land
STR_SE_RANDOM_LAND :{WHITE}Random land
STR_SE_NEW_WORLD :{BLACK}Create new scenario
STR_SE_CAPTION :{WHITE}Scenario type
STR_FLAT_WORLD_HEIGHT_DOWN :{BLACK}Move the height of flat land one down
STR_FLAT_WORLD_HEIGHT_UP :{BLACK}Move the height of flat land one up
STR_FLAT_WORLD_HEIGHT_QUERY_CAPT :{WHITE}Change height of flat land
STR_FLAT_WORLD_HEIGHT :{BLACK}Height of flat land:
STR_FLAT_WORLD_HEIGHT_NUM :{NUM}
########### String for new airports
STR_SMALL_AIRPORT :{BLACK}Small
STR_CITY_AIRPORT :{BLACK}City

View File

@ -180,4 +180,10 @@ static inline uint16 ReadLE16Unaligned(const void* x)
*/
#define ALIGN(x, n) (((x) + (n) - 1) & ~((n) - 1))
/** return the largest value that can be entered in a variable.
* known to work for uint32.
* used by TGP to set the max value of the _patches.generation_seed in its definition
*/
#define MAX_UVALUE(type) ((type)~(type)0)
#endif /* MACROS_H */

View File

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "openttd.h"
#include "heightmap.h"
#include "currency.h"
#include "functions.h"
#include "spritecache.h"
@ -29,22 +30,19 @@
#include "train.h"
#include "unmovable_map.h"
#include "screenshot.h"
#include "genworld.h"
#include "settings.h"
#include "date.h"
#include "network_data.h"
#include "network_client.h"
#include "network_server.h"
/* Min/Max date for scenario editor */
static const Date MinDate = 0; // 1920-01-01 (MAX_YEAR_BEGIN_REAL)
static const Date MaxDate = 29220; // 2000-01-01
static int _rename_id;
static int _rename_what;
static byte _terraform_size = 1;
static RailType _last_built_railtype;
extern void GenerateWorld(int mode, uint size_x, uint size_y);
extern void GenerateIndustries(void);
extern bool GenerateTowns(void);
@ -1002,9 +1000,10 @@ static void ToolbarScenDateBackward(Window *w)
// don't allow too fast scrolling
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, 6);
InvalidateWidget(w, 5);
SetWindowDirty(w);
if (_date > MinDate) SetDate(ConvertYMDToDate(_cur_year - 1, 0, 1));
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year - 1, MIN_YEAR, MAX_YEAR);
SetDate(ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
}
_left_button_clicked = false;
}
@ -1014,9 +1013,10 @@ static void ToolbarScenDateForward(Window *w)
// don't allow too fast scrolling
if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
HandleButtonClick(w, 7);
InvalidateWidget(w, 5);
SetWindowDirty(w);
if (_date < MaxDate) SetDate(ConvertYMDToDate(_cur_year + 1, 0, 1));
_patches_newgame.starting_year = clamp(_patches_newgame.starting_year + 1, MIN_YEAR, MAX_YEAR);
SetDate(ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
}
_left_button_clicked = false;
}
@ -1051,7 +1051,7 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w)
vp = w->viewport;
if (_game_mode != GM_MENU) {
if (_game_mode != GM_MENU && !IsGeneratingWorld()) {
if ((in && vp->zoom == 0) || (!in && vp->zoom == 2))
return;
@ -1064,74 +1064,6 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w)
}
}
static void ResetLandscape(void)
{
_random_seeds[0][0] = InteractiveRandom();
_random_seeds[0][1] = InteractiveRandom();
GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y);
MarkWholeScreenDirty();
}
static const Widget _ask_reset_landscape_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 4, 11, 179, 0, 13, STR_022C_RESET_LANDSCAPE, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 4, 0, 179, 14, 91, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 25, 84, 72, 83, STR_00C9_NO, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 95, 154, 72, 83, STR_00C8_YES, STR_NULL},
{ WIDGETS_END},
};
// Ask first to reset landscape or to make a random landscape
static void AskResetLandscapeWndProc(Window *w, WindowEvent *e)
{
uint mode = w->window_number;
switch (e->event) {
case WE_PAINT:
DrawWindowWidgets(w);
DrawStringMultiCenter(
90, 38,
mode ? STR_022D_ARE_YOU_SURE_YOU_WANT_TO : STR_GENERATE_RANDOM_LANDSCAPE,
168
);
break;
case WE_CLICK:
switch (e->click.widget) {
case 3:
DeleteWindow(w);
break;
case 4:
DeleteWindow(w);
DeleteWindowByClass(WC_INDUSTRY_VIEW);
DeleteWindowByClass(WC_TOWN_VIEW);
DeleteWindowByClass(WC_LAND_INFO);
if (mode) { // reset landscape
ResetLandscape();
} else { // make random landscape
SndPlayFx(SND_15_BEEP);
_switch_mode = SM_GENRANDLAND;
}
break;
}
break;
}
}
static const WindowDesc _ask_reset_landscape_desc = {
230,205, 180, 92,
WC_ASK_RESET_LANDSCAPE,0,
WDF_STD_BTN | WDF_DEF_WIDGET,
_ask_reset_landscape_widgets,
AskResetLandscapeWndProc,
};
static void AskResetLandscape(uint mode)
{
AllocateWindowDescFront(&_ask_reset_landscape_desc, mode);
}
// TODO - Incorporate into game itself to allow for ingame raising/lowering of
// larger chunks at the same time OR remove altogether, as we have 'level land' ?
/**
@ -1238,8 +1170,7 @@ static const Widget _scen_edit_land_gen_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 7, 11, 169, 0, 13, STR_0223_LAND_GENERATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_STICKYBOX, RESIZE_NONE, 7, 170, 181, 0, 13, STR_NULL, STR_STICKY_BUTTON},
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 181, 14, 101, STR_NULL, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 181, 14, 95, STR_NULL, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 14, 2, 23, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC},
{ WWT_IMGBTN, RESIZE_NONE, 14, 24, 45, 14, 35, SPR_IMG_TERRAFORM_DOWN, STR_018E_LOWER_A_CORNER_OF_LAND},
{ WWT_IMGBTN, RESIZE_NONE, 14, 46, 67, 14, 35, SPR_IMG_TERRAFORM_UP, STR_018F_RAISE_A_CORNER_OF_LAND},
@ -1250,8 +1181,7 @@ static const Widget _scen_edit_land_gen_widgets[] = {
{ WWT_IMGBTN, RESIZE_NONE, 14, 158, 179, 14, 35, SPR_IMG_TRANSMITTER, STR_028E_PLACE_TRANSMITTER},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 139, 149, 43, 54, STR_0224, STR_0228_INCREASE_SIZE_OF_LAND_AREA},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 139, 149, 56, 67, STR_0225, STR_0229_DECREASE_SIZE_OF_LAND_AREA},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 34, 149, 75, 86, STR_0226_RANDOM_LAND, STR_022A_GENERATE_RANDOM_LAND},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 34, 149, 88, 99, STR_0227_RESET_LAND, STR_022B_RESET_LANDSCAPE},
{ WWT_TEXTBTN, RESIZE_NONE, 14, 34, 149, 75, 86, STR_SE_NEW_WORLD, STR_022A_GENERATE_RANDOM_LAND},
{ WIDGETS_END},
};
@ -1388,11 +1318,7 @@ static void ScenEditLandGenWndProc(Window *w, WindowEvent *e)
} break;
case 14: /* gen random land */
HandleButtonClick(w, 14);
AskResetLandscape(0);
break;
case 15: /* reset landscape */
HandleButtonClick(w,15);
AskResetLandscape(1);
ShowCreateScenario();
break;
}
break;
@ -1422,7 +1348,7 @@ static void ScenEditLandGenWndProc(Window *w, WindowEvent *e)
}
static const WindowDesc _scen_edit_land_gen_desc = {
-1,-1, 182, 102,
-1,-1, 182, 96,
WC_SCEN_LAND_GEN,0,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON,
_scen_edit_land_gen_widgets,
@ -1879,6 +1805,8 @@ static void MainToolbarWndProc(Window *w, WindowEvent *e)
case WE_KEYPRESS: {
PlayerID local = (_local_player != OWNER_SPECTATOR) ? _local_player : 0;
if (IsGeneratingWorld()) break;
switch (e->keypress.keycode) {
case WKC_F1: case WKC_PAUSE:
ToolbarPauseClick(w);
@ -2062,13 +1990,12 @@ static void ScenEditToolbarWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
case WE_PAINT:
/* XXX look for better place for these */
if (_date <= MinDate) {
if (_patches_newgame.starting_year <= MIN_YEAR) {
SETBIT(w->disabled_state, 6);
} else {
CLRBIT(w->disabled_state, 6);
}
if (_date >= MaxDate) {
if (_patches_newgame.starting_year >= MAX_YEAR) {
SETBIT(w->disabled_state, 7);
} else {
CLRBIT(w->disabled_state, 7);
@ -2080,10 +2007,10 @@ static void ScenEditToolbarWndProc(Window *w, WindowEvent *e)
DrawWindowWidgets(w);
SetDParam(0, _date);
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
DrawStringCentered(298, 6, STR_00AF, 0);
SetDParam(0, _date);
SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1));
DrawStringCentered(161, 1, STR_0221_OPENTTD, 0);
DrawStringCentered(161, 11,STR_0222_SCENARIO_EDITOR, 0);
@ -2207,7 +2134,7 @@ static void StatusBarWndProc(Window *w, WindowEvent *e)
70, 1, (_pause || _patches.status_long_date) ? STR_00AF : STR_00AE, 0
);
if (p != NULL) {
if (p != NULL && !IsGeneratingWorld()) {
// Draw player money
SetDParam64(0, p->money64);
DrawStringCentered(570, 1, p->player_money >= 0 ? STR_0004 : STR_0005, 0);
@ -2225,7 +2152,7 @@ static void StatusBarWndProc(Window *w, WindowEvent *e)
if (!DrawScrollingStatusText(&_statusbar_news_item, WP(w,def_d).data_1))
WP(w,def_d).data_1 = -1280;
} else {
if (p != NULL) {
if (p != NULL && !IsGeneratingWorld()) {
// This is the default text
SetDParam(0, p->name_1);
SetDParam(1, p->name_2);
@ -2327,7 +2254,7 @@ static void MainWindowWndProc(Window *w, WindowEvent *e)
case WE_KEYPRESS:
if (e->keypress.keycode == WKC_BACKQUOTE) {
IConsoleSwitch();
if (!IsGeneratingWorld()) IConsoleSwitch();
e->keypress.cont = false;
break;
}
@ -2339,7 +2266,7 @@ static void MainWindowWndProc(Window *w, WindowEvent *e)
break;
}
if (_game_mode == GM_MENU) break;
if (_game_mode == GM_MENU || IsGeneratingWorld()) break;
switch (e->keypress.keycode) {
case 'C':
@ -2435,11 +2362,7 @@ void SetupColorsAndInitialWindow(void)
w = AllocateWindow(0, 0, width, height, MainWindowWndProc, WC_MAIN_WINDOW, NULL);
AssignWindowViewport(w, 0, 0, width, height, 0, 0);
w = AllocateWindowDesc(&_toolb_scen_desc);
w->disabled_state = 1 << 9;
CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
PositionMainToolbar(w); // already WC_MAIN_TOOLBAR passed (&_toolb_scen_desc)
ShowVitalWindows();
break;
default:
NOT_REACHED();
@ -2450,17 +2373,31 @@ void ShowVitalWindows(void)
{
Window *w;
w = AllocateWindowDesc(&_toolb_normal_desc);
w->disabled_state = 1 << 17; // disable zoom-in button (by default game is zoomed in)
if (_game_mode != GM_EDITOR) {
w = AllocateWindowDesc(&_toolb_normal_desc);
/* Disable zoom-in for normal things, and zoom-out if we come
* from world-generating. */
w->disabled_state = IsGeneratingWorld() ? (1 << 18) : (1 << 17);
} else {
w = AllocateWindowDesc(&_toolb_scen_desc);
/* Disable zoom-in for normal things, and zoom-out if we come
* from world-generating. */
w->disabled_state = IsGeneratingWorld() ? (1 << 10) : (1 << 9);
}
CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
if (_networking) { // if networking, disable fast-forward button
if (_networking) {
/* If networking, disable fast-forward button */
SETBIT(w->disabled_state, 1);
if (!_network_server) // if not server, disable pause button
SETBIT(w->disabled_state, 0);
/* If not server, disable pause button */
if (!_network_server) SETBIT(w->disabled_state, 0);
}
PositionMainToolbar(w); // already WC_MAIN_TOOLBAR passed (&_toolb_normal_desc)
/* 'w' is for sure a WC_MAIN_TOOLBAR */
PositionMainToolbar(w);
/* Status bad only for normal games */
if (_game_mode == GM_EDITOR) return;
_main_status_desc.top = _screen.height - 12;
w = AllocateWindowDesc(&_main_status_desc);

62
misc.c
View File

@ -19,8 +19,6 @@
#include "table/landscape_const.h"
#include "date.h"
extern void StartupEconomy(void);
char _name_array[512][32];
#ifndef MERSENNE_TWISTER
@ -96,19 +94,6 @@ void InitializePlayers(void);
static void InitializeCheats(void);
void InitializeNPF(void);
void GenerateLandscape(void);
void GenerateClearTile(void);
void GenerateIndustries(void);
void GenerateUnmovables(void);
bool GenerateTowns(void);
void StartupPlayers(void);
void StartupDisasters(void);
void GenerateTrees(void);
void ConvertGroundTilesIntoWaterTiles(void);
void InitializeGame(int mode, uint size_x, uint size_y)
{
AllocateMap(size_x, size_y);
@ -165,53 +150,6 @@ void InitializeGame(int mode, uint size_x, uint size_y)
ResetObjectToPlace();
}
void GenerateWorld(int mode, uint size_x, uint size_y)
{
// Make sure everything is done via OWNER_NONE
_current_player = OWNER_NONE;
UpdatePatches();
_generating_world = true;
InitializeGame(mode == GW_RANDOM ? 0 : IG_DATE_RESET, size_x, size_y);
SetObjectToPlace(SPR_CURSOR_ZZZ, 0, 0, 0);
// Must start economy early because of the costs.
StartupEconomy();
// Don't generate landscape items when in the scenario editor.
if (mode == GW_EMPTY) {
// empty world in scenario editor
ConvertGroundTilesIntoWaterTiles();
} else {
GenerateLandscape();
GenerateClearTile();
// only generate towns, tree and industries in newgame mode.
if (mode == GW_NEWGAME) {
GenerateTowns();
GenerateTrees();
GenerateIndustries();
GenerateUnmovables();
}
}
// These are probably pointless when inside the scenario editor.
StartupPlayers();
StartupEngines();
StartupDisasters();
_generating_world = false;
// No need to run the tile loop in the scenario editor.
if (mode != GW_EMPTY) {
uint i;
for (i = 0; i < 0x500; i++) RunTileLoop();
}
ResetObjectToPlace();
}
void DeleteName(StringID id)
{
if ((id & 0xF800) == 0x7800) {

View File

@ -3,6 +3,7 @@
#include "stdafx.h"
#include "openttd.h"
#include "hal.h"
#include "heightmap.h"
#include "debug.h"
#include "functions.h"
#include "gfxinit.h"
@ -26,6 +27,8 @@
#include "variables.h"
#include "vehicle.h"
#include "train.h"
#include "tgp.h"
#include "settings.h"
#include "date.h"
#include "fios.h"
@ -33,6 +36,9 @@
FiosItem *_fios_list;
int _saveload_mode;
extern void GenerateLandscape(byte mode);
extern void SwitchMode(int new_mode);
static bool _fios_path_changed;
static bool _savegame_sort_dirty;
@ -1094,6 +1100,72 @@ void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth,
UpdateTextBufferSize(&WP(w, querystr_d).text);
}
static void QueryWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {
case WE_PAINT:
SetDParam(0, WP(w, query_d).caption);
DrawWindowWidgets(w);
DrawStringMultiCenter(90, 38, WP(w, query_d).message, 178);
break;
case WE_CLICK:
switch (e->click.widget) {
case 3:
case 4:
WP(w, query_d).calledback = true;
if (WP(w, query_d).ok_cancel_callback != NULL) WP(w, query_d).ok_cancel_callback(e->click.widget == 4);
DeleteWindow(w);
break;
}
break;
case WE_MOUSELOOP:
if (!FindWindowById(WP(w, query_d).wnd_class, WP(w, query_d).wnd_num)) DeleteWindow(w);
break;
case WE_DESTROY:
if (!WP(w, query_d).calledback && WP(w, query_d).ok_cancel_callback != NULL) WP(w, query_d).ok_cancel_callback(false);
break;
}
}
static const Widget _query_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 4, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_NONE, 4, 11, 179, 0, 13, STR_012D, STR_NULL},
{ WWT_IMGBTN, RESIZE_NONE, 4, 0, 179, 14, 91, 0x0, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 25, 84, 72, 83, STR_012E_CANCEL, STR_NULL},
{ WWT_TEXTBTN, RESIZE_NONE, 12, 95, 154, 72, 83, STR_012F_OK, STR_NULL},
{ WIDGETS_END },
};
static const WindowDesc _query_desc = {
WDP_CENTER, WDP_CENTER, 180, 92,
WC_OK_CANCEL_QUERY, 0,
WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
_query_widgets,
QueryWndProc
};
void ShowQuery(StringID caption, StringID message, void (*ok_cancel_callback)(bool ok_clicked), WindowClass window_class, WindowNumber window_number)
{
Window *w;
DeleteWindowById(WC_OK_CANCEL_QUERY, 0);
w = AllocateWindowDesc(&_query_desc);
w->click_state = 1 << 5;
WP(w, query_d).caption = caption;
WP(w, query_d).message = message;
WP(w, query_d).wnd_class = window_class;
WP(w, query_d).wnd_num = window_number;
WP(w, query_d).ok_cancel_callback = ok_cancel_callback;
WP(w, query_d).calledback = false;
}
static const Widget _load_dialog_1_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_4001_LOAD_GAME, STR_018C_WINDOW_TITLE_DRAG_THIS},
@ -1122,6 +1194,20 @@ static const Widget _load_dialog_2_widgets[] = {
{ WIDGETS_END},
};
static const Widget _load_dialog_3_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_4011_LOAD_HEIGHTMAP,STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
{ WWT_IMGBTN, RESIZE_RIGHT, 14, 0, 256, 26, 47, 0x0, STR_NULL},
{ WWT_IMGBTN, RESIZE_RB, 14, 0, 256, 48, 293, 0x0, STR_NULL},
{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 245, 256, 48, 59, SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON},
{ WWT_6, RESIZE_RB, 14, 2, 243, 50, 291, 0x0, STR_400A_LIST_OF_DRIVES_DIRECTORIES},
{ WWT_SCROLLBAR, RESIZE_LRB, 14, 245, 256, 60, 281, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 245, 256, 282, 293, 0x0, STR_RESIZE_BUTTON},
{ WIDGETS_END},
};
static const Widget _save_dialog_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 256, 0, 13, STR_4000_SAVE_GAME, STR_018C_WINDOW_TITLE_DRAG_THIS},
@ -1158,9 +1244,8 @@ static const Widget _save_dialog_scen_widgets[] = {
{ WIDGETS_END},
};
// Colors for fios types
const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5};
const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5, 6, 6, 8};
void BuildFileList(void)
{
@ -1172,6 +1257,8 @@ void BuildFileList(void)
case SLD_LOAD_SCENARIO:
case SLD_SAVE_SCENARIO:
_fios_list = FiosGetScenarioList(_saveload_mode); break;
case SLD_LOAD_HEIGHTMAP:
_fios_list = FiosGetHeightmapList(_saveload_mode); break;
default: _fios_list = FiosGetSavegameList(_saveload_mode); break;
}
@ -1249,6 +1336,10 @@ static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
ttd_strlcpy(&o_dir.name[0], _path.scenario_dir, sizeof(o_dir.name));
break;
case SLD_LOAD_HEIGHTMAP:
ttd_strlcpy(&o_dir.name[0], _path.heightmap_dir, sizeof(o_dir.name));
break;
default:
ttd_strlcpy(&o_dir.name[0], _path.personal_dir, sizeof(o_dir.name));
}
@ -1331,6 +1422,13 @@ static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
DeleteWindow(w);
} else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
SetFiosType(file->type);
ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
DeleteWindow(w);
ShowHeightmapLoad();
} else {
// SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox
ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength);
@ -1442,11 +1540,20 @@ static const WindowDesc _save_dialog_scen_desc = {
SaveLoadDlgWndProc,
};
static const WindowDesc _load_dialog_heightmap_desc = {
WDP_CENTER, WDP_CENTER, 257, 294,
WC_SAVELOAD,0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
_load_dialog_3_widgets,
SaveLoadDlgWndProc,
};
static const WindowDesc * const _saveload_dialogs[] = {
&_load_dialog_desc,
&_load_dialog_scen_desc,
&_save_dialog_desc,
&_save_dialog_scen_desc,
&_load_dialog_heightmap_desc,
};
void ShowSaveLoadDialog(int mode)
@ -1493,107 +1600,6 @@ void RedrawAutosave(void)
SetWindowDirty(FindWindowById(WC_STATUS_BAR, 0));
}
static const Widget _select_scenario_widgets[] = {
{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
{ WWT_CAPTION, RESIZE_RIGHT, 7, 11, 256, 0, 13, STR_400E_SELECT_NEW_GAME_TYPE, STR_018C_WINDOW_TITLE_DRAG_THIS},
{ WWT_IMGBTN, RESIZE_RIGHT, 7, 0, 256, 14, 25, 0x0, STR_NULL},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 7, 0, 127, 14, 25, STR_SORT_BY_NAME, STR_SORT_ORDER_TIP},
{ WWT_PUSHTXTBTN, RESIZE_NONE, 7, 128, 256, 14, 25, STR_SORT_BY_DATE, STR_SORT_ORDER_TIP},
{ WWT_IMGBTN, RESIZE_RB, 7, 0, 244, 26, 319, 0x0, STR_NULL},
{ WWT_6, RESIZE_RB, 7, 2, 243, 28, 317, 0x0, STR_400F_SELECT_SCENARIO_GREEN_PRE},
{ WWT_SCROLLBAR, RESIZE_LRB, 7, 245, 256, 26, 307, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
{ WWT_RESIZEBOX, RESIZE_LRTB, 7, 245, 256, 308, 319, 0x0, STR_RESIZE_BUTTON},
{ WIDGETS_END},
};
static void SelectScenarioWndProc(Window *w, WindowEvent *e)
{
const int list_start = 45;
switch (e->event) {
case WE_PAINT: {
int y,pos;
const FiosItem *item;
if (_savegame_sort_dirty) {
_savegame_sort_dirty = false;
MakeSortedSaveGameList();
}
SetVScrollCount(w, _fios_num);
DrawWindowWidgets(w);
DoDrawString(
_savegame_sort_order & SORT_DESCENDING ? DOWNARROW : UPARROW,
_savegame_sort_order & SORT_BY_NAME ? w->widget[3].right - 9 : w->widget[4].right - 9,
15, 16
);
DrawString(4, 32, STR_4010_GENERATE_RANDOM_NEW_GAME, 9);
y = list_start;
pos = w->vscroll.pos;
while (pos < _fios_num) {
item = _fios_list + pos;
DoDrawString(item->title, 4, y, _fios_colors[item->type]);
pos++;
y += 10;
if (y >= w->vscroll.cap * 10 + list_start) break;
}
}
break;
case WE_CLICK:
switch (e->click.widget) {
case 3: /* Sort scenario names by name */
_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
_savegame_sort_dirty = true;
SetWindowDirty(w);
break;
case 4: /* Sort scenario names by date */
_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
_savegame_sort_dirty = true;
SetWindowDirty(w);
break;
case 6: /* Click the listbox */
if (e->click.pt.y < list_start) {
GenRandomNewGame(Random(), InteractiveRandom());
} else {
int y = (e->click.pt.y - list_start) / 10;
const char *name;
const FiosItem *file;
if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return;
file = _fios_list + y;
name = FiosBrowseTo(file);
if (name != NULL) {
SetFiosType(file->type);
strcpy(_file_to_saveload.name, name);
DeleteWindow(w);
StartScenarioEditor(Random(), InteractiveRandom());
}
}
break;
}
break;
case WE_RESIZE: {
/* Widget 3 and 4 have to go with halve speed, make it so obiwan */
uint diff = e->sizing.diff.x / 2;
w->widget[3].right += diff;
w->widget[4].left += diff;
w->widget[4].right += e->sizing.diff.x;
w->vscroll.cap += e->sizing.diff.y / 10;
} break;
}
}
void SetFiosType(const byte fiostype)
{
switch (fiostype) {
@ -1607,37 +1613,22 @@ void SetFiosType(const byte fiostype)
_file_to_saveload.mode = SL_OLD_LOAD;
break;
#ifdef WITH_PNG
case FIOS_TYPE_PNG:
_file_to_saveload.mode = SL_PNG;
break;
#endif /* WITH_PNG */
case FIOS_TYPE_BMP:
_file_to_saveload.mode = SL_BMP;
break;
default:
_file_to_saveload.mode = SL_INVALID;
break;
}
}
static const WindowDesc _select_scenario_desc = {
WDP_CENTER, WDP_CENTER, 257, 320,
WC_SAVELOAD,0,
WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
_select_scenario_widgets,
SelectScenarioWndProc
};
void AskForNewGameToStart(void)
{
Window *w;
DeleteWindowById(WC_QUERY_STRING, 0);
DeleteWindowById(WC_SAVELOAD, 0);
_saveload_mode = SLD_NEW_GAME;
BuildFileList();
w = AllocateWindowDesc(&_select_scenario_desc);
w->vscroll.cap = 27;
w->resize.step_width = 2;
w->resize.step_height = 10;
w->resize.height = w->height - 10 * 17; // Minimum of 10 in the list
}
static int32 ClickMoneyCheat(int32 p1, int32 p2)
{
DoCommandP(0, -10000000, 0, NULL, CMD_MONEY_CHEAT);

View File

@ -24,6 +24,7 @@
#include "variables.h"
#include "network_server.h"
#include "network_udp.h"
#include "settings.h"
#include "string.h"
#define BGC 5
@ -57,6 +58,7 @@ static char _edit_str_buf[64];
static void ShowNetworkStartServerWindow(void);
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
extern void SwitchMode(int new_mode);
static const StringID _connection_types_dropdown[] = {
STR_NETWORK_LAN_INTERNET,
@ -683,7 +685,7 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
_is_network_server = true;
if (nd->map == NULL) { // start random new game
GenRandomNewGame(Random(), InteractiveRandom());
ShowGenerateLandscape();
} else { // load a scenario
char *name = FiosBrowseTo(nd->map);
if (name != NULL) {
@ -692,7 +694,7 @@ static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
DeleteWindow(w);
StartScenarioEditor(Random(), InteractiveRandom());
SwitchMode(SM_START_SCENARIO);
}
}
break;
@ -1392,8 +1394,6 @@ void ShowClientList(void)
if (w != NULL) w->window_number = 0;
}
extern void SwitchMode(int new_mode);
static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e)
{
switch (e->event) {

View File

@ -21,6 +21,7 @@
#include "vehicle.h"
#include "station.h"
#include "variables.h"
#include "genworld.h"
// This file handles all the server-commands
@ -1308,18 +1309,13 @@ void NetworkUpdateClientInfo(uint16 client_index)
}
}
extern void SwitchMode(int new_mode);
/* Check if we want to restart the map */
static void NetworkCheckRestartMap(void)
{
if (_network_restart_game_year != 0 && _cur_year >= _network_restart_game_year) {
DEBUG(net, 0)("Auto-restarting map. Year %d reached.", _cur_year);
_random_seeds[0][0] = Random();
_random_seeds[0][1] = InteractiveRandom();
SwitchMode(SM_NEWGAME);
StartNewGameWithoutGUI(GENERATE_NEW_SEED);
}
}

112
openttd.c
View File

@ -48,11 +48,11 @@
#include "train.h"
#include "yapf/yapf.h"
#include "settings.h"
#include "genworld.h"
#include "date.h"
#include <stdarg.h>
void GenerateWorld(int mode, uint size_x, uint size_y);
void CallLandscapeTick(void);
void IncreaseDate(void);
void DoPaletteAnimations(void);
@ -295,6 +295,7 @@ static void LoadIntroGame(void)
#endif
if (SaveOrLoad(filename, SL_LOAD) != SL_OK) {
GenerateWorld(GW_EMPTY, 64, 64); // if failed loading, make empty world.
WaitTillGeneratedWorld();
}
_pause = 0;
@ -317,7 +318,7 @@ int ttd_main(int argc, char *argv[])
char musicdriver[16], sounddriver[16], videodriver[16];
int resolution[2] = {0,0};
Year startyear = INVALID_YEAR;
uint generation_seed = GENERATE_NEW_SEED;
bool dedicated = false;
bool network = false;
char *network_conn = NULL;
@ -376,7 +377,7 @@ int ttd_main(int argc, char *argv[])
_switch_mode = SM_NEWGAME;
}
break;
case 'G': _random_seeds[0][0] = atoi(mgo.opt); break;
case 'G': generation_seed = atoi(mgo.opt); break;
case 'c': _config_file = strdup(mgo.opt); break;
case -2:
case 'h':
@ -409,6 +410,7 @@ int ttd_main(int argc, char *argv[])
if (videodriver[0]) ttd_strlcpy(_ini_videodriver, videodriver, sizeof(_ini_videodriver));
if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
if (startyear != INVALID_YEAR) _patches_newgame.starting_year = startyear;
if (generation_seed != GENERATE_NEW_SEED) _patches_newgame.generation_seed = generation_seed;
if (_dedicated_forks && !dedicated) _dedicated_forks = false;
@ -457,6 +459,13 @@ int ttd_main(int argc, char *argv[])
/* XXX - ugly hack, if diff_level is 9, it means we got no setting from the config file */
if (_opt_newgame.diff_level == 9) SetDifficultyLevel(0, &_opt_newgame);
/* Make sure _patches is filled with _patches_newgame if we switch to a game directly */
if (_switch_mode != SM_NONE) {
memcpy(&_opt, &_opt_newgame, sizeof(_opt));
GfxLoadSprites();
UpdatePatches();
}
// initialize the ingame console
IConsoleInit();
_cursor.in_window = true;
@ -464,6 +473,7 @@ int ttd_main(int argc, char *argv[])
IConsoleCmdExec("exec scripts/autoexec.scr 0");
GenerateWorld(GW_EMPTY, 64, 64); // Make the viewport initialization happy
WaitTillGeneratedWorld();
#ifdef ENABLE_NETWORK
if (network && _network_available) {
@ -567,36 +577,35 @@ static void ShowScreenshotResult(bool b)
}
static void MakeNewGame(void)
static void MakeNewGameDone(void)
{
/* In a dedicated server, the server does not play */
if (_network_dedicated) {
_local_player = OWNER_SPECTATOR;
return;
}
/* Create a single player */
DoStartupNewPlayer(false);
_local_player = 0;
_current_player = _local_player;
DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
MarkWholeScreenDirty();
}
static void MakeNewGame(bool from_heightmap)
{
_game_mode = GM_NORMAL;
// Copy in game options
_opt_ptr = &_opt;
memcpy(_opt_ptr, &_opt_newgame, sizeof(*_opt_ptr));
GenerateWorldSetCallback(&MakeNewGameDone);
GenerateWorld(from_heightmap ? GW_HEIGHTMAP : GW_NEWGAME, 1 << _patches.map_x, 1 << _patches.map_y);
}
GfxLoadSprites();
// Reinitialize windows
ResetWindowSystem();
LoadStringWidthTable();
SetupColorsAndInitialWindow();
// Randomize world
GenerateWorld(GW_NEWGAME, 1<<_patches.map_x, 1<<_patches.map_y);
// In a dedicated server, the server does not play
if (_network_dedicated) {
_local_player = OWNER_SPECTATOR;
} else {
// Create a single player
DoStartupNewPlayer(false);
_local_player = 0;
_current_player = _local_player;
DoCommandP(0, (_patches.autorenew << 15 ) | (_patches.autorenew_months << 16) | 4, _patches.autorenew_money, NULL, CMD_REPLACE_VEHICLE);
}
static void MakeNewEditorWorldDone(void)
{
_local_player = OWNER_NONE;
MarkWholeScreenDirty();
}
@ -605,23 +614,8 @@ static void MakeNewEditorWorld(void)
{
_game_mode = GM_EDITOR;
// Copy in game options
_opt_ptr = &_opt;
memcpy(_opt_ptr, &_opt_newgame, sizeof(GameOptions));
GfxLoadSprites();
// Re-init the windowing system
ResetWindowSystem();
// Create toolbars
SetupColorsAndInitialWindow();
// Startup the game system
GenerateWorldSetCallback(&MakeNewEditorWorldDone);
GenerateWorld(GW_EMPTY, 1 << _patches.map_x, 1 << _patches.map_y);
_local_player = OWNER_NONE;
MarkWholeScreenDirty();
}
void StartupPlayers(void);
@ -688,7 +682,7 @@ bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
switch (ogm) {
case GM_MENU: LoadIntroGame(); break;
case GM_EDITOR: MakeNewEditorWorld(); break;
default: MakeNewGame(); break;
default: MakeNewGame(false); break;
}
return false;
@ -738,7 +732,7 @@ void SwitchMode(int new_mode)
snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "Random Map");
}
#endif /* ENABLE_NETWORK */
MakeNewGame();
MakeNewGame(false);
break;
case SM_START_SCENARIO: /* New Game --> Choose one of the preset scenarios */
@ -768,6 +762,22 @@ void SwitchMode(int new_mode)
break;
}
case SM_START_HEIGHTMAP: /* Load a heightmap and start a new game from it */
#ifdef ENABLE_NETWORK
if (_network_server) {
snprintf(_network_game_info.map_name, lengthof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title);
}
#endif /* ENABLE_NETWORK */
MakeNewGame(true);
break;
case SM_LOAD_HEIGHTMAP: /* Load heightmap from scenario editor */
_local_player = OWNER_NONE;
GenerateWorld(GW_HEIGHTMAP, 1 << _patches.map_x, 1 << _patches.map_y);
MarkWholeScreenDirty();
break;
case SM_LOAD_SCENARIO: { /* Load scenario from scenario editor */
if (SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR)) {
Player *p;
@ -784,6 +794,7 @@ void SwitchMode(int new_mode)
}
}
_generating_world = false;
_patches_newgame.starting_year = BASE_YEAR + _cur_year;
// delete all stations owned by a player
DeleteAllPlayerStations();
} else {
@ -805,9 +816,9 @@ void SwitchMode(int new_mode)
break;
case SM_GENRANDLAND: /* Generate random land within scenario editor */
_local_player = OWNER_NONE;
GenerateWorld(GW_RANDOM, 1 << _patches.map_x, 1 << _patches.map_y);
// XXX: set date
_local_player = OWNER_NONE;
MarkWholeScreenDirty();
break;
}
@ -826,6 +837,7 @@ void StateGameLoop(void)
{
// dont execute the state loop during pause
if (_pause) return;
if (IsGeneratingWorld()) return;
if (_game_mode == GM_EDITOR) {
RunTileLoop();
@ -884,7 +896,7 @@ static void DoAutosave(void)
static void ScrollMainViewport(int x, int y)
{
if (_game_mode != GM_MENU) {
if (_game_mode != GM_MENU && !IsGeneratingWorld()) {
Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
assert(w);
@ -963,7 +975,7 @@ void GameLoop(void)
if (_network_available)
NetworkUDPGameLoop();
if (_networking) {
if (_networking && !IsGeneratingWorld()) {
// Multiplayer
NetworkGameLoop();
} else {

View File

@ -77,14 +77,17 @@ enum SwitchModes {
SM_GENRANDLAND = 6,
SM_LOAD_SCENARIO = 9,
SM_START_SCENARIO = 10,
SM_START_HEIGHTMAP = 11,
SM_LOAD_HEIGHTMAP = 12,
};
/* Modes for GenerateWorld */
enum GenerateWorldModes {
GW_NEWGAME = 0, /* Generate a map for a new game */
GW_EMPTY = 1, /* Generate an empty map (sea-level) */
GW_RANDOM = 2, /* Generate a random map for SE */
GW_NEWGAME = 0, /* Generate a map for a new game */
GW_EMPTY = 1, /* Generate an empty map (sea-level) */
GW_RANDOM = 2, /* Generate a random map for SE */
GW_HEIGHTMAP = 3, /* Generate a newgame from a heightmap */
};
/* Modes for InitializeGame, those are _bits_! */
@ -410,6 +413,9 @@ enum {
WC_HIGHSCORE = 0x4D,
WC_ENDSCREEN = 0x4E,
WC_SIGN_LIST = 0x4F,
WC_GENERATE_LANDSCAPE = 0x50,
WC_GENERATE_PROGRESS_WINDOW = 0x51,
WC_OK_CANCEL_QUERY = 0x52,
};

View File

@ -168,6 +168,9 @@
<File
RelativePath=".\aystar.c">
</File>
<File
RelativePath=".\bmp.c">
</File>
<File
RelativePath=".\callback_table.c">
</File>
@ -231,12 +234,18 @@
<File
RelativePath=".\fios.c">
</File>
<File
RelativePath=".\genworld.c">
</File>
<File
RelativePath=".\gfx.c">
</File>
<File
RelativePath=".\gfxinit.c">
</File>
<File
RelativePath=".\heightmap.c">
</File>
<File
RelativePath=".\landscape.c">
</File>
@ -378,6 +387,9 @@
<File
RelativePath=".\texteff.c">
</File>
<File
RelativePath=".\tgp.c">
</File>
<File
RelativePath=".\thread.c">
</File>
@ -424,6 +436,9 @@
<File
RelativePath=".\aystar.h">
</File>
<File
RelativePath=".\bmp.h">
</File>
<File
RelativePath=".\command.h">
</File>
@ -463,6 +478,10 @@
<File
RelativePath=".\functions.h">
</File>
<File
RelativePath=".\genworld.h"
>
</File>
<File
RelativePath=".\gfx.h">
</File>
@ -475,6 +494,9 @@
<File
RelativePath=".\hal.h">
</File>
<File
RelativePath=".\heightmap.h">
</File>
<File
RelativePath=".\industry.h">
</File>
@ -604,6 +626,9 @@
<File
RelativePath=".\string.h">
</File>
<File
RelativePath=".\tgp.h">
</File>
<File
RelativePath=".\thread.h">
</File>
@ -662,6 +687,9 @@
<File
RelativePath=".\engine_gui.c">
</File>
<File
RelativePath=".\genworld_gui.c">
</File>
<File
RelativePath=".\graph_gui.c">
</File>

View File

@ -455,6 +455,9 @@
<File
RelativePath=".\aystar.c"
>
<File
RelativePath=".\bmp.c"
>
</File>
<File
RelativePath=".\callback_table.c"
@ -556,6 +559,10 @@
RelativePath=".\fios.c"
>
</File>
<File
RelativePath=".\genworld.c"
>
</File>
<File
RelativePath=".\gfx.c"
>
@ -564,6 +571,10 @@
RelativePath=".\gfxinit.c"
>
</File>
<File
RelativePath=".\heightmap.c"
>
</File>
<File
RelativePath=".\landscape.c"
>
@ -752,6 +763,10 @@
RelativePath=".\texteff.c"
>
</File>
<File
RelativePath=".\tgp.c"
>
</File>
<File
RelativePath=".\thread.c"
>
@ -850,6 +865,9 @@
<File
RelativePath=".\aystar.h"
>
<File
RelativePath=".\bmp.h"
>
</File>
<File
RelativePath=".\clear.h"
@ -911,6 +929,10 @@
RelativePath=".\functions.h"
>
</File>
<File
RelativePath=".\genworld.h"
>
</File>
<File
RelativePath=".\gfx.h"
>
@ -927,6 +949,10 @@
RelativePath=".\hal.h"
>
</File>
<File
RelativePath=".\heightmap.h"
>
</File>
<File
RelativePath=".\industry.h"
>
@ -1103,6 +1129,10 @@
RelativePath=".\string.h"
>
</File>
<File
RelativePath=".\tgp.h"
>
</File>
<File
RelativePath=".\thread.h"
>
@ -1183,6 +1213,10 @@
RelativePath=".\engine_gui.c"
>
</File>
<File
RelativePath=".\genworld_gui.c"
>
</File>
<File
RelativePath=".\graph_gui.c"
>

View File

@ -143,4 +143,4 @@ rm -rf $RPM_BUILD_ROOT
- Upgraded to 0.3.4
* Wed Jul 31 2004 Dominik Scherer <> 0.3.3-1mdk
- Initial release
- Initial release

View File

@ -137,8 +137,8 @@ void DisplaySplashImage(void)
#else // WITH_PNG
#else /* WITH_PNG */
void DisplaySplashImage(void) {}
#endif // WITH_PNG
#endif /* WITH_PNG */

2
os2.c
View File

@ -180,6 +180,7 @@ void DeterminePaths(void)
_path.save_dir = str_fmt("%ssave", _path.personal_dir);
_path.autosave_dir = str_fmt("%s\\autosave", _path.save_dir);
_path.scenario_dir = str_fmt("%sscenario", _path.personal_dir);
_path.heightmap_dir = str_fmt("%sscenario\\heightmap", _path.personal_dir);
_path.gm_dir = str_fmt("%sgm\\", _path.game_data_dir);
_path.data_dir = str_fmt("%sdata\\", _path.game_data_dir);
@ -202,6 +203,7 @@ void DeterminePaths(void)
mkdir(_path.save_dir);
mkdir(_path.autosave_dir);
mkdir(_path.scenario_dir);
mkdir(_path.heightmap_dir);
}
/**

View File

@ -30,7 +30,7 @@
#include "variables.h"
#include <setjmp.h>
const uint16 SAVEGAME_VERSION = 29;
const uint16 SAVEGAME_VERSION = 30;
uint16 _sl_version; /// the major savegame version identifier
byte _sl_minor_version; /// the minor savegame version, DO NOT USE!

View File

@ -14,6 +14,8 @@ typedef enum SaveOrLoadMode {
SL_LOAD = 0,
SL_SAVE = 1,
SL_OLD_LOAD = 2,
SL_PNG = 3,
SL_BMP = 4,
} SaveOrLoadMode;
SaveOrLoadResult SaveOrLoad(const char *filename, int mode);

View File

@ -254,7 +254,7 @@ static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *user
fclose(f);
return true;
}
#endif // WITH_PNG
#endif /* WITH_PNG */
//************************************************

View File

@ -36,6 +36,7 @@
#include "npf.h"
#include "yapf/yapf.h"
#include "newgrf.h"
#include "genworld.h"
#include "date.h"
/** The patch values that are used for new games and/or modified in config file */
@ -1435,6 +1436,17 @@ const SettingDesc _patch_settings[] = {
SDT_CONDVAR (Patches, yapf.road_slope_penalty , SLE_UINT, 28, SL_MAX_VERSION,NS, 0, 2 * YAPF_TILE_LENGTH, 0, 1000000, STR_NULL, NULL),
SDT_CONDVAR (Patches, yapf.road_crossing_penalty , SLE_UINT, 28, SL_MAX_VERSION,NS, 0, 3 * YAPF_TILE_LENGTH, 0, 1000000, STR_NULL, NULL),
/***************************************************************************/
/* Terrain genation related patch options */
SDT_CONDVAR(Patches, land_generator, SLE_UINT8, 30, SL_MAX_VERSION, 0, MS, 1, 0, 1, STR_CONFIG_PATCHES_LAND_GENERATOR, NULL),
SDT_CONDVAR(Patches, oil_refinery_limit, SLE_UINT8, 30, SL_MAX_VERSION, 0, 0, 16, 12, 48, STR_CONFIG_PATCHES_OIL_REF_EDGE_DISTANCE, NULL),
SDT_CONDVAR(Patches, tgen_smoothness, SLE_UINT8, 30, SL_MAX_VERSION, 0, MS, 1, 0, 3, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN, NULL),
SDT_CONDVAR(Patches, generation_seed, SLE_UINT32, 30, SL_MAX_VERSION, 0, 0, GENERATE_NEW_SEED, 0, MAX_UVALUE(uint32), STR_NULL, NULL),
SDT_CONDVAR(Patches, tree_placer, SLE_UINT8, 30, SL_MAX_VERSION, 0, MS, 2, 0, 2, STR_CONFIG_PATCHES_TREE_PLACER, NULL),
SDT_VAR (Patches, heightmap_rotation, SLE_UINT8, S, MS, 0, 0, 1, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION, NULL),
SDT_VAR (Patches, progress_update_interval, SLE_UINT16, S, 0, 200, 0, 5000, STR_CONFIG_PATCHES_PROGRESS_UPDATE_INTERVAL, NULL),
SDT_VAR (Patches, se_flat_world_height, SLE_UINT8, S, 0, 0, 0, 15, STR_CONFIG_PATCHES_SE_FLAT_WORLD_HEIGHT, NULL),
SDT_END()
};

View File

@ -568,9 +568,8 @@ static const char *_patches_ui[] = {
"window_snap_radius",
"invisible_trees",
"population_in_label",
"map_x",
"map_y",
"link_terraform_toolbar",
"progress_update_interval",
};
static const char *_patches_construction[] = {
@ -580,6 +579,7 @@ static const char *_patches_construction[] = {
"signal_side",
"always_small_airport",
"drag_signals_density",
"oil_refinery_limit",
};
static const char *_patches_stations[] = {
@ -600,9 +600,7 @@ static const char *_patches_economy[] = {
"multiple_industry_per_town",
"same_industry_close",
"bribe",
"snow_line_height",
"colored_news_year",
"starting_year",
"ending_year",
"smooth_economy",
"allow_shares",

View File

@ -65,18 +65,15 @@ void str_validate(char *str)
if (!IsValidAsciiChar(*str, CS_ALPHANUMERAL)) *str = '?';
}
void strtolower(char *str)
{
for (; *str != '\0'; str++) *str = tolower(*str);
}
/** Only allow valid ascii-function codes. Filter special codes like BELL and
* so on [we need a special filter here later]
/**
* Only allow certain keys. You can define the filter to be used. This makes
* sure no invalid keys can get into an editbox, like BELL.
* @param key character to be checked
* @return true or false depending if the character is printable/valid or not */
* @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not
*/
bool IsValidAsciiChar(byte key, CharSetFilter afilter)
{
// XXX This filter stops certain crashes, but may be too restrictive.
bool firsttest = false;
switch (afilter) {
@ -84,8 +81,9 @@ bool IsValidAsciiChar(byte key, CharSetFilter afilter)
firsttest = (key >= ' ' && key < 127);
break;
case CS_NUMERAL://we are quite strict, here
return (key >= 48 && key <= 57);
/* We are very strict here */
case CS_NUMERAL:
return (key >= '0' && key <= '9');
case CS_ALPHA:
default:
@ -93,7 +91,13 @@ bool IsValidAsciiChar(byte key, CharSetFilter afilter)
break;
}
/* Allow some special chars too that are non-ASCII but still valid (like '^' above 'a') */
return (firsttest || (key >= 160 &&
key != 0xAA && key != 0xAC && key != 0xAD && key != 0xAF &&
key != 0xB5 && key != 0xB6 && key != 0xB7 && key != 0xB9));
}
void strtolower(char *str)
{
for (; *str != '\0'; str++) *str = tolower(*str);
}

View File

@ -29,19 +29,25 @@ char* CDECL str_fmt(const char* str, ...);
* replaces them with a question mark '?' */
void str_validate(char *str);
/**
* Valid filter types for IsValidAsciiChar.
*/
typedef enum CharSetFilter {
CS_ALPHANUMERAL, //! Both numeric and alphabetic and spaces and stuff
CS_NUMERAL, //! Only numeric ones
CS_ALPHA, //! Only alphabetic values
} CharSetFilter;
/**
* Only allow certain keys. You can define the filter to be used. This makes
* sure no invalid keys can get into an editbox, like BELL.
* @param key character to be checked
* @param afilter the filter to use
* @return true or false depending if the character is printable/valid or not
*/
bool IsValidAsciiChar(byte key, CharSetFilter afilter);
/** Convert the given string to lowercase */
void strtolower(char *str);
typedef enum CharSetFilter { //valid char filtering
CS_ALPHANUMERAL, //both numeric and alphabetic
CS_NUMERAL, //only numeric ones.
CS_ALPHA, //only alphabetic values
} CharSetFilter;
/** Only allow valid ascii-function codes. Filter special codes like BELL and
* so on [we need a special filter here later]
* @param key character to be checked
* @return true or false depending if the character is printable/valid or not */
bool IsValidAsciiChar(byte key, CharSetFilter afilter);
#endif /* STRING_H */

826
tgp.c Normal file
View File

@ -0,0 +1,826 @@
/* $Id$ */
#include "stdafx.h"
#include <math.h>
#include "openttd.h"
#include "clear_map.h"
#include "functions.h"
#include "map.h"
#include "table/strings.h"
#include "clear_map.h"
#include "tile.h"
#include "variables.h"
#include "void_map.h"
#include "tgp.h"
#include "console.h"
#include "genworld.h"
/*
* OTTD Perlin Noise Landscape Generator, aka TerraGenesis Perlin
*
* Quickie guide to Perlin Noise
* Perlin noise is a predictable pseudo random number sequence. By generating
* it in 2 dimensions, it becomes a useful random map, that for a given seed
* and starting X & Y is entirely predictable. On the face of it, that may not
* be useful. However, it means that if you want to replay a map in a different
* terrain, or just vary the sea level, you just re-run the generator with the
* same seed. The seed is an int32, and is randomised on each run of New Game.
* The Scenario Generator does not randomise the value, so that you can
* experiment with one terrain until you are happy, or click "Random" for a new
* random seed.
*
* Perlin Noise is a series of "octaves" of random noise added together. By
* reducing the amplitude of the noise with each octave, the first octave of
* noise defines the main terrain sweep, the next the ripples on that, and the
* next the ripples on that. I use 6 octaves, with the amplitude controlled by
* a power ratio, usually known as a persistence or p value. This I vary by the
* smoothness selection, as can be seen in the table below. The closer to 1,
* the more of that octave is added. Each octave is however raised to the power
* of its position in the list, so the last entry in the "smooth" row, 0.35, is
* raised to the power of 6, so can only add 0.001838... of the amplitude to
* the running total.
*
* In other words; the first p value sets the general shape of the terrain, the
* second sets the major variations to that, ... until finally the smallest
* bumps are added.
*
* Usefully, this routine is totally scaleable; so when 32bpp comes along, the
* terrain can be as bumpy as you like! It is also infinitely expandable; a
* single random seed terrain continues in X & Y as far as you care to
* calculate. In theory, we could use just one seed value, but randomly select
* where in the Perlin XY space we use for the terrain. Personally I prefer
* using a simple (0, 0) to (X, Y), with a varying seed.
*
*
* Other things i have had to do: mountainous wasnt mountainous enough, and
* since we only have 0..15 heights available, I add a second generated map
* (with a modified seed), onto the original. This generally raises the
* terrain, which then needs scaling back down. Overall effect is a general
* uplift.
*
* However, the values on the top of mountains are then almost guaranteed to go
* too high, so large flat plateaus appeared at height 15. To counter this, I
* scale all heights above 12 to proportion up to 15. It still makes the
* mountains have flatish tops, rather than craggy peaks, but at least they
* arent smooth as glass.
*
*
* For a full discussion of Perlin Noise, please visit:
* http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
*
*
* Evolution II
*
* The algorithm as described in the above link suggests to compute each tile height
* as composition of several noise waves. Some of them are computed directly by
* noise(x, y) function, some are calculated using linear approximation. Our
* first implementation of perlin_noise_2D() used 4 noise(x, y) calls plus
* 3 linear interpolations. It was called 6 times for each tile. This was a bit
* CPU expensive.
*
* The following implementation uses optimized algorithm that should produce
* the same quality result with much less computations, but more memory accesses.
* The overal speedup should be 300% to 800% depending on CPU and memory speed.
*
* I will try to explain it on the example below:
*
* Have a map of 4 x 4 tiles, our simplifiead noise generator produces only two
* values -1 and +1, use 3 octaves with wave lenght 1, 2 and 4, with amplitudes
* 3, 2, 1. Original algorithm produces:
*
* h00 = lerp(lerp(-3, 3, 0/4), lerp(3, -3, 0/4), 0/4) + lerp(lerp(-2, 2, 0/2), lerp( 2, -2, 0/2), 0/2) + -1 = lerp(-3.0, 3.0, 0/4) + lerp(-2, 2, 0/2) + -1 = -3.0 + -2 + -1 = -6.0
* h01 = lerp(lerp(-3, 3, 1/4), lerp(3, -3, 1/4), 0/4) + lerp(lerp(-2, 2, 1/2), lerp( 2, -2, 1/2), 0/2) + 1 = lerp(-1.5, 1.5, 0/4) + lerp( 0, 0, 0/2) + 1 = -1.5 + 0 + 1 = -0.5
* h02 = lerp(lerp(-3, 3, 2/4), lerp(3, -3, 2/4), 0/4) + lerp(lerp( 2, -2, 0/2), lerp(-2, 2, 0/2), 0/2) + -1 = lerp( 0, 0, 0/4) + lerp( 2, -2, 0/2) + -1 = 0 + 2 + -1 = 1.0
* h03 = lerp(lerp(-3, 3, 3/4), lerp(3, -3, 3/4), 0/4) + lerp(lerp( 2, -2, 1/2), lerp(-2, 2, 1/2), 0/2) + 1 = lerp( 1.5, -1.5, 0/4) + lerp( 0, 0, 0/2) + 1 = 1.5 + 0 + 1 = 2.5
*
* h10 = lerp(lerp(-3, 3, 0/4), lerp(3, -3, 0/4), 1/4) + lerp(lerp(-2, 2, 0/2), lerp( 2, -2, 0/2), 1/2) + 1 = lerp(-3.0, 3.0, 1/4) + lerp(-2, 2, 1/2) + 1 = -1.5 + 0 + 1 = -0.5
* h11 = lerp(lerp(-3, 3, 1/4), lerp(3, -3, 1/4), 1/4) + lerp(lerp(-2, 2, 1/2), lerp( 2, -2, 1/2), 1/2) + -1 = lerp(-1.5, 1.5, 1/4) + lerp( 0, 0, 1/2) + -1 = -0.75 + 0 + -1 = -1.75
* h12 = lerp(lerp(-3, 3, 2/4), lerp(3, -3, 2/4), 1/4) + lerp(lerp( 2, -2, 0/2), lerp(-2, 2, 0/2), 1/2) + 1 = lerp( 0, 0, 1/4) + lerp( 2, -2, 1/2) + 1 = 0 + 0 + 1 = 1.0
* h13 = lerp(lerp(-3, 3, 3/4), lerp(3, -3, 3/4), 1/4) + lerp(lerp( 2, -2, 1/2), lerp(-2, 2, 1/2), 1/2) + -1 = lerp( 1.5, -1.5, 1/4) + lerp( 0, 0, 1/2) + -1 = 0.75 + 0 + -1 = -0.25
*
*
* Optimization 1:
*
* 1) we need to allocate a bit more tiles: (size_x + 1) * (size_y + 1) = (5 * 5):
*
* 2) setup corner values using amplitude 3
* { -3.0 X X X 3.0 }
* { X X X X X }
* { X X X X X }
* { X X X X X }
* { 3.0 X X X -3.0 }
*
* 3a) interpolate values in the middle
* { -3.0 X 0.0 X 3.0 }
* { X X X X X }
* { 0.0 X 0.0 X 0.0 }
* { X X X X X }
* { 3.0 X 0.0 X -3.0 }
*
* 3b) add patches with amplitude 2 to them
* { -5.0 X 2.0 X 1.0 }
* { X X X X X }
* { 2.0 X -2.0 X 2.0 }
* { X X X X X }
* { 1.0 X 2.0 X -5.0 }
*
* 4a) interpolate values in the middle
* { -5.0 -1.5 2.0 1.5 1.0 }
* { -1.5 -0.75 0.0 0.75 1.5 }
* { 2.0 0.0 -2.0 0.0 2.0 }
* { 1.5 0.75 0.0 -0.75 -1.5 }
* { 1.0 1.5 2.0 -1.5 -5.0 }
*
* 4b) add patches with amplitude 1 to them
* { -6.0 -0.5 1.0 2.5 0.0 }
* { -0.5 -1.75 1.0 -0.25 2.5 }
* { 1.0 1.0 -3.0 1.0 1.0 }
* { 2.5 -0.25 1.0 -1.75 -0.5 }
* { 0.0 2.5 1.0 -0.5 -6.0 }
*
*
*
* Optimization 2:
*
* As you can see above, each noise function was called just once. Therefore
* we don't need to use noise function that calculates the noise from x, y and
* some prime. The same quality result we can obtain using standard Random()
* function instead.
*
*/
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#define M_PI 3.14159265358979323846
#endif /* M_PI_2 */
/** Fixed point type for heights */
typedef int16 height_t;
static const int height_decimal_bits = 4;
static const height_t _invalid_height = -32768;
/** Fixed point array for amplitudes (and percent values) */
typedef int amplitude_t;
static const int amplitude_decimal_bits = 10;
/** Height map - allocated array of heights (MapSizeX() + 1) x (MapSizeY() + 1) */
typedef struct HeightMap
{
height_t *h; //! array of heights
uint dim_x; //! height map size_x MapSizeX() + 1
uint total_size; //! height map total size
uint size_x; //! MapSizeX()
uint size_y; //! MapSizeY()
} HeightMap;
/** Global height map instance */
static HeightMap _height_map = {NULL, 0, 0, 0, 0};
/** Height map accessors */
#define HeightMapXY(x, y) _height_map.h[(x) + (y) * _height_map.dim_x]
/** Conversion: int to height_t */
#define I2H(i) ((i) << height_decimal_bits)
/** Conversion: height_t to int */
#define H2I(i) ((i) >> height_decimal_bits)
/** Conversion: int to amplitude_t */
#define I2A(i) ((i) << amplitude_decimal_bits)
/** Conversion: amplitude_t to int */
#define A2I(i) ((i) >> amplitude_decimal_bits)
/** Conversion: amplitude_t to height_t */
#define A2H(a) ((height_decimal_bits < amplitude_decimal_bits) \
? ((a) >> (amplitude_decimal_bits - height_decimal_bits)) \
: ((a) << (height_decimal_bits - amplitude_decimal_bits)))
/** Walk through all items of _height_map.h */
#define FOR_ALL_TILES_IN_HEIGHT(h) for (h = _height_map.h; h < &_height_map.h[_height_map.total_size]; h++)
/** Noise amplitudes (multiplied by 1024)
* - indexed by "smoothness setting" and log2(frequency) */
static const amplitude_t _amplitudes_by_smoothness_and_frequency[4][12] = {
// Very smooth
{1000, 350, 123, 43, 15, 1, 1, 0, 0, 0, 0, 0},
// Smooth
{1000, 1000, 403, 200, 64, 8, 1, 0, 0, 0, 0, 0},
// Rough
{1000, 1200, 800, 500, 200, 16, 4, 0, 0, 0, 0, 0},
// Very Rough
{1500, 1000, 1200, 1000, 500, 32, 20, 0, 0, 0, 0, 0},
};
/** Desired water percentage (100% == 1024) - indexed by _opt.diff.quantity_sea_lakes */
static const amplitude_t _water_percent[4] = {20, 80, 250, 400};
/** Desired maximum height - indexed by _opt.diff.terrain_type */
static const int8 _max_height[4] = {
6, // Very flat
9, // Flat
12, // Hilly
15 // Mountainous
};
/** Check if a X/Y set are within the map. */
static inline bool IsValidXY(uint x, uint y)
{
return ((int)x) >= 0 && x < _height_map.size_x && ((int)y) >= 0 && y < _height_map.size_y;
}
/** Allocate array of (MapSizeX()+1)*(MapSizeY()+1) heights and init the _height_map structure members */
static inline bool AllocHeightMap(void)
{
height_t *h;
_height_map.size_x = MapSizeX();
_height_map.size_y = MapSizeY();
/* Allocate memory block for height map row pointers */
_height_map.total_size = (_height_map.size_x + 1) * (_height_map.size_y + 1);
_height_map.dim_x = _height_map.size_x + 1;
_height_map.h = calloc(_height_map.total_size, sizeof(*_height_map.h));
if (_height_map.h == NULL) return false;
/* Iterate through height map initialize values */
FOR_ALL_TILES_IN_HEIGHT(h) *h = _invalid_height;
return true;
}
/** Free height map */
static inline void FreeHeightMap(void)
{
if (_height_map.h == NULL) return;
free(_height_map.h);
_height_map.h = NULL;
}
/** RandomHeight() generator */
static inline height_t RandomHeight(amplitude_t rMax)
{
amplitude_t ra = (Random() << 16) | (Random() & 0x0000FFFF);
height_t rh;
/* Scale the amplitude for better resolution */
rMax *= 16;
/* Spread height into range -rMax..+rMax */
rh = A2H(ra % (2 * rMax + 1) - rMax);
return rh;
}
/** One interpolation and noise round */
static bool ApplyNoise(uint log_frequency, amplitude_t amplitude)
{
uint size_min = min(_height_map.size_x, _height_map.size_y);
uint step = size_min >> log_frequency;
uint x, y;
assert(_height_map.h != NULL);
/* Are we finished? */
if (step == 0) return false;
if (log_frequency == 0) {
/* This is first round, we need to establish base heights with step = size_min */
for (y = 0; y <= _height_map.size_y; y += step) {
for (x = 0; x <= _height_map.size_x; x += step) {
height_t height = (amplitude > 0) ? RandomHeight(amplitude) : 0;
HeightMapXY(x, y) = height;
}
}
return true;
}
/* It is regular iteration round.
* Interpolate height values at odd x, even y tiles */
for (y = 0; y <= _height_map.size_y; y += 2 * step) {
for (x = 0; x < _height_map.size_x; x += 2 * step) {
height_t h00 = HeightMapXY(x + 0 * step, y);
height_t h02 = HeightMapXY(x + 2 * step, y);
height_t h01 = (h00 + h02) / 2;
HeightMapXY(x + 1 * step, y) = h01;
}
}
/* Interpolate height values at odd y tiles */
for (y = 0; y < _height_map.size_y; y += 2 * step) {
for (x = 0; x <= _height_map.size_x; x += step) {
height_t h00 = HeightMapXY(x, y + 0 * step);
height_t h20 = HeightMapXY(x, y + 2 * step);
height_t h10 = (h00 + h20) / 2;
HeightMapXY(x, y + 1 * step) = h10;
}
}
for (y = 0; y <= _height_map.size_y; y += step) {
for (x = 0; x <= _height_map.size_x; x += step) {
HeightMapXY(x, y) += RandomHeight(amplitude);
}
}
return (step > 1);
}
/** Base Perlin noise generator - fills height map with raw Perlin noise */
static void HeightMapGenerate(void)
{
uint size_min = min(_height_map.size_x, _height_map.size_y);
uint iteration_round = 0;
amplitude_t amplitude;
bool continue_iteration;
uint log_size_min, log_frequency_min;
int log_frequency;
/* Find first power of two that fits */
for (log_size_min = 6; (1U << log_size_min) < size_min; log_size_min++) { }
log_frequency_min = log_size_min - 6;
do {
log_frequency = iteration_round - log_frequency_min;
if (log_frequency >= 0) {
amplitude = _amplitudes_by_smoothness_and_frequency[_patches.tgen_smoothness][log_frequency];
} else {
amplitude = 0;
}
continue_iteration = ApplyNoise(iteration_round, amplitude);
iteration_round++;
} while(continue_iteration);
}
/** Returns min, max and average height from height map */
static void HeightMapGetMinMaxAvg(height_t *min_ptr, height_t *max_ptr, height_t *avg_ptr)
{
height_t h_min, h_max, h_avg, *h;
int64 h_accu = 0;
h_min = h_max = HeightMapXY(0, 0);
/* Get h_min, h_max and accumulate heights into h_accu */
FOR_ALL_TILES_IN_HEIGHT(h) {
if (*h < h_min) h_min = *h;
if (*h > h_max) h_max = *h;
h_accu += *h;
}
/* Get average height */
h_avg = (height_t)(h_accu / (_height_map.size_x * _height_map.size_y));
/* Return required results */
if (min_ptr != NULL) *min_ptr = h_min;
if (max_ptr != NULL) *max_ptr = h_max;
if (avg_ptr != NULL) *avg_ptr = h_avg;
}
/** Dill histogram and return pointer to its base point - to the count of zero heights */
static int *HeightMapMakeHistogram(height_t h_min, height_t h_max, int *hist_buf)
{
int *hist = hist_buf - h_min;
height_t *h;
/* Fill histogram */
FOR_ALL_TILES_IN_HEIGHT(h) {
assert(*h >= h_min);
assert(*h <= h_max);
hist[*h]++;
}
return hist;
}
/** Applies sine wave redistribution onto height map */
static void HeightMapSineTransform(height_t h_min, height_t h_max)
{
height_t *h;
FOR_ALL_TILES_IN_HEIGHT(h) {
double fheight;
if (*h < h_min) continue;
/* Transform height into 0..1 space */
fheight = (double)(*h - h_min) / (double)(h_max - h_min);
/* Apply sine transform depending on landscape type */
switch(_opt.landscape) {
case LT_CANDY:
case LT_NORMAL:
/* Move and scale 0..1 into -1..+1 */
fheight = 2 * fheight - 1;
/* Sine transform */
fheight = sin(fheight * M_PI_2);
/* Transform it back from -1..1 into 0..1 space */
fheight = 0.5 * (fheight + 1);
break;
case LT_HILLY:
{
/* Arctic terrain needs special height distribution.
* Redistribute heights to have more tiles at highest (75%..100%) range */
double sine_upper_limit = 0.75;
double linear_compression = 2;
if (fheight >= sine_upper_limit) {
/* Over the limit we do linear compression up */
fheight = 1.0 - (1.0 - fheight) / linear_compression;
} else {
double m = 1.0 - (1.0 - sine_upper_limit) / linear_compression;
/* Get 0..sine_upper_limit into -1..1 */
fheight = 2.0 * fheight / sine_upper_limit - 1.0;
/* Sine wave transform */
fheight = sin(fheight * M_PI_2);
/* Get -1..1 back to 0..(1 - (1 - sine_upper_limit) / linear_compression) == 0.0..m */
fheight = 0.5 * (fheight + 1.0) * m;
}
}
break;
case LT_DESERT:
{
/* Desert terrain needs special height distribution.
* Half of tiles should be at lowest (0..25%) heights */
double sine_lower_limit = 0.5;
double linear_compression = 2;
if (fheight <= sine_lower_limit) {
/* Under the limit we do linear compression down */
fheight = fheight / linear_compression;
} else {
double m = sine_lower_limit / linear_compression;
/* Get sine_lower_limit..1 into -1..1 */
fheight = 2.0 * ((fheight - sine_lower_limit) / (1.0 - sine_lower_limit)) - 1.0;
/* Sine wave transform */
fheight = sin(fheight * M_PI_2);
/* Get -1..1 back to (sine_lower_limit / linear_compression)..1.0 */
fheight = 0.5 * ((1.0 - m) * fheight + (1.0 + m));
}
}
break;
default:
NOT_REACHED();
break;
}
/* Transform it back into h_min..h_max space */
*h = fheight * (h_max - h_min) + h_min;
if (*h < 0) *h = I2H(0);
if (*h >= h_max) *h = h_max - 1;
}
}
/** Adjusts heights in height map to contain required amount of water tiles */
static void HeightMapAdjustWaterLevel(amplitude_t water_percent, height_t h_max_new)
{
height_t h_min, h_max, h_avg, h_water_level;
int water_tiles, desired_water_tiles;
height_t *h;
int *hist_buf, *hist;
HeightMapGetMinMaxAvg(&h_min, &h_max, &h_avg);
/* Allocate histogram buffer and clear its cells */
hist_buf = calloc(h_max - h_min + 1, sizeof(*hist_buf));
/* Fill histogram */
hist = HeightMapMakeHistogram(h_min, h_max, hist_buf);
/* How many water tiles do we want? */
desired_water_tiles = (int)(((int64)water_percent) * (int64)(_height_map.size_x * _height_map.size_y)) >> amplitude_decimal_bits;
/* Raise water_level and accumulate values from histogram until we reach required number of water tiles */
for (h_water_level = h_min, water_tiles = 0; h_water_level < h_max; h_water_level++) {
water_tiles += hist[h_water_level];
if (water_tiles >= desired_water_tiles) break;
}
/* We now have the proper water level value.
* Transform the height map into new (normalized) height map:
* values from range: h_min..h_water_level will become negative so it will be clamped to 0
* values from range: h_water_level..h_max are transformed into 0..h_max_new
* , where h_max_new is 4, 8, 12 or 16 depending on terrain type (very flat, flat, hilly, mountains)
*/
FOR_ALL_TILES_IN_HEIGHT(h) {
/* Transform height from range h_water_level..h_max into 0..h_max_new range */
*h = (height_t)(((int)h_max_new) * (*h - h_water_level) / (h_max - h_water_level)) + I2H(1);
/* Make sure all values are in the proper range (0..h_max_new) */
if (*h < 0) *h = I2H(0);
if (*h >= h_max_new) *h = h_max_new - 1;
}
free(hist_buf);
}
static double perlin_coast_noise_2D(const double x, const double y, const double p, const int prime);
/**
* This routine sculpts in from the edge a random amount, again a Perlin
* sequence, to avoid the rigid flat-edge slopes that were present before. The
* Perlin noise map doesnt know where we are going to slice across, and so we
* often cut straight through high terrain. the smoothing routine makes it
* legal, gradually increasing up from the edge to the original terrain height.
* By cutting parts of this away, it gives a far more irregular edge to the
* map-edge. Sometimes it works beautifully with the existing sea & lakes, and
* creates a very realistic coastline. Other times the variation is less, and
* the map-edge shows its cliff-like roots.
*
* This routine may be extended to randomly sculpt the height of the terrain
* near the edge. This will have the coast edge at low level (1-3), rising in
* smoothed steps inland to about 15 tiles in. This should make it look as
* though the map has been built for the map size, rather than a slice through
* a larger map.
*
* Please note that all the small numbers; 53, 101, 167, etc. are small primes
* to help give the perlin noise a bit more of a random feel.
*/
static void HeightMapCoastLines(void)
{
int smallest_size = min(_patches.map_x, _patches.map_y);
const int margin = 4;
uint y, x;
uint max_x;
uint max_y;
/* Lower to sea level */
for (y = 0; y <= _height_map.size_y; y++) {
/* Top right */
max_x = myabs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.9, 53) + 0.25) * 5 + (perlin_coast_noise_2D(y, y, 0.35, 179) + 1) * 12);
max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x);
if (smallest_size < 8 && max_x > 5) max_x /= 1.5;
for (x = 0; x < max_x; x++) {
HeightMapXY(x, y) = 0;
}
/* Bottom left */
max_x = myabs((perlin_coast_noise_2D(_height_map.size_y - y, y, 0.85, 101) + 0.3) * 6 + (perlin_coast_noise_2D(y, y, 0.45, 67) + 0.75) * 8);
max_x = max((smallest_size * smallest_size / 16) + max_x, (smallest_size * smallest_size / 16) + margin - max_x);
if (smallest_size < 8 && max_x > 5) max_x /= 1.5;
for (x = _height_map.size_x; x > (_height_map.size_x - 1 - max_x); x--) {
HeightMapXY(x, y) = 0;
}
}
/* Lower to sea level */
for (x = 0; x <= _height_map.size_x; x++) {
/* Top left */
max_y = myabs((perlin_coast_noise_2D(x, _height_map.size_y / 2, 0.9, 167) + 0.4) * 5 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.4, 211) + 0.7) * 9);
max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y);
if (smallest_size < 8 && max_y > 5) max_y /= 1.5;
for (y = 0; y < max_y; y++) {
HeightMapXY(x, y) = 0;
}
/* Bottom right */
max_y = myabs((perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.85, 71) + 0.25) * 6 + (perlin_coast_noise_2D(x, _height_map.size_y / 3, 0.35, 193) + 0.75) * 12);
max_y = max((smallest_size * smallest_size / 16) + max_y, (smallest_size * smallest_size / 16) + margin - max_y);
if (smallest_size < 8 && max_y > 5) max_y /= 1.5;
for (y = _height_map.size_y; y > (_height_map.size_y - 1 - max_y); y--) {
HeightMapXY(x, y) = 0;
}
}
}
/** Start at given point, move in given direction, find and Smooth coast in that direction */
static void HeightMapSmoothCoastInDirection(int org_x, int org_y, int dir_x, int dir_y)
{
const int max_coast_dist_from_edge = 35;
const int max_coast_Smooth_depth = 35;
int x, y;
int ed; // coast distance from edge
int depth;
height_t h_prev = 16;
height_t h;
assert(IsValidXY(org_x, org_y));
/* Search for the coast (first non-water tile) */
for (x = org_x, y = org_y, ed = 0; IsValidXY(x, y) && ed < max_coast_dist_from_edge; x += dir_x, y += dir_y, ed++) {
/* Coast found? */
if (HeightMapXY(x, y) > 15) break;
/* Coast found in the neighborhood? */
if (IsValidXY(x + dir_y, y + dir_x) && HeightMapXY(x + dir_y, y + dir_x) > 0) break;
/* Coast found in the neighborhood on the other side */
if (IsValidXY(x - dir_y, y - dir_x) && HeightMapXY(x - dir_y, y - dir_x) > 0) break;
}
/* Coast found or max_coast_dist_from_edge has been reached.
* Soften the coast slope */
for (depth = 0; IsValidXY(x, y) && depth <= max_coast_Smooth_depth; depth++, x += dir_x, y += dir_y) {
h = HeightMapXY(x, y);
h = min(h, h_prev + (4 + depth)); // coast softening formula
HeightMapXY(x, y) = h;
h_prev = h;
}
}
/** Smooth coasts by modulating height of tiles close to map edges with cosine of distance from edge */
static void HeightMapSmoothCoasts(void)
{
uint x, y;
/* First Smooth NW and SE coasts (y close to 0 and y close to size_y) */
for (x = 0; x < _height_map.size_x; x++) {
HeightMapSmoothCoastInDirection(x, 0, 0, 1);
HeightMapSmoothCoastInDirection(x, _height_map.size_y - 1, 0, -1);
}
/* First Smooth NE and SW coasts (x close to 0 and x close to size_x) */
for (y = 0; y < _height_map.size_y; y++) {
HeightMapSmoothCoastInDirection(0, y, 1, 0);
HeightMapSmoothCoastInDirection(_height_map.size_x - 1, y, -1, 0);
}
}
/**
* This routine provides the essential cleanup necessary before OTTD can
* display the terrain. When generated, the terrain heights can jump more than
* one level between tiles. This routine smooths out those differences so that
* the most it can change is one level. When OTTD can support cliffs, this
* routine may not be necessary.
*/
static void HeightMapSmoothSlopes(height_t dh_max)
{
int x, y;
for (y = 1; y <= (int)_height_map.size_y; y++) {
for (x = 1; x <= (int)_height_map.size_x; x++) {
height_t h_max = min(HeightMapXY(x - 1, y), HeightMapXY(x, y - 1)) + dh_max;
if (HeightMapXY(x, y) > h_max) HeightMapXY(x, y) = h_max;
}
}
for (y = _height_map.size_y - 1; y >= 0; y--) {
for (x = _height_map.size_x - 1; x >= 0; x--) {
height_t h_max = min(HeightMapXY(x + 1, y), HeightMapXY(x, y + 1)) + dh_max;
if (HeightMapXY(x, y) > h_max) HeightMapXY(x, y) = h_max;
}
}
}
/** Height map terraform post processing:
* - water level adjusting
* - coast Smoothing
* - slope Smoothing
* - height histogram redistribution by sine wave transform */
static void HeightMapNormalize(void)
{
const amplitude_t water_percent = _water_percent[_opt.diff.quantity_sea_lakes];
const height_t h_max_new = I2H(_max_height[_opt.diff.terrain_type]);
const height_t roughness = 7 + 3 * _patches.tgen_smoothness;
HeightMapAdjustWaterLevel(water_percent, h_max_new);
HeightMapCoastLines();
HeightMapSmoothSlopes(roughness);
HeightMapSmoothCoasts();
HeightMapSmoothSlopes(roughness);
HeightMapSineTransform(12, h_max_new);
HeightMapSmoothSlopes(16);
}
static inline int perlin_landXY(uint x, uint y)
{
return HeightMapXY(x, y);
}
/* The following decimals are the octave power modifiers for the Perlin noise */
static const double _perlin_p_values[][7] = { // perlin frequency per power
{ 0.35, 0.35, 0.35, 0.35, 0.35, 0.25, 0.539 }, // Very smooth
{ 0.45, 0.55, 0.45, 0.45, 0.35, 0.25, 0.89 }, // Smooth
{ 0.85, 0.80, 0.70, 0.45, 0.45, 0.35, 1.825 }, // Rough 1.825
{ 0.95, 0.85, 0.80, 0.55, 0.55, 0.45, 2.245 } // Very Rough 2.25
};
/**
* The Perlin Noise calculation using large primes
* The initial number is adjusted by two values; the generation_seed, and the
* passed parameter; prime.
* prime is used to allow the perlin noise generator to create useful random
* numbers from slightly different series.
*/
static double int_noise(const long x, const long y, const int prime)
{
long n = x + y * prime + _patches.generation_seed;
n = (n << 13) ^ n;
/* Pseudo-random number generator, using several large primes */
return 1.0 - (double)((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0;
}
/**
* Hj. Malthaner's routine included 2 different noise smoothing methods.
* We now use the "raw" int_noise one.
* However, it may be useful to move to the other routine in future.
* So it is included too.
*/
static double smoothed_noise(const int x, const int y, const int prime)
{
#if 0
/* A hilly world (four corner smooth) */
const double sides = int_noise(x - 1, y) + int_noise(x + 1, y) + int_noise(x, y - 1) + int_noise(x, y + 1);
const double center = int_noise(x, y);
return (sides + sides + center * 4) / 8.0;
#endif
/* This gives very hilly world */
return int_noise(x, y, prime);
}
/**
* This routine determines the interpolated value between a and b
*/
static inline double linear_interpolate(const double a, const double b, const double x)
{
return a + x * (b - a);
}
/**
* This routine returns the smoothed interpolated noise for an x and y, using
* the values from the surrounding positions.
*/
static double interpolated_noise(const double x, const double y, const int prime)
{
const int integer_X = (int)x;
const int integer_Y = (int)y;
const double fractional_X = x - (double)integer_X;
const double fractional_Y = y - (double)integer_Y;
const double v1 = smoothed_noise(integer_X, integer_Y, prime);
const double v2 = smoothed_noise(integer_X + 1, integer_Y, prime);
const double v3 = smoothed_noise(integer_X, integer_Y + 1, prime);
const double v4 = smoothed_noise(integer_X + 1, integer_Y + 1, prime);
const double i1 = linear_interpolate(v1, v2, fractional_X);
const double i2 = linear_interpolate(v3, v4, fractional_X);
return linear_interpolate(i1, i2, fractional_Y);
}
/**
* This is a similar function to the main perlin noise calculation, but uses
* the value p passed as a parameter rather than selected from the predefined
* sequences. as you can guess by its title, i use this to create the indented
* coastline, which is just another perlin sequence.
*/
static double perlin_coast_noise_2D(const double x, const double y, const double p, const int prime)
{
double total = 0.0;
int i;
for (i = 0; i < 6; i++) {
const double frequency = (double)(1 << i);
const double amplitude = pow(p, (double)i);
total += interpolated_noise((x * frequency) / 64.0, (y * frequency) / 64.0, prime) * amplitude;
}
return total;
}
/** A small helper function */
static void TgenSetTileHeight(TileIndex tile, int height)
{
SetTileHeight(tile, height);
MakeClear(tile, CLEAR_GRASS, 3);
}
/**
* The main new land generator using Perlin noise. Desert landscape is handled
* different to all others to give a desert valley between two high mountains.
* Clearly if a low height terrain (flat/very flat) is chosen, then the tropic
* areas wont be high enough, and there will be very little tropic on the map.
* Thus Tropic works best on Hilly or Mountainous.
*/
void GenerateTerrainPerlin(void)
{
uint x, y;
if (!AllocHeightMap()) return;
HeightMapGenerate();
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
HeightMapNormalize();
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
/* Transfer height map into OTTD map */
for (y = 2; y < _height_map.size_y - 2; y++) {
for (x = 2; x < _height_map.size_x - 2; x++) {
int height = H2I(HeightMapXY(x, y));
if (height < 0) height = 0;
if (height > 15) height = 15;
TgenSetTileHeight(TileXY(x, y), height);
}
}
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
/* Recreate void tiles at the border in case they have been affected by generation */
for (y = 0; y < _height_map.size_y - 1; y++) MakeVoid(_height_map.size_x * y + _height_map.size_x - 1);
for (x = 0; x < _height_map.size_x; x++) MakeVoid(_height_map.size_x * y + x);
FreeHeightMap();
}

8
tgp.h Normal file
View File

@ -0,0 +1,8 @@
/* $Id$ */
#ifndef TGP_H
#define TGP_H
void GenerateTerrainPerlin(void);
#endif

View File

@ -7,7 +7,7 @@
#if defined(__AMIGA__) || defined(__MORPHOS__)
OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void* arg) { return NULL; }
void* OTTDJoinThread(OTTDThread* t) { return NULL; }
void OTTDExitThread() { NOT_REACHED(); };
#elif defined(__OS2__)
@ -57,6 +57,10 @@ void* OTTDJoinThread(OTTDThread* t)
return ret;
}
void OTTDExitThread(void)
{
_endthread();
}
#elif defined(UNIX)
@ -91,6 +95,10 @@ void* OTTDJoinThread(OTTDThread* t)
return ret;
}
void OTTDExitThread(void)
{
pthread_exit(NULL);
}
#elif defined(WIN32)
@ -141,4 +149,9 @@ void* OTTDJoinThread(OTTDThread* t)
free(t);
return ret;
}
void OTTDExitThread(void)
{
ExitThread(0);
}
#endif

View File

@ -8,6 +8,7 @@ typedef struct OTTDThread OTTDThread;
typedef void* (*OTTDThreadFunc)(void*);
OTTDThread* OTTDCreateThread(OTTDThreadFunc, void*);
void* OTTDJoinThread(OTTDThread*);
void* OTTDJoinThread(OTTDThread*);
void OTTDExitThread(void);
#endif /* THREAD_H */

View File

@ -28,6 +28,7 @@
#include "bridge.h"
#include "date.h"
#include "table/town_land.h"
#include "genworld.h"
enum {
/* Max towns: 64000 (8 * 8000) */
@ -1054,7 +1055,10 @@ bool GenerateTowns(void)
uint num = 0;
uint n = ScaleByMapSize(_num_initial_towns[_opt.diff.number_towns] + (Random() & 7));
SetGeneratingWorldProgress(GWP_TOWN, n);
do {
IncreaseGeneratingWorldProgress(GWP_TOWN);
// try 20 times to create a random-sized town for the first loop.
if (CreateRandomTown(20, 0) != NULL) num++;
} while (--n);

View File

@ -15,6 +15,13 @@
#include "town.h"
#include "sound.h"
#include "variables.h"
#include "genworld.h"
enum TreePlacer {
TP_NONE,
TP_ORIGINAL,
TP_IMPROVED,
};
static TreeType GetRandomTreeType(TileIndex tile, uint seed)
{
@ -83,18 +90,70 @@ static void PlaceMoreTrees(void)
} while (--i);
}
void PlaceTreesRandomly(void)
/**
* Place a tree at the same height as an existing tree.
* This gives cool effects to the map.
*/
void PlaceTreeAtSameHeight(TileIndex tile, uint height)
{
uint i;
for (i = 0; i < 1000; i++) {
uint32 r = Random();
int x = GB(r, 0, 5) - 16;
int y = GB(r, 8, 5) - 16;
TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y));
/* Keep in range of the existing tree */
if (myabs(x) + myabs(y) > 16) continue;
/* Clear tile, no farm-tiles or rocks */
if (!IsTileType(cur_tile, MP_CLEAR) ||
IsClearGround(cur_tile, CLEAR_FIELDS) ||
IsClearGround(cur_tile, CLEAR_ROCKS))
continue;
/* Not too much height difference */
if (myabs(GetTileZ(cur_tile) - height) > 2) continue;
/* Place one tree and quit */
PlaceTree(cur_tile, r);
break;
}
}
void PlaceTreesRandomly(void)
{
uint i, j, ht;
i = ScaleByMapSize(1000);
do {
uint32 r = Random();
TileIndex tile = RandomTileSeed(r);
IncreaseGeneratingWorldProgress(GWP_TREE);
if (IsTileType(tile, MP_CLEAR) &&
!IsClearGround(tile, CLEAR_FIELDS) &&
!IsClearGround(tile, CLEAR_ROCKS)) {
PlaceTree(tile, r);
if (_patches.tree_placer != TP_IMPROVED) continue;
/* Place a number of trees based on the tile height.
* This gives a cool effect of multiple trees close together.
* It is almost real life ;) */
ht = GetTileZ(tile);
/* The higher we get, the more trees we plant */
j = GetTileZ(tile) / TILE_HEIGHT * 2;
while (j--) {
/* Above snowline more trees! */
if (_opt.landscape == LT_HILLY && ht > _opt.snow_line) {
PlaceTreeAtSameHeight(tile, ht);
PlaceTreeAtSameHeight(tile, ht);
};
PlaceTreeAtSameHeight(tile, ht);
}
}
} while (--i);
@ -105,7 +164,10 @@ void PlaceTreesRandomly(void)
do {
uint32 r = Random();
TileIndex tile = RandomTileSeed(r);
if (IsTileType(tile, MP_CLEAR) && GetTropicZone(tile) == TROPICZONE_RAINFOREST) {
IncreaseGeneratingWorldProgress(GWP_TREE);
if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_FIELDS) && GetTropicZone(tile) == TROPICZONE_RAINFOREST) {
PlaceTree(tile, r);
}
} while (--i);
@ -114,11 +176,24 @@ void PlaceTreesRandomly(void)
void GenerateTrees(void)
{
uint i;
uint i, total;
if (_patches.tree_placer == TP_NONE) return;
if (_opt.landscape != LT_CANDY) PlaceMoreTrees();
for (i = _opt.landscape == LT_HILLY ? 15 : 6; i != 0; i--) {
switch (_patches.tree_placer) {
case TP_ORIGINAL: i = _opt.landscape == LT_HILLY ? 15 : 6; break;
case TP_IMPROVED: i = _opt.landscape == LT_HILLY ? 4 : 2; break;
default: NOT_REACHED(); return;
}
total = ScaleByMapSize(1000);
if (_opt.landscape == LT_DESERT) total += ScaleByMapSize(15000);
total *= i;
SetGeneratingWorldProgress(GWP_TREE, total);
for (; i != 0; i--) {
PlaceTreesRandomly();
}
}

2
unix.c
View File

@ -214,6 +214,7 @@ void DeterminePaths(void)
_path.save_dir = str_fmt("%ssave", _path.personal_dir);
_path.autosave_dir = str_fmt("%s/autosave", _path.save_dir);
_path.scenario_dir = str_fmt("%sscenario", _path.personal_dir);
_path.heightmap_dir = str_fmt("%sscenario/heightmap", _path.personal_dir);
_path.gm_dir = str_fmt("%sgm/", _path.game_data_dir);
_path.data_dir = str_fmt("%sdata/", _path.game_data_dir);
@ -236,6 +237,7 @@ void DeterminePaths(void)
mkdir(_path.save_dir, 0755);
mkdir(_path.autosave_dir, 0755);
mkdir(_path.scenario_dir, 0755);
mkdir(_path.heightmap_dir, 0755);
}
bool InsertTextBufferClipboard(Textbuf *tb)

View File

@ -18,6 +18,7 @@
#include "unmovable_map.h"
#include "variables.h"
#include "table/unmovable_land.h"
#include "genworld.h"
/** Destroy a HQ.
* During normal gameplay you can only implicitely destroy a HQ when you are
@ -309,12 +310,13 @@ static bool IsRadioTowerNearby(TileIndex tile)
BEGIN_TILE_LOOP(tile, 9, 9, tile_s)
if (IsTransmitterTile(tile)) return true;
END_TILE_LOOP(tile, 9, 9, tile_s)
return false;
}
void GenerateUnmovables(void)
{
int i,j;
int i, li, j, loop_count;
TileIndex tile;
uint h;
uint maxx;
@ -324,12 +326,16 @@ void GenerateUnmovables(void)
/* add radio tower */
i = ScaleByMapSize(1000);
j = ScaleByMapSize(40); // maximum number of radio towers on the map
j = ScaleByMapSize(15); // maximum number of radio towers on the map
li = ScaleByMapSize1D((Random() & 3) + 7);
SetGeneratingWorldProgress(GWP_UNMOVABLE, j + li);
do {
tile = RandomTile();
if (IsTileType(tile, MP_CLEAR) && GetTileSlope(tile, &h) == SLOPE_FLAT && h >= TILE_HEIGHT * 4) {
if (IsRadioTowerNearby(tile)) continue;
MakeTransmitter(tile);
IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
if (--j == 0) break;
}
} while (--i);
@ -337,16 +343,27 @@ void GenerateUnmovables(void)
if (_opt.landscape == LT_DESERT) return;
/* add lighthouses */
i = ScaleByMapSize1D((Random() & 3) + 7);
i = li;
maxx = MapMaxX();
maxy = MapMaxY();
loop_count = 0;
do {
uint32 r;
DiagDirection dir;
int perimeter;
restart:
/* Avoid infinite loops */
if (++loop_count > 1000) break;
r = Random();
dir = GB(r, 30, 2);
/* Scatter the lighthouses more evenly around the perimeter */
perimeter = (GB(r, 16, 16) % (2 * (maxx + maxy))) - maxy;
for (dir = DIAGDIR_NE; perimeter > 0; dir++) {
perimeter -= (DiagDirToAxis(dir) == AXIS_X) ? maxx : maxy;
}
switch (dir) {
default:
case DIAGDIR_NE: tile = TileXY(maxx, r % maxy); break;
@ -363,6 +380,7 @@ restart:
assert(tile == TILE_MASK(tile));
MakeLighthouse(tile);
IncreaseGeneratingWorldProgress(GWP_UNMOVABLE);
} while (--i);
}

View File

@ -107,7 +107,15 @@ typedef struct Patches {
bool roadveh_queue; // buggy road vehicle queueing
bool autoscroll; // scroll when moving mouse to the edge.
byte errmsg_duration; // duration of error message
byte land_generator; // the landscape generator
byte oil_refinery_limit; // distance oil refineries allowed from map edge
byte snow_line_height; // a number 0-15 that configured snow line height
byte tgen_smoothness; // how rough is the terrain from 0-3
uint32 generation_seed; // noise seed for world generation
byte tree_placer; // the tree placer algorithm
byte heightmap_rotation;// rotation director for the heightmap
uint16 progress_update_interval;// interval between two updates of the progress in hundreds of milliseconds
byte se_flat_world_height; // land height a flat world gets in SE
bool bribe; // enable bribing the local authority
bool nonuniform_stations;// allow nonuniform train stations
bool always_small_airport; // always allow small airports
@ -244,6 +252,7 @@ typedef struct Paths {
char *save_dir;
char *autosave_dir;
char *scenario_dir;
char *heightmap_dir;
char *second_data_dir;
} Paths;

View File

@ -56,6 +56,7 @@ extern void HideMenuBar(void);
#include "../window.h"
#include "../network.h"
#include "../variables.h"
#include "../genworld.h"
#include "../os/macosx/splash.h"
#include "cocoa_v.h"
@ -709,7 +710,7 @@ static void QZ_GameLoop(void)
#endif
{
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
} else if (_fast_forward & 2) {
} else if (_fast_forward & 2 && !IsGeneratingWorld()) {
_fast_forward = 0;
}

View File

@ -12,6 +12,7 @@
#include "../window.h"
#include "../console.h"
#include "../variables.h"
#include "../genworld.h"
#include "dedicated_v.h"
#ifdef BEOS_NET_SERVER
@ -240,8 +241,9 @@ static void DedicatedVideoMainLoop(void)
/* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */
if (_switch_mode != SM_LOAD) {
StartNewGameWithoutGUI(GENERATE_NEW_SEED);
SwitchMode(_switch_mode);
_switch_mode = SM_NONE;
GenRandomNewGame(Random(), InteractiveRandom());
} else {
_switch_mode = SM_NONE;
/* First we need to test if the savegame can be loaded, else we will end up playing the

View File

@ -13,6 +13,7 @@
#include "../window.h"
#include "../network.h"
#include "../variables.h"
#include "../genworld.h"
#include "sdl_v.h"
#include <SDL.h>
@ -461,7 +462,7 @@ static void SdlVideoMainLoop(void)
if (keys[SDLK_TAB])
#endif
{
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
if (!_networking && _game_mode != GM_MENU && !IsGeneratingWorld()) _fast_forward |= 2;
} else if (_fast_forward & 2) {
_fast_forward = 0;
}

View File

@ -9,6 +9,7 @@
#include "../variables.h"
#include "../win32.h"
#include "../window.h"
#include "../genworld.h"
#include "win32_v.h"
#include <windows.h>
@ -792,7 +793,7 @@ static void Win32GdiMainLoop(void)
* real key is in the upper 16 bits (see WM_SYSKEYDOWN in WndProcGdi()) */
if ((_pressed_key >> 16) & WKC_TAB &&
#endif
!_networking && _game_mode != GM_MENU)
!_networking && _game_mode != GM_MENU && !IsGeneratingWorld())
_fast_forward |= 2;
} else if (_fast_forward & 2) {
_fast_forward = 0;

View File

@ -899,6 +899,7 @@ void DeterminePaths(void)
_path.save_dir = str_fmt("%ssave", cfg);
_path.autosave_dir = str_fmt("%s\\autosave", _path.save_dir);
_path.scenario_dir = str_fmt("%sscenario", cfg);
_path.heightmap_dir = str_fmt("%sscenario\\heightmap", cfg);
_path.gm_dir = str_fmt("%sgm\\", cfg);
_path.data_dir = str_fmt("%sdata\\", cfg);
_path.lang_dir = str_fmt("%slang\\", cfg);
@ -913,6 +914,7 @@ void DeterminePaths(void)
CreateDirectory(_path.save_dir, NULL);
CreateDirectory(_path.autosave_dir, NULL);
CreateDirectory(_path.scenario_dir, NULL);
CreateDirectory(_path.heightmap_dir, NULL);
}
int CDECL snprintf(char *str, size_t size, const char *format, ...)

View File

@ -12,6 +12,7 @@
#include "console.h"
#include "variables.h"
#include "table/sprites.h"
#include "genworld.h"
// delta between mouse cursor and upper left corner of dragged window
static Point _drag_delta;
@ -1323,10 +1324,11 @@ static void HandleKeypress(uint32 key)
we.keypress.cont = true;
// check if we have a query string window open before allowing hotkeys
if (FindWindowById(WC_QUERY_STRING, 0) != NULL ||
FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL ||
FindWindowById(WC_CONSOLE, 0) != NULL ||
FindWindowById(WC_SAVELOAD, 0) != NULL) {
if (FindWindowById(WC_QUERY_STRING, 0) != NULL ||
FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL ||
FindWindowById(WC_GENERATE_LANDSCAPE, 0) != NULL ||
FindWindowById(WC_CONSOLE, 0) != NULL ||
FindWindowById(WC_SAVELOAD, 0) != NULL) {
query_open = true;
}
@ -1337,6 +1339,7 @@ static void HandleKeypress(uint32 key)
if (query_open &&
w->window_class != WC_QUERY_STRING &&
w->window_class != WC_SEND_NETWORK_MSG &&
w->window_class != WC_GENERATE_LANDSCAPE &&
w->window_class != WC_CONSOLE &&
w->window_class != WC_SAVELOAD) {
continue;
@ -1376,7 +1379,7 @@ static void MouseLoop(int click, int mousewheel)
y = _cursor.pos.y;
if (click == 0 && mousewheel == 0) {
if (_patches.autoscroll && _game_mode != GM_MENU) {
if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
w = FindWindowFromPt(x, y);
if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
vp = IsPtInWindowViewport(w, x, y);
@ -1406,7 +1409,7 @@ static void MouseLoop(int click, int mousewheel)
w = MaybeBringWindowToFront(w);
vp = IsPtInWindowViewport(w, x, y);
if (vp != NULL) {
if (_game_mode == GM_MENU) return;
if (_game_mode == GM_MENU || IsGeneratingWorld()) return;
// only allow zooming in-out in main window, or in viewports
if (mousewheel &&
@ -1455,7 +1458,16 @@ void InputLoop(void)
int click;
int mousewheel;
_current_player = _local_player;
/*
* During the generation of the world, there might be
* another thread that is currently building for example
* a road. To not interfere with those tasks, we should
* NOT change the _current_player here.
*
* This is not necessary either, as the only events that
* can be handled are the 'close application' events
*/
if (!IsGeneratingWorld()) _current_player = _local_player;
// Handle pressed keys
if (_pressed_key != 0) {

View File

@ -304,6 +304,16 @@ typedef struct querystr_d {
} querystr_d;
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(querystr_d));
typedef struct query_d {
StringID caption;
StringID message;
WindowClass wnd_class;
WindowNumber wnd_num;
void (*ok_cancel_callback)(bool ok_clicked);
bool calledback;
} query_d;
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d));
typedef struct {
byte item_count; /* follow_vehicle */
byte sel_index; /* scrollpos_x */