diff --git a/Makefile b/Makefile deleted file mode 100644 --- a/Makefile +++ /dev/null @@ -1,1132 +0,0 @@ -# $Id$ - -############################################################################## -# -# Usage -# - -# Synopsis: -# -# make WITH_ZLIB=1 UNIX=1 MANUAL_CONFIG=1 -# -# (See below for the list of possible options.) -# -# Alternately, you can run make without the MANUAL_CONFIG part. It then -# generates Makefile.config, where you can customize all the options. -# However beware that for all subsequent calls the option values from -# Makefile.config take precedence to the commandline options. -# -# (That means that you probably want to either specify the options on command -# line together with MANUAL_CONFIG=1 or you want to specify no commandline -# options at all.) - -# Targets: -# -# Defaults to building binary -# clean: remove intermediate build files -# mrproper: remove intermediate files and makefile configuration -# upgradeconf: add new options to old Makefile.config -# osx: OS X application -# release: used by OSX to make a dmg file ready to release - -# Options: -# -# Summary of OS choice defines -# WIN32: building on Windows -# UNIX: building on *nix derivate (Linux, FreeBSD) -# OSX: building on Mac OS X -# MORPHOS: building on MorphOS -# BEOS: building on BeOS -# SUNOS: building on SunOS (Solaris) -# -# Summary of library choice defines -# WITH_ZLIB: savegames using zlib -# WITH_PNG: screenshots using PNG -# WITH_SDL: SDL video driver support -# WITH_COCOA: Cocoa video driver support -# -# Summary of other defines: -# DEBUG: build in debug mode -# PROFILE: build in profile mode, disables -s and -fomit-frame-pointer -# TRANSLATOR: build in translator mode (untranslated strings are prepended by -# a mark) -# RELEASE: this will be the released version number. It replaces all places -# where it normally would print the revision number -# MIDI: if set, it will use it as custom path to midi player. -# If unset, it will use the hardcoded path in the c code -# This can still be overriden by the music.extmidi openttd.cfg option. -# WITH_DIRECTMUSIC: enable DirectMusic MIDI support -# WITH_NETWORK: enable networking -# DEDICATED: allows compilation on UNIX without SDL. Useful for dedicated servers -# -# Paths: -# INSTALL: If not set, the game uses the directory of the binary to -# store everything (lang, data, gm, save and openttd.cfg), this is the `old' behaviour. -# In this case, none of the following paths are used, you also should _not_ -# use `make install', but copy the required stuff yourself (or just play out -# of you source directory, which should work fine). -# If you want to use `make install' to install the game globally, you should -# define it _before_ you build the game. If you only define INSTALL when you -# do `make install', the game won't be able to find it's files (so you should -# also define all the following paths before building). -# -# So, the following paths should be defined if INSTALL is defined. -# None of these paths have to end with / -# PREFIX: Normally /usr/local -# BINARY_DIR: The location of the binary, normally games. (Will be prefixed -# with $PREFIX) -# DATA_DIR: The location of the data (lang, data, gm and scenario), normally -# share/games/openttd. (Will be prefixed with $PREFIX) Note that scenarios -# are only put here if USE_HOMEDIR is true, otherwise they are placed in -# PERSONAL_DIR/scenario -# ICON_DIR: The location of the openttd icon. (Will be prefixed with -# $PREFIX). -# PERSONAL_DIR: The directory where openttd.cfg and the save folder will be -# stored. You cannot use ~ here, define USE_HOMEDIR for that. -# USE_HOMEDIR: If this variable is set, PERSONAL_DIR will be prefixed with -# ~/ at runtime (the user's homedir) -# SECOND_DATA_PATH Use this data dir if a file is not found in the data dir in the data path -# CUSTOM_LANG_PATH If this is set, it will use the path given to search for lng files -# instead of the lang dir in the data path -# NOTE: both SECOND_DATA_PATH and CUSTOM_LANG_PATH uses paths relative to where OTTD is opened -# -# DEST_DIR: make install will use this directory instead of the filesystem -# root to install its files. This should normally not be used by -# ordinary users, currently it is only used for the debian -# packaging. This value should only be set when calling `make -# install' and is not saved in Makefile.config -# (Note that DESTDIR is checked if DEST_DIR is not set.) -# -# STATIC: link statically -# CYGWIN: build in Cygwin environment -# MINGW: build with MingW compiler, link with MingW libraries -# -# CUSTOM_FONTCONFIG: use a custom name/path to the libfontconfig library. Useful for static linking -# -# VERBOSE: show full compiler invocations instead of brief progress messages -# -# Special for crosscompiling there are some commands available: -# -# UNIVERSAL_BINARY: builds a universal binary for OSX. Make sure you got both PPC and x86 libs. Only works with GCC 4 or newer -# TRIPLE_BINARY: builds a universal binary with the addition of code optimised for G5 (which means a total of 3 binaries in one file) -# OTTD_PPC, OTTD_PPC970, OTTD_i386: compile for target architecture. -# Multiple flags can be used so OTTD_PPC:=1 OTTD_i386:=1 produces the same result as UNIVERSAL_BINARY -# -# ENDIAN_FORCE: forces the endian-check to give a certain result. Can be BE, LE or PREPROCESSOR. -# PREPROCESSOR is always used on all OSX targets and will make the preprocessor pick the right endian. -# this means that you don't have to think about endianess when compiling for OSX. -# Very useful for universal binaries and crosscompilers. Not sure if it works on non OSX targets -# WINDRES: the location of your windres -# CC_HOST: the gcc of your localhost if you are making a target that produces incompatible executables -# CFLAGS_HOST: cflags used for CC_HOST. Make it something if you are getting errors when you try to compi -# windows executables on linux. (just: CFLAGS_HOST:='-I' or something) - - -############################################################################## -# -# Configuration -# - - -# Makefile version tag -# it checks if the version tag in Makefile.config is the same and force update outdated config files -MAKEFILE_VERSION:=10 - -# Automatic configuration -MAKE_CONFIG:=Makefile.config -MAKEFILE:=Makefile -LIB_DETECTION=makefiledir/Makefile.libdetection -CONFIG_WRITER=makefiledir/Makefile.config_writer - -# Apply automatic configuration -# See target section for how this is built, suppress errors -# since first time it isn't found but make reads this twice --include $(MAKE_CONFIG) - - -# updates Makefile.config if it's outdated -ifneq ($(MAKEFILE_VERSION),$(CONFIG_VERSION)) - UPDATECONFIG:=upgradeconf - CONFIG_INCLUDED:= -endif - -# this is used if there aren't any Makefile.config -ifndef CONFIG_INCLUDED -# sets network on by default if there aren't any config file -ENABLE_NETWORK:=1 - -# paths for make install -# disabled as they would break it for some (many?) people if they were default -#PREFIX:=/usr/local -#DATA_DIR:=share/games/openttd -#BINARY_DIR:=games -#PERSONAL_DIR:=.openttd -#USE_HOMEDIR:=1 - --include $(LIB_DETECTION) -endif - -ifdef SUPRESS_LANG_ERRORS -LANG_ERRORS = >/dev/null 2>&1 -endif - -ifdef OSX --include os/macosx/Makefile.setup -endif - -ifdef STATIC -ifndef WIN32 -ifndef OSX -ifndef MORPHOS -ifndef SKIP_STATIC_CHECK -$(error Static is only known to work on MorphOS and MacOSX!!! --- Check Makefile.config for more info and howto bypass this check) -endif -endif -endif -endif -endif - -ifdef WITH_COCOA -ifdef WITH_SDL -$(error You can not use both the SDL video driver and the Cocoa video driver at the same time) -endif -ifdef DEDICATED -$(error You can not use the Cocoa video driver in a dedicated server) -endif -else -# Force SDL on UNIX platforms -ifndef WITH_SDL -ifdef UNIX -ifndef DEDICATED -$(error You need to have SDL installed in order to run OpenTTD on UNIX. Use DEDICATED if you want to compile a CLI based server) -endif -endif -endif -endif - -# remove the dependancy for sdl if DEDICALTED is used -ifdef DEDICATED - WITH_SDL:= -endif - -# add -lpthread to LDFLAGS -ifndef WIN32 - ifndef MORPHOS - ifndef OSX - LDFLAGS+=-lpthread - endif - endif -endif - -ifdef OSX - LDFLAGS+=-framework Cocoa -endif - -ifdef WITH_SDL - ifndef SDL_CONFIG -$(error WITH_SDL can't be used when SDL_CONFIG is not set. Edit Makefile.config to correct this) - endif -endif - -ifdef WITH_PNG - ifndef LIBPNG_CONFIG -$(error WITH_PNG can't be used when LIBPNG_CONFIG is not set. Edit Makefile.config to correct this) - endif -endif - -ifdef WITH_FREETYPE - ifndef FREETYPE_CONFIG -$(error WITH_FREETYPE can't be used when FREETYPE_CONFIG is not set. Edit Makefile.config to correct this) - endif -endif - -ifdef WITH_FONTCONFIG - ifndef FONTCONFIG_CONFIG -$(error WITH_FONTCONFIG can't be used when FONTOCNFIG_CONFIG is not set. Edit Makefile.config to correct this) - endif -endif - -############################################################################## -# -# Compiler configuration -# - -# Executable file extension -ifdef WIN32 - EXE=.exe -else - ifdef OS2 - EXE=.exe - else - EXE= - endif -endif - -# Set output executable names -TTD=openttd$(EXE) -ENDIAN_CHECK=endian_check$(EXE) -STRGEN=strgen/strgen$(EXE) -OSXAPP="OpenTTD.app" - -ifdef RELEASE -REV:=$(RELEASE) -else -ifeq ($(shell if test -d .svn; then echo 1; fi), 1) -REV_MODIFIED := $(shell svnversion . | sed -n 's/.*\(M\).*/\1/p' ) -REV := $(shell LC_ALL=C svn info | awk '/^URL:.*branch/ { BRANCH="-"a[split($$2, a, "/")] } /^Last Changed Rev:/ { REV="r"$$4"$(REV_MODIFIED)" } END { print REV BRANCH }') -endif -endif - -# define flag to use for -lrt (some OSes overwrites this later for compatibility) -ifndef LRT -ifndef MORPHOS -LRT:= -lrt -endif -endif - -# MorphOS needs builddate -BUILDDATE=`date +%d.%m.%y` - -# Check if there is a windres override -ifndef WINDRES -WINDRES = windres -endif - -# Check that CXX is defined. If not, then it's g++ -ifndef CXX -CXX = g++ -endif - -# Check if CXX_HOST is defined. If not, it is CXX -ifndef CXX_HOST -CXX_HOST = $(CXX) -endif - -# Check if we have a new target -ifndef CXX_TARGET -CXX_TARGET = $(CXX_HOST) -endif - -# Check if CC_HOST is defined. If not, it is CC -ifndef CC_HOST -CC_HOST = $(CC) -endif - -ifndef CFLAGS_HOST -CFLAGS_HOST = $(BASECFLAGS) -endif - -# Check if we have a new target -ifndef CC_TARGET -CC_TARGET = $(CC_HOST) -endif - -CC_VERSION = $(shell $(CC_TARGET) -dumpversion | cut -c 1,3) - -# GNU make can only test for (in)equality -# this is a workaround to test for >= -ifeq ($(shell expr $(CC_VERSION) \>= 29), 1) - CFLAGS += -O -Wall -Wno-multichar -Wsign-compare -Wundef - CC_CFLAGS += -Wstrict-prototypes - CFLAGS += -Wwrite-strings -Wpointer-arith -endif -ifeq ($(shell expr $(CC_VERSION) \>= 30), 1) - CFLAGS += -W -Wno-unused-parameter -endif -ifeq ($(shell expr $(CC_VERSION) \>= 34), 1) - CC_CFLAGS += -Wdeclaration-after-statement -Wold-style-definition -endif - -ifdef DEBUG - ifeq ($(shell expr $(DEBUG) \>= 1), 1) - CFLAGS += -g -D_DEBUG - endif - ifeq ($(shell expr $(DEBUG) \>= 2), 1) - CFLAGS += -fno-inline - endif - ifeq ($(shell expr $(DEBUG) \>= 3), 1) - CFLAGS += -O0 - endif -endif - -ifdef PROFILE - CFLAGS += -pg - LDFLAGS += -pg - ifdef OSX - # Shark (Xcode's profiling tool) needs -g to relate CPU usage to line numbers in the source code - BASECFLAGS += -g - endif -endif - -CDEFS=-DWITH_REV - -ifndef DEBUG -ifndef PROFILE -# Release mode -ifndef MORPHOS -ifndef IRIX -# automatical strip breaks under morphos -ifdef OSX -# it appears that OSX can't handle automated stripping when mixing C and C++ -# we will do it manually in the target OSX_STRIP -OSX_STRIP:=OSX_STRIP -else -LDFLAGS += -s -endif -endif -endif -endif - -ifdef OSX -# these compilerflags makes the app run as fast as possible without making the app unstable. It works on G3 or newer -BASECFLAGS += -O3 -funroll-loops -fsched-interblock -falign-loops=16 -falign-jumps=16 -falign-functions=16 -falign-jumps-max-skip=15 -falign-loops-max-skip=15 -mdynamic-no-pic -else -ifdef MORPHOS -BASECFLAGS += -I/gg/os-include -O2 -noixemul -fstrict-aliasing -fexpensive-optimizations -BASECFLAGS += -mcpu=604 -fno-inline -mstring -mmultiple -else -BASECFLAGS += -O2 -endif -ifndef PROFILE -ifndef IRIX -BASECFLAGS += -fomit-frame-pointer -endif -endif -endif -endif - -ifdef STATIC -ifndef OSX # OSX can't build static if -static flag is used -LDFLAGS += -static -endif -endif - -# If building on MingW don't link with Cygwin libs -ifdef WIN32 -ifdef CYGWIN -BASECFLAGS += -mwin32 -LDFLAGS += -mwin32 -endif -ifdef MINGW -BASECFLAGS += -mno-cygwin -LDFLAGS += -mno-cygwin -# -lrt fails with MINGW, so we disable it -LRT:= -endif -endif - -CFLAGS += $(BASECFLAGS) - -ifdef UNIX -CDEFS += -DUNIX -endif - -ifdef BEOS -CDEFS += -DBEOS -LDFLAGS += -lmidi -lbe -lpthread -ifdef WITH_NETWORK - ifdef BEOS_NET_SERVER - CDEFS += -DBEOS_NET_SERVER - LDFLAGS += -lnet - else - # BONE needs a few more libraries than R5 - LDFLAGS += -lbind -lsocket - endif -endif -endif - -ifdef MORPHOS -# -Wstrict-prototypes generates much noise because of system headers -# and it also uses 4-byte bools in the C++ ABI, so C bools need to be that size as well for YAPF to work -CFLAGS += -Wno-strict-prototypes -DFOUR_BYTE_BOOL -endif - -ifdef SUNOS -CDEFS += -DSUNOS -ifdef WITH_NETWORK -LDFLAGS += -lnsl -lsocket -endif -endif - -# tell the source that we are building a dedicated server -ifdef DEDICATED -CDEFS += -DDEDICATED -endif - -# SDL config -ifdef WITH_SDL -CDEFS += -DWITH_SDL -CCFLAGS_SDL := $(shell $(SDL_CONFIG) --cflags) -CFLAGS += $(CCFLAGS_SDL) -ifdef STATIC -LDFLAGS_SDL := $(shell $(SDL_CONFIG) --static-libs) -else -LDFLAGS_SDL := $(shell $(SDL_CONFIG) --libs) -endif -LIBS += $(LDFLAGS_SDL) -endif - -# zlib config -ifdef WITH_ZLIB - CDEFS += -DWITH_ZLIB - ifdef STATIC - ifdef OSX - # OSX links dynamically to zlib, even in static builds since it's always present in the system - LIBS += -lz - else - LIBS += $(STATIC_ZLIB_PATH) - endif - else - LIBS += -lz - endif -endif - -# libpng config -ifdef WITH_PNG -CDEFS += -DWITH_PNG -CCFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --cppflags --I_opts) -CFLAGS += $(CCFLAGS_PNG) - -# seems like older libpng versions are broken and need this -PNGCONFIG_FLAGS = --ldflags --libs -ifdef STATIC -ifdef OSX -# Seems like we need a tiny hack for OSX static to work -LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --prefix)/lib/libpng.a -else -LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --static $(PNGCONFIG_FLAGS)) -endif -else -LDFLAGS_PNG := $(shell $(LIBPNG_CONFIG) --L_opts $(PNGCONFIG_FLAGS)) -endif -LIBS += $(LDFLAGS_PNG) -endif - -# use std C++ lib: -LIBS += -lstdc++ -ifndef MINGW - LIBS += -lc -endif - -# freetype config -ifdef WITH_FREETYPE -CDEFS += -DWITH_FREETYPE -CCFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --cflags) -LDFLAGS_FREETYPE := $(shell $(FREETYPE_CONFIG) --libs) -CFLAGS += $(CCFLAGS_FREETYPE) -LIBS += $(LDFLAGS_FREETYPE) -endif - -# fontconfig config -ifdef WITH_FONTCONFIG -CDEFS += -DWITH_FONTCONFIG -CCFLAGS_FONTCONFIG := $(shell $(FONTCONFIG_CONFIG) --cflags) -LDFLAGS_FONTCONFIG := $(shell $(FONTCONFIG_CONFIG) --libs) - -ifdef CUSTOM_FONTCONFIG -# To allow usage of non-default libs, such as absolute path to static libs -# not stored in Makefile.config -LDFLAGS_FONTCONFIG := $(CUSTOM_FONTCONFIG) -endif - -CFLAGS += $(CCFLAGS_FONTCONFIG) -LIBS += $(LDFLAGS_FONTCONFIG) -endif - -# iconv is enabled defaultly on OSX >= 10.3 -ifdef OSX - WITH_ICONV=1 - LIBS += -liconv -endif - -ifdef WITH_ICONV - CDEFS += -DWITH_ICONV - ifdef WITH_ICONV_PATH - CFLAGS += -I$(WITH_ICONV_PATH) - endif -endif - -# enables/disables assert() -ifdef DISABLE_ASSERTS -CFLAGS += -DNDEBUG -endif - -ifdef NO_THREADS -CFLAGS += -DNO_THREADS -endif - -# automatically disables asserts for release -ifdef RELEASE -ifndef ENABLE_ASSERTS -CFLAGS += -DNDEBUG -endif -endif - -ifdef TRANSLATOR -STRGEN_FLAGS=-t -else -STRGEN_FLAGS= -endif - -# OSX specific setup -ifdef OSX - # set the endian flag for OSX, that can't fail - ENDIAN_FORCE:=PREPROCESSOR - - # -lrt fails on OSX, so we disable it - LRT:= - - ifndef DEDICATED - LIBS += -framework QuickTime - endif - - ifdef WITH_COCOA - CDEFS += -DWITH_COCOA - LIBS += -F/System/Library/Frameworks -framework Cocoa -framework Carbon -framework AudioUnit - endif - - # OSX path setup - ifndef SECOND_DATA_PATH - SECOND_DATA_PATH:="$(OSXAPP)/Contents/Data/" - endif - - ifndef CUSTOM_LANG_DIR - ifndef DEDICATED - CUSTOM_LANG_DIR:="$(OSXAPP)/Contents/Lang/" - endif - endif -endif - -ifdef MIDI -CDEFS += -DEXTERNAL_PLAYER=\"$(MIDI)\" -ifdef MIDI_ARG -CDEFS += -DMIDI_ARG=\"$(MIDI_ARG)\" -endif -endif - -ifdef WITH_NETWORK -CDEFS += -DENABLE_NETWORK -ifdef QNX -LIBS += -lsocket -endif -endif - - -ifdef SECOND_DATA_PATH -CDEFS += -DSECOND_DATA_DIR=\"$(SECOND_DATA_PATH)/\" -endif - -ifdef CUSTOM_LANG_DIR -CDEFS += -DCUSTOM_LANG_DIR=\"$(CUSTOM_LANG_DIR)/\" -endif - -ifdef WITH_DIRECTMUSIC -CDEFS += -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT -endif - -ifdef WIN32 -LIBS += -lws2_32 -lwinmm -lgdi32 -ldxguid -lole32 -ifdef WITH_DIRECTMUSIC -LIBS += -lstdc++ -endif -TTDLDFLAGS += -Wl,--subsystem,windows -endif - -ifndef DEST_DIR -DEST_DIR = $(DESTDIR) -endif - -# sets up the paths for use for make install -ifdef INSTALL -# We use _PREFIXED vars here, so the paths are recalculated every time, and -# the prefix is not prepended in the makefile config -BINARY_DIR_PREFIXED:=$(PREFIX)/$(BINARY_DIR) -DATA_DIR_PREFIXED:=$(PREFIX)/$(DATA_DIR) -ICON_DIR_PREFIXED:=$(PREFIX)/$(ICON_DIR) -# We use _INSTALL vars here, these vars are the locations where the files will -# be installed -DATA_DIR_INSTALL=$(DEST_DIR)/$(DATA_DIR_PREFIXED) -BINARY_DIR_INSTALL=$(DEST_DIR)/$(BINARY_DIR_PREFIXED) -ICON_DIR_INSTALL=$(DEST_DIR)/$(ICON_DIR_PREFIXED) -# Let the code know where to find stuff -ifdef DATA_DIR_PREFIXED -CDEFS += -DGAME_DATA_DIR=\"$(DATA_DIR_PREFIXED)/\" -endif - -ifdef PERSONAL_DIR -CDEFS += -DPERSONAL_DIR=\"$(PERSONAL_DIR)/\" -endif - -ifdef USE_HOMEDIR -CDEFS += -DUSE_HOMEDIR -endif - -ifdef ICON_DIR -CDEFS += -DICON_DIR=\"$(ICON_DIR_PREFIXED)/\" -endif -endif - -############################################################################## -# -# What to compile -# (users do not want to modify anything below) -# - - -### Sources - -# clean up C_SOURCES first. Needed since building universal binaries on OSX calls the makefile recursively (just one time) -SRCS := - -SRCS += aircraft_cmd.c -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 += build_vehicle_gui.c -SRCS += callback_table.c -SRCS += clear_cmd.c -SRCS += command.c -SRCS += console.c -SRCS += console_cmds.c -SRCS += currency.c -SRCS += date.c -SRCS += debug.c -SRCS += dedicated.c -SRCS += depot.c -SRCS += depot_gui.c -SRCS += disaster_cmd.c -SRCS += dock_gui.c -SRCS += driver.c -SRCS += dummy_land.c -SRCS += economy.c -SRCS += elrail.c -SRCS += engine.c -SRCS += engine_gui.c -SRCS += fileio.c -SRCS += fios.c -SRCS += fontcache.c -SRCS += genworld.c -SRCS += genworld_gui.c -SRCS += gfx.c -SRCS += gfxinit.c -SRCS += graph_gui.c -SRCS += heightmap.c -SRCS += helpers.cpp -SRCS += industry_cmd.c -SRCS += industry_gui.c -SRCS += intro_gui.c -SRCS += landscape.c -SRCS += main_gui.c -SRCS += map.c -SRCS += md5.c -SRCS += mersenne.c -SRCS += minilzo.c -SRCS += misc.c -SRCS += misc_cmd.c -SRCS += misc_gui.c -SRCS += mixer.c -SRCS += music.c -SRCS += music_gui.c -SRCS += namegen.c -SRCS += network/core/packet.c -SRCS += network/core/tcp.c -SRCS += network/core/udp.c -SRCS += network/network.c -SRCS += network/network_client.c -SRCS += network/network_data.c -SRCS += network/network_gamelist.c -SRCS += network/network_gui.c -SRCS += network/network_server.c -SRCS += network/network_udp.c -SRCS += newgrf.c -SRCS += newgrf_cargo.c -SRCS += newgrf_config.c -SRCS += newgrf_engine.c -SRCS += newgrf_gui.c -SRCS += newgrf_sound.c -SRCS += newgrf_spritegroup.c -SRCS += newgrf_station.c -SRCS += newgrf_text.c -SRCS += news_gui.c -SRCS += npf.c -SRCS += oldloader.c -SRCS += oldpool.c -SRCS += openttd.c -SRCS += order_cmd.c -SRCS += order_gui.c -SRCS += os_timer.c -SRCS += pathfind.c -SRCS += player_gui.c -SRCS += players.c -SRCS += queue.c -SRCS += rail.c -SRCS += rail_cmd.c -SRCS += rail_gui.c -SRCS += rev.c -SRCS += road_cmd.c -SRCS += road_gui.c -SRCS += road_map.c -SRCS += roadveh_cmd.c -SRCS += roadveh_gui.c -SRCS += saveload.c -SRCS += screenshot.c -SRCS += settings.c -SRCS += settings_gui.c -SRCS += ship_cmd.c -SRCS += ship_gui.c -SRCS += signs.c -SRCS += smallmap_gui.c -SRCS += sound.c -SRCS += spritecache.c -SRCS += station_cmd.c -SRCS += station_gui.c -SRCS += station_map.c -SRCS += string.c -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 -SRCS += town_gui.c -SRCS += train_cmd.c -SRCS += train_gui.c -SRCS += tree_cmd.c -SRCS += tunnel_map.c -SRCS += tunnelbridge_cmd.c -SRCS += unmovable_cmd.c -SRCS += vehicle.c -SRCS += vehicle_gui.c -SRCS += viewport.c -SRCS += water_cmd.c -SRCS += waypoint.c -SRCS += widget.c -SRCS += window.c -SRCS += music/null_m.c -SRCS += sound/null_s.c -SRCS += video/dedicated_v.c -SRCS += video/null_v.c -SRCS += yapf/follow_track.cpp -SRCS += yapf/yapf_common.cpp -SRCS += yapf/yapf_rail.cpp -SRCS += yapf/yapf_road.cpp -SRCS += yapf/yapf_ship.cpp - -# AI related files -SRCS += ai/ai.c -SRCS += ai/default/default.c -SRCS += ai/trolly/build.c -SRCS += ai/trolly/pathfinder.c -SRCS += ai/trolly/shared.c -SRCS += ai/trolly/trolly.c - -ifdef WITH_SDL - SRCS += sdl.c - SRCS += sound/sdl_s.c - SRCS += video/sdl_v.c -endif - -ifdef WIN32 - SRCS += win32.c - SRCS += music/win32_m.c - SRCS += sound/win32_s.c - SRCS += video/win32_v.c -else - SRCS += unix.c - SRCS += music/extmidi.c -endif - -ifdef OSX - SRCS += os/macosx/macos.m - ifndef DEDICATED - SRCS += music/qtmidi.c - endif - ifdef WITH_COCOA - SRCS += video/cocoa_v.m - SRCS += sound/cocoa_s.c - SRCS += os/macosx/splash.c - endif -endif - -ifdef BEOS - SRCS += music/bemidi.cpp -endif - -ifdef WIN32 - SRCS += ottdres.rc -endif - -ifdef WITH_DIRECTMUSIC - SRCS += music/dmusic.cpp -endif - -OBJS += $(filter %.o, $(SRCS:%.cpp=%.o) $(SRCS:%.m=%.o) $(SRCS:%.c=%.o) $(SRCS:%.rc=%.o)) -DEPS = $(OBJS:%.o=.deps/%.d) - -LANG_TXT = $(filter-out %.unfinished.txt,$(wildcard lang/*.txt)) -LANGS = $(LANG_TXT:%.txt=%.lng) - - -############################################################################## -# -# Build commands -# - -# If we are verbose, we will show commands prefixed by $(Q). -# The $(Q)s get replaced by @ in non-verbose mode. -# Inspired by the Linux kernel build system. -ifdef VERBOSE - Q = -else - Q = @ -endif - - -############################################################################## -# -# Targets -# - - -### Normal build rules - - -ifdef OSX -# needs to be before all -OSX:=OSX -endif - - -all: endian_target.h endian_host.h $(UPDATECONFIG) $(LANGS) $(TTD) $(OSX) - -ifdef OSX --include os/macosx/Makefile -endif - -endian_host.h: $(ENDIAN_CHECK) - @echo '===> Testing endianness for host' - $(Q)./$(ENDIAN_CHECK) > $@ - -endian_target.h: $(ENDIAN_CHECK) - @echo '===> Testing endianness for target' - $(Q)./$(ENDIAN_CHECK) $(ENDIAN_FORCE) > $@ - -$(ENDIAN_CHECK): endian_check.c - @echo '===> Compiling and Linking $@' - $(Q)$(CC_HOST) $(CFLAGS_HOST) $(CDEFS) $< -o $@ - - -ifndef MACOSX_BUILD -# OSX links in os/macosx/Makefile to handle universal binaries better -$(TTD): $(OBJS) $(MAKE_CONFIG) - @echo '===> Linking $@' - $(Q)$(CXX_TARGET) $(LDFLAGS) $(TTDLDFLAGS) $(OBJS) $(LIBS) -o $@ -endif - -$(STRGEN): strgen/strgen.c string.c endian_host.h table/control_codes.h - @echo '===> Compiling and Linking $@' - $(Q)$(CC_HOST) $(CFLAGS_HOST) -DSTRGEN strgen/strgen.c string.c -o $@ - -table/strings.h: lang/english.txt $(STRGEN) - @echo '===> Generating $@' - $(Q)$(STRGEN) -s lang -d table - -lang/%.lng: lang/%.txt $(STRGEN) lang/english.txt - @echo '===> Compiling language $(*F)' - $(Q)$(STRGEN) $(STRGEN_FLAGS) -s lang -d lang $< $(LANG_ERRORS) || rm -f $@ - -ifdef MORPHOS - -release: all - $(Q)rm -fr "/t/openttd-$(RELEASE)-morphos.lha" - $(Q)mkdir -p "/t/" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/docs" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/data" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/lang" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/scenario" - $(Q)mkdir -p "/t/openttd-$(RELEASE)-morphos/scenario/heightmap" - $(Q)cp -R $(TTD) "/t/openttd-$(RELEASE)-morphos/" - $(Q)cp data/* "/t/openttd-$(RELEASE)-morphos/data/" - $(Q)cp lang/*.lng "/t/openttd-$(RELEASE)-morphos/lang/" - $(Q)-cp scenario/*.scn "/t/openttd-$(RELEASE)-morphos/scenario/" - $(Q)-cp scenario/heightmap/* "/t/openttd-$(RELEASE)-morphos/scenario/heightmap/" - $(Q)cp readme.txt "/t/openttd-$(RELEASE)-morphos/docs/ReadMe" - $(Q)cp docs/console.txt "/t/openttd-$(RELEASE)-morphos/docs/Console" - $(Q)cp COPYING "/t/openttd-$(RELEASE)-morphos/docs/" - $(Q)cp changelog.txt "/t/openttd-$(RELEASE)-morphos/docs/ChangeLog" - $(Q)cp known-bugs.txt "/t/openttd-$(RELEASE)-morphos/docs/known-bugs.txt" - $(Q)cp os/morphos/icons/openttd.info "/t/openttd-$(RELEASE)-morphos/$(TTD).info" - $(Q)cp os/morphos/icons/docs.info "/t/openttd-$(RELEASE)-morphos/docs.info" - $(Q)cp os/morphos/icons/drawer.info "/t/openttd-$(RELEASE)-morphos.info" - $(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/ReadMe.info" - $(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/Console.info" - $(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/COPYING.info" - $(Q)cp os/morphos/icons/document.info "/t/openttd-$(RELEASE)-morphos/docs/ChangeLog.info" - $(Q)strip --strip-all --strip-unneeded --remove-section .comment "/t/openttd-$(RELEASE)-morphos/$(TTD)" - $(Q)lha a -r "t:openttd-$(RELEASE)-morphos.lha" "t:openttd-$(RELEASE)-morphos" - $(Q)lha a "t:openttd-$(RELEASE)-morphos.lha" "t:openttd-$(RELEASE)-morphos.info" - $(Q)rm -fr "/t/openttd-$(RELEASE)-morphos" - $(Q)rm -fr "/t/openttd-$(RELEASE)-morphos.info" - @echo "Release archive can be found in RAM:t/ now." - -.PHONY: release -endif - -rev.c: FORCE - @# setting the revision number in a place, there the binary can read it - @echo 'const char _openttd_revision[] = "$(REV)";' >>rev.c.new - @# some additions for MorphOS versions tag - @echo '#ifdef __MORPHOS__' >>rev.c.new - @echo 'const char morphos_versions_tag[] = "\\0$$VER: OpenTTD $(REV) ('${BUILDDATE}') © OpenTTD Team [MorphOS, PowerPC]";' >>rev.c.new - @echo '#endif' >>rev.c.new - @# Only update the real rev.c if it actually changed, to prevent - @# useless rebuilds. - @cmp -s rev.c rev.c.new 2>/dev/null || mv rev.c.new rev.c - @rm -f rev.c.new - -FORCE: - - -clean: - @echo '===> Cleaning up' -# endian.h is out-dated and no longer in use, so it can be removed soon - $(Q)rm -rf .deps *~ $(TTD) $(STRGEN) core table/strings.h $(LANGS) $(OBJS) $(OSX_MIDI_PLAYER_FILE) endian.h endian_host.h endian_target.h $(ENDIAN_CHECK) .OSX - -mrproper: clean - $(Q)rm -rf $(MAKE_CONFIG) - -ifndef OSX -ifndef MORPHOS -install: -ifeq ($(INSTALL),) - $(error make install is highly experimental at his state and not\ - tested very much - use at your own risk - to use run \"make install INSTALL:=1\" - make sure Makefile.config\ - is set correctly up - run \"make upgradeconf\") -endif - -ifeq ($(PREFIX), ) - $(error no prefix set - check Makefile.config) -endif -# We compare against the non prefixed version here, so we won't install -# if only the prefix has been set -ifeq ($(DATA_DIR),) - $(error no data path set - check Makefile.config) -endif -ifeq ($(BINARY_DIR),) - $(error no binary path set - check Makefile.config) -endif -# We'll install in $DEST_DIR instead of root if it is set (we don't -# care about extra /'s - install -d $(DATA_DIR_INSTALL)/lang \ - $(DATA_DIR_INSTALL)/data \ - $(DATA_DIR_INSTALL)/gm \ - $(ICON_DIR_INSTALL) \ - $(BINARY_DIR_INSTALL) -ifndef USE_HOMEDIR - mkdir -p $(PERSONAL_DIR)/scenario - mkdir -p $(PERSONAL_DIR)/scenario/heightmap -else - mkdir -p $(DATA_DIR_INSTALL)/scenario - mkdir -p $(DATA_DIR_INSTALL)/scenario/heightmap -endif - install $(TTD) $(BINARY_DIR_INSTALL) - install -m 644 lang/*.lng $(DATA_DIR_INSTALL)/lang - install -m 644 data/*.grf $(DATA_DIR_INSTALL)/data - install -m 644 data/opntitle.dat $(DATA_DIR_INSTALL)/data - # Generic menu icon - install -m 644 media/openttd.64.png $(ICON_DIR_INSTALL) - # Debian menu icon - install -m 644 media/openttd.32.xpm $(ICON_DIR_INSTALL) - # Window icon - install -m 644 media/openttd.32.bmp $(ICON_DIR_INSTALL) -else #MorphOS -install: - $(error make install is not supported on MorphOS) -endif -else # OSX -install: - $(error make install is not supported on MacOSX) -endif - - -love: - @echo "YES! I thought you would never ask. We will have a great time. You can keep me turned on all night" - -.PHONY: clean all $(OSX) install love - - -### Automatic configuration --include $(CONFIG_WRITER) - - -# Export all variables set to subprocesses (a bit dirty) -.EXPORT_ALL_VARIABLES: -upgradeconf: $(MAKE_CONFIG) - $(Q)rm $(MAKE_CONFIG) - $(Q)$(MAKE) $(MAKE_CONFIG) - -.PHONY: upgradeconf - - -### Internal build rules - -# This makes sure the .deps dir is always around. -DEPS_MAGIC := $(shell mkdir -p $(sort $(dir $(DEPS)))) - -depend: - @true # The include handles this automagically - -# Introduce the dependencies -ifeq ($(findstring $(MAKECMDGOALS), clean info mrproper upgradeconf $(MAKE_CONFIG)),) --include $(DEPS) -endif - -# Silence stale header dependency errors -%.h: - @true - -.deps/%.d: %.c $(MAKE_CONFIG) table/strings.h endian_target.h - @echo '===> DEP $<' - $(Q)$(CC_TARGET) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@ - -.deps/%.d: %.cpp $(MAKE_CONFIG) table/strings.h endian_target.h - @echo '===> DEP $<' - $(Q)$(CXX_TARGET) $(CFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@ - -.deps/%.d: %.m $(MAKE_CONFIG) table/strings.h endian_target.h - @echo '===> DEP $<' - $(Q)$(CC_TARGET) $(OBJCFLAGS) $(CDEFS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:.deps/%.d=%.o):#' > $@ - - -ifndef MACOSX_BUILD -# OSX uses os/macosx/Makefile to compile files -%.o: %.c $(MAKE_CONFIG) - @echo '===> Compiling $<' - $(Q)$(CC_TARGET) $(CC_CFLAGS) $(CFLAGS) $(CDEFS) -c -o $@ $< - -%.o: %.cpp $(MAKE_CONFIG) - @echo '===> Compiling $<' - $(Q)$(CXX_TARGET) $(CFLAGS) $(CDEFS) -c -o $@ $< - -%.o: %.m $(MAKE_CONFIG) - @echo '===> Compiling $<' - $(Q)$(CC_TARGET) $(CC_CFLAGS) $(CFLAGS) $(CDEFS) -c -o $@ $< -endif - -%.o: %.rc - @echo '===> Compiling resource $<' - $(Q)$(WINDRES) -o $@ $< - - -info: - @echo 'CFLAGS = $(CFLAGS)' - @echo 'LDFLAGS = $(LDFLAGS)' - @echo 'LIBS = $(LIBS)' - @echo 'CDEFS = $(CDEFS)' diff --git a/Makefile.in b/Makefile.in new file mode 100644 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,249 @@ +# Auto-generated file -- DO NOT EDIT + +# Check if we want to show what we are doing +ifdef VERBOSE + Q = +else + Q = @ +endif + +include Makefile.am + +SOURCE_LIST = !!SOURCE_LIST!! +CONFIG_CACHE_SOURCE_LIST = !!CONFIG_CACHE_SOURCE_LIST!! +CONFIGURE_FILES = !!CONFIGURE_FILES!! +LIPO = !!LIPO!! +BIN_DIR = !!BIN_DIR!! +SRC_DIR = !!SRC_DIR!! +ROOT_DIR = !!ROOT_DIR!! +BUNDLE_DIR = "$(ROOT_DIR)/bundle" +BUNDLES_DIR = "$(ROOT_DIR)/bundles" +INSTALL_DIR = !!INSTALL_DIR!! +INSTALL_BINARY_DIR = "$(INSTALL_DIR)/"!!BINARY_DIR!! +INSTALL_ICON_DIR = "$(INSTALL_DIR)/"!!ICON_DIR!! +INSTALL_DATA_DIR = "$(INSTALL_DIR)/"!!DATA_DIR!! +INSTALL_PERSONAL_DIR = !!PERSONAL_DIR!! +# TODO: ENABLE_INSTALL should be removed when the search path patch has been applied +ENABLE_INSTALL = !!ENABLE_INSTALL!! +TTD = !!TTD!! +TTDS = $(SRC_DIRS:%=%/$(TTD)) +OS = !!OS!! +OSXAPP = !!OSXAPP!! + +RES := $(shell if ! [ -f $(CONFIG_CACHE_SOURCE_LIST) ] || [ -n "`cmp $(CONFIG_CACHE_SOURCE_LIST) $(SOURCE_LIST)`" ]; then cp $(SOURCE_LIST) $(CONFIG_CACHE_SOURCE_LIST); fi ) + +all: config.cache + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir all; \ + done +ifdef LIPO +# Lipo is an OSX thing. If it is defined, it means we are building for universal, +# and so we have have to combine the binaries into one big binary + +# Remove the last binary made by the last compiled target + rm -f $(BIN_DIR)/$(TTD) +# Make all the binaries into one + $(LIPO) -create -output $(BIN_DIR)/$(TTD) $(TTDS) +endif + +config.cache: $(CONFIG_CACHE_SOURCE_LIST) $(CONFIGURE_FILES) +ifeq ($(shell if test -f config.cache; then echo 1; fi), 1) + @echo "----------------" + @echo "The system detected that source.list or any configure file is altered." + @echo " Going to reconfigure with last known settings..." + @echo "----------------" +# Make sure we don't lock config.cache + @$(shell cat config.cache) || exit 1 + @echo "----------------" + @echo "Reconfig done. Now compiling..." + @echo "----------------" +else + @echo "----------------" + @echo "Have not found a configuration, please run configure first." + @echo "----------------" + @exit 1 +endif + +clean: + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir clean; \ + done + $(Q)rm -rf $(BUNDLE_TARGET) + +lang: + @for dir in $(LANG_DIRS); do \ + $(MAKE) -C $$dir all; \ + done + +mrproper: + @for dir in $(DIRS); do \ + $(MAKE) -C $$dir mrproper; \ + rm -f $$dir/Makefile; \ + done + $(Q)rm -rf objs + $(Q)rm -f Makefile Makefile.am + $(Q)rm -f $(CONFIG_CACHE_SOURCE_LIST) config.cache config.log + $(Q)rm -rf $(BUNDLE_DIR) + $(Q)rm -rf $(BUNDLES_DIR) + +depend: + @for dir in $(SRC_DIRS); do \ + $(MAKE) -C $$dir depend; \ + done + +run: all + $(Q)cd !!BIN_DIR!! && ./!!TTD!! + +%.o: + @for dir in $(SRC_DIRS); do \ + $(MAKE) -C $$dir $@; \ + done + +%.lng: + @for dir in $(LANG_DIRS); do \ + $(MAKE) -C $$dir $@; \ + done + +# +# Creation of bundles +# + +# The revision is needed for the bundle name and creating an OSX application bundle. +ifdef REVISION +REV := $(REVISION) +else +# Are we a SVN dir? +ifeq ($(shell if test -d $(SRC_DIR)/.svn; then echo 1; fi), 1) +# Find if the local source if modified +REV_MODIFIED := $(shell svnversion $(SRC_DIR) | sed -n 's/.*\(M\).*/\1/p' ) +# Find the revision like: rXXXX-branch +REV := $(shell LC_ALL=C svn info $(SRC_DIR) | awk '/^URL:.*branches/ { split($$2, a, "/"); BRANCH="-"a[5] } /^Last Changed Rev:/ { REV="r"$$4"$(REV_MODIFIED)" } END { print REV BRANCH }') +endif +endif +# Make sure we have something in REV +ifeq ($(REV),) +REV := norev000 +endif + +ifndef BUNDLE_NAME +BUNDLE_NAME = OTTD-$(OS)-custom-$(REV) +endif + +# An OSX application bundle needs the data files, lang files and openttd executable in a different location. +ifdef OSXAPP +DATA_DIR = $(BUNDLE_DIR)/$(OSXAPP)/Contents/Data +LANG_DIR = $(BUNDLE_DIR)/$(OSXAPP)/Contents/Lang +TTD_DIR = $(BUNDLE_DIR)/$(OSXAPP)/Contents/MacOS +else +DATA_DIR = $(BUNDLE_DIR)/data +LANG_DIR = $(BUNDLE_DIR)/lang +TTD_DIR = $(BUNDLE_DIR) +endif + +bundle: all + @echo '[BUNDLE] Constructing bundle' + $(Q)rm -rf "${BUNDLE_DIR}" + $(Q)mkdir -p "${BUNDLE_DIR}" + $(Q)mkdir -p "$(BUNDLE_DIR)/docs" + $(Q)mkdir -p "$(BUNDLE_DIR)/scenario" + $(Q)mkdir -p "$(BUNDLE_DIR)/scenario/heightmap" + $(Q)mkdir -p "$(BUNDLE_DIR)/media" + $(Q)mkdir -p "$(TTD_DIR)" + $(Q)mkdir -p "$(DATA_DIR)" + $(Q)mkdir -p "$(LANG_DIR)" +ifdef OSXAPP + $(Q)mkdir -p "$(BUNDLE_DIR)/$(OSXAPP)/Contents/Resources" + $(Q)echo "APPL????" > "$(BUNDLE_DIR)/$(OSXAPP)/Contents/PkgInfo" + $(Q)cp "$(ROOT_DIR)/os/macosx/openttd.icns" "$(BUNDLE_DIR)/$(OSXAPP)/Contents/Resources/openttd.icns" + $(Q)$(ROOT_DIR)/os/macosx/plistgen.sh "${BUNDLE_DIR}/$(OSXAPP)" "$(REV)" + $(Q)cp "$(ROOT_DIR)/docs/OSX_install_instructions.txt" "$(BUNDLE_DIR)/docs/" + $(Q)cp "$(ROOT_DIR)/docs/OSX_why_multiple_applications.txt" "$(BUNDLE_DIR)/docs/" + $(Q)cp "$(ROOT_DIR)/os/macosx/splash.png" "$(DATA_DIR)" +endif + $(Q)cp "$(BIN_DIR)/$(TTD)" "$(TTD_DIR)/" + $(Q)cp "$(BIN_DIR)/data/"*.grf "$(DATA_DIR)/" + $(Q)cp "$(BIN_DIR)/data/opntitle.dat" "$(DATA_DIR)/" + $(Q)cp "$(BIN_DIR)/lang/"*.lng "$(LANG_DIR)/" + $(Q)cp "$(ROOT_DIR)/readme.txt" "$(BUNDLE_DIR)/" + $(Q)cp "$(ROOT_DIR)/COPYING" "$(BUNDLE_DIR)/" + $(Q)cp "$(ROOT_DIR)/known-bugs.txt" "$(BUNDLE_DIR)/docs/" + $(Q)cp "$(ROOT_DIR)/docs/multiplayer.txt" "$(BUNDLE_DIR)/docs/" + $(Q)cp "$(ROOT_DIR)/changelog.txt" "$(BUNDLE_DIR)/docs/" + $(Q)cp "$(ROOT_DIR)/media/openttd.64.png" "$(BUNDLE_DIR)/media/" + $(Q)cp "$(ROOT_DIR)/media/openttd.32.xpm" "$(BUNDLE_DIR)/media/" + $(Q)cp "$(ROOT_DIR)/media/openttd.32.bmp" "$(BUNDLE_DIR)/media/" +ifeq ($(shell if test -d $(BIN_DIR)/scenario/*.scn; then echo 1; fi), 1) + $(Q)cp "$(BIN_DIR)/scenario/"*.scn "$(BUNDLE_DIR)/scenario/" +endif +ifeq ($(shell if test -d $(BIN_DIR)/scenario/heightmaps/*; then echo 1; fi), 1) + $(Q)cp "$(BIN_DIR)/scenario/heightmaps/"* "$(BUNDLE_DIR)/scenario/heightmap/" +endif + +### Packing the current bundle into several compressed file formats ### +# +# Zips & dmgs do not contain a root folder, i.e. they have files in the root of the zip/dmg. +# gzip, bzip2 and lha archives have a root folder, with the same name as the bundle. +# +# One can supply a custom name by adding BUNDLE_NAME:= to the make command. +# +bundle_zip: bundle + @echo '[BUNDLE] Creating $(BUNDLE_NAME).zip' + $(Q)mkdir -p "$(BUNDLES_DIR)" + $(Q)cd "$(BUNDLE_DIR)" && zip -r $(shell if test -z "$(VERBOSE)"; then echo '-q'; fi) "$(BUNDLES_DIR)/$(BUNDLE_NAME).zip" . + +bundle_gzip: bundle + @echo '[BUNDLE] Creating $(BUNDLE_NAME).tar.gz' + $(Q)mkdir -p "$(BUNDLES_DIR)/.gzip/$(BUNDLE_NAME)" + $(Q)cp -R "$(BUNDLE_DIR)/"* "$(BUNDLES_DIR)/.gzip/$(BUNDLE_NAME)/" + $(Q)cd "$(BUNDLES_DIR)/.gzip" && tar -zc$(shell if test -n "$(VERBOSE)"; then echo 'v'; fi)f "$(BUNDLES_DIR)/$(BUNDLE_NAME).tar.gz" "$(BUNDLE_NAME)" + $(Q)rm -rf "$(BUNDLES_DIR)/.gzip" + +bundle_bzip2: bundle + @echo '[BUNDLE] Creating $(BUNDLE_NAME).tar.bz2' + $(Q)mkdir -p "$(BUNDLES_DIR)/.bzip2/$(BUNDLE_NAME)" + $(Q)cp -R "$(BUNDLE_DIR)/"* "$(BUNDLES_DIR)/.bzip2/$(BUNDLE_NAME)/" + $(Q)cd "$(BUNDLES_DIR)/.bzip2" && tar -jc$(shell if test -n "$(VERBOSE)"; then echo 'v'; fi)f "$(BUNDLES_DIR)/$(BUNDLE_NAME).tar.bz2" "$(BUNDLE_NAME)" + $(Q)rm -rf "$(BUNDLES_DIR)/.bzip2" + +bundle_lha: bundle + @echo '[BUNDLE] Creating $(BUNDLE_NAME).lha' + $(Q)mkdir -p "$(BUNDLES_DIR)/.lha/$(BUNDLE_NAME)" + $(Q)cp -R "$(BUNDLE_DIR)/"* "$(BUNDLES_DIR)/.lha/$(BUNDLE_NAME)/" + $(Q)cd "$(BUNDLES_DIR)/.lha" && lha ao6 "$(BUNDLES_DIR)/$(BUNDLE_NAME).lha" "$(BUNDLE_NAME)" + $(Q)rm -rf "$(BUNDLES_DIR)/.lha" + +bundle_dmg: bundle + @echo '[BUNDLE] Creating $(BUNDLE_NAME).dmg' + $(Q)mkdir -p "$(BUNDLES_DIR)" + $(Q)hdiutil create -ov -format UDZO -srcfolder "$(BUNDLE_DIR)" "$(BUNDLES_DIR)/$(BUNDLE_NAME).dmg" + +# TODO: ENABLE_INSTALL should be removed when the search path patch has been applied +ifeq ($(ENABLE_INSTALL), 0) +install: + @echo '[INSTALL] Cannot install. Not compiled with installation paths' +else +ifdef OSXAPP +install: + @echo '[INSTALL] Cannot install the OSX Application Bundle' +else +install: bundle + @echo '[INSTALL] Installing OpenTTD' + $(Q)install -d "$(INSTALL_BINARY_DIR)" + $(Q)install -d "$(INSTALL_ICON_DIR)" + $(Q)install -d "$(INSTALL_DATA_DIR)/gm" + $(Q)install -d "$(INSTALL_DATA_DIR)/data" + $(Q)install -d "$(INSTALL_DATA_DIR)/lang" + $(Q)install -d "$(INSTALL_DATA_DIR)/docs" + $(Q)install -m 755 "$(BUNDLE_DIR)/$(TTD)" "$(INSTALL_BINARY_DIR)" + $(Q)install -m 644 "$(BUNDLE_DIR)/lang/"* "$(INSTALL_DATA_DIR)/lang" + $(Q)install -m 644 "$(BUNDLE_DIR)/data/"* "$(INSTALL_DATA_DIR)/data" + $(Q)install -m 644 "$(BUNDLE_DIR)/docs/"* "$(INSTALL_DATA_DIR)/docs" + $(Q)install -m 644 "$(BUNDLE_DIR)/media/"* "$(INSTALL_ICON_DIR)" +ifdef INSTALL_PERSONAL_DIR + $(Q)mkdir -p ~/"$(INSTALL_PERSONAL_DIR)" + $(Q)cp -R "$(BUNDLE_DIR)/scenario" ~/"$(INSTALL_PERSONAL_DIR)" +else + $(Q)cp -R "$(BUNDLE_DIR)/scenario" "$(INSTALL_DATA_DIR)" +endif # INSTALL_PERSONAL_DIR +endif # OSXAPP +endif # ENABLE_INSTALL diff --git a/Makefile.lang.in b/Makefile.lang.in new file mode 100644 --- /dev/null +++ b/Makefile.lang.in @@ -0,0 +1,85 @@ +# Auto-generated file -- DO NOT EDIT + +STRGEN = !!STRGEN!! +ENDIAN_CHECK = !!ENDIAN_CHECK!! +SRC_DIR = !!SRC_DIR!! +LANG_DIR = !!LANG_DIR!! +BIN_DIR = !!BIN_DIR!! +LANGS_SRC = $(shell ls $(LANG_DIR)/*.txt) +LANGS = $(LANGS_SRC:$(LANG_DIR)/%.txt=%.lng) +CC_BUILD = !!CC_BUILD!! +CFLAGS_BUILD = !!CFLAGS_BUILD!! +STRGEN_FLAGS = !!STRGEN_FLAGS!! +STAGE = !!STAGE!! +LANG_SUPPRESS= !!LANG_SUPPRESS!! +LANG_OBJS_DIR= !!LANG_OBJS_DIR!! + +ifeq ($(LANG_SUPPRESS), yes) +LANG_ERRORS = >/dev/null 2>&1 +endif + +# Make sure endian_host.h is reasable as if it was in the src/ dir +CFLAGS_BUILD += -I $(LANG_OBJS_DIR) + +ENDIAN_TARGETS := endian_host.h endian_target.h $(ENDIAN_CHECK) + +# Check if we want to show what we are doing +ifdef VERBOSE + Q = + E = @true +else + Q = @ + E = @echo +endif + +RES := $(shell mkdir -p $(BIN_DIR)/lang ) + +all: table/strings.h $(LANGS) + +strgen.o: $(SRC_DIR)/strgen/strgen.c endian_host.h + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.c=%.c)' + $(Q)$(CC_BUILD) $(CFLAGS_BUILD) -DSTRGEN -c -o $@ $< + +string.o: $(SRC_DIR)/string.c endian_host.h + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.c=%.c)' + $(Q)$(CC_BUILD) $(CFLAGS_BUILD) -DSTRGEN -c -o $@ $< + +lang/english.txt: $(LANG_DIR)/english.txt + $(Q)mkdir -p lang + $(Q)cp $(LANG_DIR)/english.txt lang/english.txt + +$(STRGEN): string.o strgen.o + $(E) '$(STAGE) Compiling and Linking $@' + $(Q)$(CC_BUILD) string.o strgen.o -o $@ + +table/strings.h: lang/english.txt $(STRGEN) + $(E) '$(STAGE) Generating $@' + @mkdir -p table + $(Q)./$(STRGEN) -s $(LANG_DIR) -d table + +$(LANGS): %.lng: $(LANG_DIR)/%.txt $(STRGEN) lang/english.txt + $(E) '$(STAGE) Compiling language $(*F)' + $(Q)./$(STRGEN) $(STRGEN_FLAGS) -s $(LANG_DIR) -d $(LANG_OBJS_DIR) $< $(LANG_ERRORS) && cp $@ $(BIN_DIR)/lang + +# The targets to compile the endian-code + +endian_host.h: $(ENDIAN_CHECK) + $(E) '$(STAGE) Testing endianness for host' + $(Q)./$(ENDIAN_CHECK) > $@ + +$(ENDIAN_CHECK): $(SRC_DIR)/endian_check.c + $(E) '$(STAGE) Compiling and Linking $@' + $(Q)$(CC_BUILD) $(CFLAGS_BUILD) $< -o $@ + +depend: + +clean: + $(E) '$(STAGE) Cleaning up language files' + $(Q)rm -f strgen.o table/strings.h $(STRGEN) $(LANGS) $(LANGS:%=$(BIN_DIR)/lang/%) lang/english.* $(ENDIAN_TARGETS) + +mrproper: clean + +%.lng: + @echo '$(STAGE) No such language: $(@:%.lng=%)' + +.PHONY: all mrproper depend clean diff --git a/Makefile.src.in b/Makefile.src.in new file mode 100644 --- /dev/null +++ b/Makefile.src.in @@ -0,0 +1,263 @@ +# Auto-generated file -- DO NOT EDIT + +CC_HOST = !!CC_HOST!! +CXX_HOST = !!CXX_HOST!! +CC_BUILD = !!CC_BUILD!! +WINDRES = !!WINDRES!! +STRIP = !!STRIP!! +CC_CFLAGS = !!CC_CFLAGS!! +CFLAGS = !!CFLAGS!! +CFLAGS_BUILD = !!CFLAGS_BUILD!! +LIBS = !!LIBS!! +LDFLAGS = !!LDFLAGS!! +BIN_DIR = !!BIN_DIR!! +LANG_DIR = !!LANG_DIR!! +SRC_OBJS_DIR = !!SRC_OBJS_DIR!! +LANG_OBJS_DIR= !!LANG_OBJS_DIR!! +SRC_DIR = !!SRC_DIR!! +MEDIA_DIR = !!MEDIA_DIR!! +TTD = !!TTD!! +STRGEN = !!STRGEN!! +ENDIAN_CHECK = !!ENDIAN_CHECK!! +ENDIAN_FORCE = !!ENDIAN_FORCE!! +OS = !!OS!! +STAGE = !!STAGE!! +MAKEDEPEND = !!MAKEDEPEND!! +CFLAGS_MAKEDEP= !!CFLAGS_MAKEDEP!! +SORT = !!SORT!! +CONFIG_CACHE_COMPILER = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_COMPILER!! +CONFIG_CACHE_LINKER = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_LINKER!! +CONFIG_CACHE_ENDIAN = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_ENDIAN!! +CONFIG_CACHE_SOURCE = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_SOURCE!! +CONFIG_CACHE_VERSION = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_VERSION!! + +OBJS_C := !!OBJS_C!! +OBJS_CPP := !!OBJS_CPP!! +OBJS_M := !!OBJS_M!! +OBJS_RC := !!OBJS_RC!! +OBJS := $(OBJS_C) $(OBJS_CPP) $(OBJS_M) $(OBJS_RC) +SRCS := !!SRCS!! + +# All C-files depend on those 3 files +FILE_DEP := $(CONFIG_CACHE_COMPILER) $(LANG_OBJS_DIR)/table/strings.h endian_target.h +# Create all dirs and subdirs +RES := $(shell mkdir -p $(BIN_DIR) $(sort $(dir $(OBJS)))) + +# Make sure endian_target.h is reasable as if it was in the src/ dir +CFLAGS += -I $(SRC_OBJS_DIR) -I $(LANG_OBJS_DIR) + +ENDIAN_TARGETS := endian_target.h $(ENDIAN_CHECK) + +# Check if we want to show what we are doing +ifdef VERBOSE + Q = + E = @true +else + Q = @ + E = @echo +endif + +# Our default target +all: $(TTD) + +# This are 2 rules that are pointing back to STRGEN stuff. +# There is not really a need to have them here, but in case +# some weirdo wants to run 'make' in the 'src' dir and expects +# the languages to be recompiled, this catches that case and +# takes care of it nicely. +$(LANG_OBJS_DIR)/$(STRGEN): + $(MAKE) -C $(LANG_OBJS_DIR) $(STRGEN) + +$(LANG_OBJS_DIR)/table/strings.h: $(LANG_DIR)/english.txt $(LANG_OBJS_DIR)/$(STRGEN) + $(MAKE) -C $(LANG_OBJS_DIR) table/strings.h + +# Make the revision number +ifdef REVISION +REV := $(REVISION) +else +# Are we a SVN dir? +ifeq ($(shell if test -d $(SRC_DIR)/.svn; then echo 1; fi), 1) +# Find if the local source if modified +REV_MODIFIED := $(shell svnversion $(SRC_DIR) | sed -n 's/.*\(M\).*/\1/p' ) +# Find the revision like: rXXXX-branch +REV := $(shell LC_ALL=C svn info $(SRC_DIR) | awk '/^URL:.*branch/ { split($$2, a, "/"); BRANCH="-"a[5] } /^Last Changed Rev:/ { REV="r"$$4"$(REV_MODIFIED)" } END { print REV BRANCH }') +endif +endif +# Make sure we have something in REV +ifeq ($(REV),) +REV := norev000 +endif + +# This helps to recompile if flags change +RES := $(shell if [ "`cat $(CONFIG_CACHE_COMPILER) 2>/dev/null`" != "$(CC_CFLAGS) $(CFLAGS)" ]; then echo "$(CC_CFLAGS) $(CFLAGS)" > $(CONFIG_CACHE_COMPILER); fi ) +RES := $(shell if [ "`cat $(CONFIG_CACHE_LINKER) 2>/dev/null`" != "$(LDFLAGS) $(LIBS)" ]; then echo "$(LDFLAGS) $(LIBS)" > $(CONFIG_CACHE_LINKER); fi ) +RES := $(shell if [ "`cat $(CONFIG_CACHE_ENDIAN) 2>/dev/null`" != "$(ENDIAN_FORCE)" ]; then echo "$(ENDIAN_FORCE)" > $(CONFIG_CACHE_ENDIAN); fi ) + +# If there is a change in the source-file-list, make sure we recheck the deps +RES := $(shell if [ "`cat $(CONFIG_CACHE_SOURCE) 2>/dev/null`" != "$(SRCS)" ]; then echo "$(SRCS)" > $(CONFIG_CACHE_SOURCE); fi ) +# If there is a change in the revision, make sure we recompile rev.c +RES := $(shell if [ "`cat $(CONFIG_CACHE_VERSION) 2>/dev/null`" != "$(REV)" ]; then echo "$(REV)" > $(CONFIG_CACHE_VERSION); fi ) + +ifndef MAKEDEPEND +# The slow, but always correct, dep-check +DEP_MASK := %.d +DEPS := $(OBJS:%.o=%.d) + +# Only include the deps if we are compiling everything +ifeq ($(filter $(ENDIAN_TARGETS) %.o clean mrproper, $(MAKECMDGOALS)),) +-include $(DEPS) +else +# In case we want to compile a single target, include the .d file for it +ifneq ($(filter %.o, $(MAKECMDGOALS)),) +SINGLE_DEP := $(filter %.o, $(MAKECMDGOALS)) +-include $(SINGLE_DEP:%.o=%.d) +endif +endif + +# Find the deps via GCC. Rarely wrong, but a bit slow + +$(OBJS_C:%.o=%.d): %.d: $(SRC_DIR)/%.c $(FILE_DEP) + $(E) '$(STAGE) DEP $(<:$(SRC_DIR)/%.c=%.c)' + $(Q)$(CC_HOST) $(CC_CFLAGS) $(CFLAGS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:%.d=%.o):#' > $@ + +$(OBJS_CPP:%.o=%.d): %.d: $(SRC_DIR)/%.cpp $(FILE_DEP) + $(E) '$(STAGE) DEP $(<:$(SRC_DIR)/%.cpp=%.cpp)' + $(Q)$(CXX_HOST) $(CFLAGS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:%.d=%.o):#' > $@ + +$(OBJS_M:%.o=%.d): %.d: $(SRC_DIR)/%.m $(FILE_DEP) + $(E) '$(STAGE) DEP $(<:$(SRC_DIR)/%.m=%.m)' + $(Q)$(CC_HOST) $(CC_CFLAGS) $(CFLAGS) -MM $< | sed 's#^$(@F:%.d=%.o):#$@ $(@:%.d=%.o):#' > $@ + +else +# The much faster, but can be wrong, dep-check +DEP_MASK := +DEPS := Makefile.dep + +# Only include the deps if we are not cleaning +ifeq ($(filter $(ENDIAN_TARGETS) depend clean mrproper, $(MAKECMDGOALS)),) +-include Makefile.dep +endif + +# Make sure that only 'make depend' ALWAYS triggers a recheck +ifeq ($(filter depend, $(MAKECMDGOALS)),) +Makefile.dep: $(FILE_DEP) $(SRCS:%=$(SRC_DIR)/%) $(CONFIG_CACHE_SOURCE) +else +Makefile.dep: FORCE +endif + $(E) '$(STAGE) DEP CHECK (all files)' + $(Q)rm -f Makefile.dep.tmp + $(Q)touch Makefile.dep.tmp + +# Calculate the deps via makedepend + $(Q)$(MAKEDEPEND) -f$(SRC_OBJS_DIR)/Makefile.dep.tmp -o.o -Y -v -- $(CFLAGS_MAKEDEP) -- $(SRCS:%=$(SRC_DIR)/%) 2>/dev/null + +# Convert x:/... paths to /x/... for mingw +ifeq ($(OS), MINGW) + @cat Makefile.dep.tmp | sed 's@\([a-zA-Z]\):/@/\1/@g' > Makefile.dep.tmp.mingw + @cp Makefile.dep.tmp.mingw Makefile.dep.tmp + @rm -f Makefile.dep.tmp.mingw +endif + +# Remove all comments and includes that don't start with $(SRC_DIR) +# Remove $(SRC_DIR) from object-file-name + @awk ' \ + /^# DO NOT/ { print $$0 ; next} \ + /^#/ {next} \ + /:/ { \ + left = NF - 1; \ + for (n = 2; n <= NF; n++) { \ + if (match($$n, "^$(SRC_DIR)") == 0) { \ + $$n = ""; \ + left--; \ + } \ + } \ + gsub("$(SRC_DIR)/", "", $$1); \ + if (left > 0) { \ + print $$0; \ + $$1 = "Makefile.dep:"; \ + print $$0; \ + } \ + next \ + } \ + { \ + print $$0 \ + } \ + ' < Makefile.dep.tmp | sed 's/ */ /g;s/ $$//' | $(SORT) > Makefile.dep + + $(Q)rm -f Makefile.dep.tmp Makefile.dep.tmp.bak + +endif + +# Avoid problems with deps if a .h file is deleted without the deps +# being updated. Now the Makefile continues, the deps are recreated +# and all will be fine. +%.h: + @true + + +# Compile all the files according to the targets + +$(OBJS_C): %.o: $(SRC_DIR)/%.c $(DEP_MASK) $(FILE_DEP) + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.c=%.c)' + $(Q)$(CC_HOST) $(CC_CFLAGS) $(CFLAGS) -c -o $@ $< + +$(OBJS_CPP): %.o: $(SRC_DIR)/%.cpp $(DEP_MASK) $(FILE_DEP) + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.cpp=%.cpp)' + $(Q)$(CXX_HOST) $(CFLAGS) -c -o $@ $< + +$(OBJS_M): %.o: $(SRC_DIR)/%.m $(DEP_MASK) $(FILE_DEP) + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.m=%.m)' + $(Q)$(CC_HOST) $(CC_CFLAGS) $(CFLAGS) -c -o $@ $< + +$(OBJS_RC): %.o: $(SRC_DIR)/%.rc $(FILE_DEP) + $(E) '$(STAGE) Compiling resource $(<:$(SRC_DIR)/%.rc=%.rc)' + $(Q)$(WINDRES) -o $@ -I $(MEDIA_DIR) $< + +$(TTD): rev.o $(OBJS) $(CONFIG_CACHE_LINKER) + $(E) '$(STAGE) Linking $@' + $(Q)$(CXX_HOST) $(LDFLAGS) rev.o $(OBJS) $(LIBS) -o $@ && cp $@ $(BIN_DIR)/ +ifdef STRIP + $(Q)$(STRIP) $@ +endif + +# The targets to compile the endian-code + +endian_target.h: $(ENDIAN_CHECK) $(CONFIG_CACHE_ENDIAN) + $(E) '$(STAGE) Testing endianness for target' + $(Q)./$(ENDIAN_CHECK) $(ENDIAN_FORCE) > $@ + +$(ENDIAN_CHECK): $(SRC_DIR)/endian_check.c + $(E) '$(STAGE) Compiling and Linking $@' + $(Q)$(CC_BUILD) $(CFLAGS_BUILD) $< -o $@ + +# Revision files + +rev.c: $(CONFIG_CACHE_VERSION) +# setting the revision number in a place, there the binary can read it + @echo 'const char _openttd_revision[] = "$(REV)";' > rev.c +# Some additions for MorphOS versions tag +ifeq ($(OS),MORPHOS) + @echo '#ifdef __MORPHOS__' >> rev.c + @echo 'const char morphos_versions_tag[] = "\\0$$VER: OpenTTD $(REV) ('`date +%d.%m.%y`') (C) OpenTTD Team [MorphOS, PowerPC]";' >> rev.c + @echo '#endif' >> rev.c +endif + +rev.o: rev.c $(FILE_DEP) + $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.c=%.c)' + $(Q)$(CC_HOST) $(CC_CFLAGS) $(CFLAGS) -c -o $@ $< + +FORCE: + +depend: $(DEPS) + +clean: + $(E) '$(STAGE) Cleaning up object files' + $(Q)rm -f $(DEPS) $(OBJS) $(TTD) $(TTD:%=$(BIN_DIR)/%) $(CONFIG_CACHE_COMPILER) $(CONFIG_CACHE_LINKER) $(CONFIG_CACHE_ENDIAN) $(CONFIG_CACHE_SOURCE) $(ENDIAN_TARGETS) rev.o + +mrproper: clean + $(Q)rm -f rev.c + +%.o: + @echo '$(STAGE) No such source-file: $(@:%.o=%).[c|cpp|m|rc]' + +.PHONY: all mrproper depend clean FORCE diff --git a/ai/ai.c b/ai/ai.c deleted file mode 100644 --- a/ai/ai.c +++ /dev/null @@ -1,247 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "../variables.h" -#include "../command.h" -#include "../network/network.h" -#include "ai.h" -#include "default/default.h" - -/** - * Dequeues commands put in the queue via AI_PutCommandInQueue. - */ -static void AI_DequeueCommands(PlayerID player) -{ - AICommand *com, *entry_com; - - entry_com = _ai_player[player].queue; - - /* It happens that DoCommandP issues a new DoCommandAI which adds a new command - * to this very same queue (don't argue about this, if it currently doesn't - * happen I can tell you it will happen with AIScript -- TrueLight). If we - * do not make the queue NULL, that commands will be dequeued immediatly. - * Therefor we safe the entry-point to entry_com, and make the queue NULL, so - * the new queue can be safely built up. */ - _ai_player[player].queue = NULL; - _ai_player[player].queue_tail = NULL; - - /* Dequeue all commands */ - while ((com = entry_com) != NULL) { - _current_player = player; - - _cmd_text = com->text; - DoCommandP(com->tile, com->p1, com->p2, com->callback, com->procc); - - /* Free item */ - entry_com = com->next; - free(com->text); - free(com); - } -} - -/** - * Needed for SP; we need to delay DoCommand with 1 tick, because else events - * will make infinite loops (AIScript). - */ -static void AI_PutCommandInQueue(PlayerID player, TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCallback* callback) -{ - AICommand *com; - - if (_ai_player[player].queue_tail == NULL) { - /* There is no item in the queue yet, create the queue */ - _ai_player[player].queue = malloc(sizeof(AICommand)); - _ai_player[player].queue_tail = _ai_player[player].queue; - } else { - /* Add an item at the end */ - _ai_player[player].queue_tail->next = malloc(sizeof(AICommand)); - _ai_player[player].queue_tail = _ai_player[player].queue_tail->next; - } - - /* This is our new item */ - com = _ai_player[player].queue_tail; - - /* Assign the info */ - com->tile = tile; - com->p1 = p1; - com->p2 = p2; - com->procc = procc; - com->callback = callback; - com->next = NULL; - com->text = NULL; - - /* Copy the cmd_text, if needed */ - if (_cmd_text != NULL) { - com->text = strdup(_cmd_text); - _cmd_text = NULL; - } -} - -/** - * Executes a raw DoCommand for the AI. - */ -int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback) -{ - PlayerID old_lp; - int32 res = 0; - const char* tmp_cmdtext; - - /* If you enable DC_EXEC with DC_QUERY_COST you are a really strange - * person.. should we check for those funny jokes? - */ - - /* The test already resets _cmd_text, so backup the pointer */ - tmp_cmdtext = _cmd_text; - - /* First, do a test-run to see if we can do this */ - res = DoCommand(tile, p1, p2, flags & ~DC_EXEC, procc); - /* The command failed, or you didn't want to execute, or you are quering, return */ - if (CmdFailed(res) || !(flags & DC_EXEC) || (flags & DC_QUERY_COST)) { - return res; - } - - /* Restore _cmd_text */ - _cmd_text = tmp_cmdtext; - - /* If we did a DC_EXEC, and the command did not return an error, execute it - * over the network */ - if (flags & DC_AUTO) procc |= CMD_AUTO; - if (flags & DC_NO_WATER) procc |= CMD_NO_WATER; - - /* NetworkSend_Command needs _local_player to be set correctly, so - * adjust it, and put it back right after the function */ - old_lp = _local_player; - _local_player = _current_player; - -#ifdef ENABLE_NETWORK - /* Send the command */ - if (_networking) { - /* Network is easy, send it to his handler */ - NetworkSend_Command(tile, p1, p2, procc, callback); - } else { -#else - { -#endif - /* If we execute BuildCommands directly in SP, we have a big problem with events - * so we need to delay is for 1 tick */ - AI_PutCommandInQueue(_current_player, tile, p1, p2, procc, callback); - } - - /* Set _local_player back */ - _local_player = old_lp; - - return res; -} - - -int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc) -{ - return AI_DoCommandCc(tile, p1, p2, flags, procc, NULL); -} - - -/** - * Run 1 tick of the AI. Don't overdo it, keep it realistic. - */ -static void AI_RunTick(PlayerID player) -{ - extern void AiNewDoGameLoop(Player *p); - - Player *p = GetPlayer(player); - _current_player = player; - - if (_patches.ainew_active) { - AiNewDoGameLoop(p); - } else { - /* Enable all kind of cheats the old AI needs in order to operate correctly... */ - _is_old_ai_player = true; - AiDoGameLoop(p); - _is_old_ai_player = false; - } -} - - -/** - * The gameloop for AIs. - * Handles one tick for all the AIs. - */ -void AI_RunGameLoop(void) -{ - /* Don't do anything if ai is disabled */ - if (!_ai.enabled) return; - - /* Don't do anything if we are a network-client, or the AI has been disabled */ - if (_networking && (!_network_server || !_patches.ai_in_multiplayer)) return; - - /* New tick */ - _ai.tick++; - - /* Make sure the AI follows the difficulty rule.. */ - assert(_opt.diff.competitor_speed <= 4); - if ((_ai.tick & ((1 << (4 - _opt.diff.competitor_speed)) - 1)) != 0) return; - - /* Check for AI-client (so joining a network with an AI) */ - if (!_networking || _network_server) { - /* Check if we want to run AIs (server or SP only) */ - const Player* p; - - FOR_ALL_PLAYERS(p) { - if (p->is_active && p->is_ai) { - /* This should always be true, else something went wrong... */ - assert(_ai_player[p->index].active); - - /* Run the script */ - AI_DequeueCommands(p->index); - AI_RunTick(p->index); - } - } - } - - _current_player = OWNER_NONE; -} - -/** - * A new AI sees the day of light. You can do here what ever you think is needed. - */ -void AI_StartNewAI(PlayerID player) -{ - assert(IsValidPlayer(player)); - - /* Called if a new AI is booted */ - _ai_player[player].active = true; -} - -/** - * This AI player died. Give it some chance to make a final puf. - */ -void AI_PlayerDied(PlayerID player) -{ - /* Called if this AI died */ - _ai_player[player].active = false; -} - -/** - * Initialize some AI-related stuff. - */ -void AI_Initialize(void) -{ - /* First, make sure all AIs are DEAD! */ - AI_Uninitialize(); - - memset(&_ai, 0, sizeof(_ai)); - memset(&_ai_player, 0, sizeof(_ai_player)); - - _ai.enabled = true; -} - -/** - * Deinitializer for AI-related stuff. - */ -void AI_Uninitialize(void) -{ - const Player* p; - - FOR_ALL_PLAYERS(p) { - if (p->is_active && p->is_ai) AI_PlayerDied(p->index); - } -} diff --git a/ai/ai.h b/ai/ai.h deleted file mode 100644 --- a/ai/ai.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef AI_H -#define AI_H - -#include "../functions.h" -#include "../network/network.h" -#include "../player.h" -#include "../command.h" - -/* How DoCommands look like for an AI */ -typedef struct AICommand { - uint32 tile; - uint32 p1; - uint32 p2; - uint32 procc; - CommandCallback* callback; - - char *text; - uint uid; - - struct AICommand *next; -} AICommand; - -/* The struct for an AIScript Player */ -typedef struct AIPlayer { - bool active; ///< Is this AI active? - AICommand *queue; ///< The commands that he has in his queue - AICommand *queue_tail; ///< The tail of this queue -} AIPlayer; - -/* The struct to keep some data about the AI in general */ -typedef struct AIStruct { - /* General */ - bool enabled; ///< Is AI enabled? - uint tick; ///< The current tick (something like _frame_counter, only for AIs) -} AIStruct; - -VARDEF AIStruct _ai; -VARDEF AIPlayer _ai_player[MAX_PLAYERS]; - -// ai.c -void AI_StartNewAI(PlayerID player); -void AI_PlayerDied(PlayerID player); -void AI_RunGameLoop(void); -void AI_Initialize(void); -void AI_Uninitialize(void); -int32 AI_DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc); -int32 AI_DoCommandCc(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc, CommandCallback* callback); - -/** Is it allowed to start a new AI. - * This function checks some boundries to see if we should launch a new AI. - * @return True if we can start a new AI. - */ -static inline bool AI_AllowNewAI(void) -{ - /* If disabled, no AI */ - if (!_ai.enabled) - return false; - - /* If in network, but no server, no AI */ - if (_networking && !_network_server) - return false; - - /* If in network, and server, possible AI */ - if (_networking && _network_server) { - /* Do we want AIs in multiplayer? */ - if (!_patches.ai_in_multiplayer) - return false; - - /* Only the NewAI is allowed... sadly enough the old AI just doesn't support this - * system, because all commands are delayed by at least 1 tick, which causes - * a big problem, because it uses variables that are only set AFTER the command - * is really executed... */ - if (!_patches.ainew_active) - return false; - } - - return true; -} - -#define AI_CHANCE16(a,b) ((uint16) AI_Random() <= (uint16)((65536 * a) / b)) -#define AI_CHANCE16R(a,b,r) ((uint16)(r = AI_Random()) <= (uint16)((65536 * a) / b)) - -/** - * The random-function that should be used by ALL AIs. - */ -static inline uint AI_RandomRange(uint max) -{ - /* We pick RandomRange if we are in SP (so when saved, we do the same over and over) - * but we pick InteractiveRandomRange if we are a network_server or network-client. - */ - if (_networking) - return InteractiveRandomRange(max); - else - return RandomRange(max); -} - -/** - * The random-function that should be used by ALL AIs. - */ -static inline uint32 AI_Random(void) -{ -/* We pick RandomRange if we are in SP (so when saved, we do the same over and over) - * but we pick InteractiveRandomRange if we are a network_server or network-client. - */ - if (_networking) - return InteractiveRandom(); - else - return Random(); -} - -#endif /* AI_H */ diff --git a/ai/default/default.c b/ai/default/default.c deleted file mode 100644 --- a/ai/default/default.c +++ /dev/null @@ -1,3934 +0,0 @@ -/* $Id$ */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../aircraft.h" -#include "../../bridge_map.h" -#include "../../functions.h" -#include "../../map.h" -#include "../../rail_map.h" -#include "../../road_map.h" -#include "../../roadveh.h" -#include "../../station_map.h" -#include "../../tile.h" -#include "../../player.h" -#include "../../tunnel_map.h" -#include "../../vehicle.h" -#include "../../engine.h" -#include "../../command.h" -#include "../../town.h" -#include "../../industry.h" -#include "../../station.h" -#include "../../pathfind.h" -#include "../../economy.h" -#include "../../airport.h" -#include "../../depot.h" -#include "../../variables.h" -#include "../../bridge.h" -#include "../../date.h" -#include "default.h" - -// remove some day perhaps? -static uint _ai_service_interval; - -typedef void AiStateAction(Player *p); - -enum { - AIS_0 = 0, - AIS_1 = 1, - AIS_VEH_LOOP = 2, - AIS_VEH_CHECK_REPLACE_VEHICLE = 3, - AIS_VEH_DO_REPLACE_VEHICLE = 4, - AIS_WANT_NEW_ROUTE = 5, - AIS_BUILD_DEFAULT_RAIL_BLOCKS = 6, - AIS_BUILD_RAIL = 7, - AIS_BUILD_RAIL_VEH = 8, - AIS_DELETE_RAIL_BLOCKS = 9, - AIS_BUILD_DEFAULT_ROAD_BLOCKS = 10, - AIS_BUILD_ROAD = 11, - AIS_BUILD_ROAD_VEHICLES = 12, - AIS_DELETE_ROAD_BLOCKS = 13, - AIS_AIRPORT_STUFF = 14, - AIS_BUILD_DEFAULT_AIRPORT_BLOCKS = 15, - AIS_BUILD_AIRCRAFT_VEHICLES = 16, - AIS_CHECK_SHIP_STUFF = 17, - AIS_BUILD_DEFAULT_SHIP_BLOCKS = 18, - AIS_DO_SHIP_STUFF = 19, - AIS_SELL_VEHICLE = 20, - AIS_REMOVE_STATION = 21, - AIS_REMOVE_TRACK = 22, - AIS_REMOVE_SINGLE_RAIL_TILE = 23 -}; - - -#include "../../table/ai_rail.h" - -static byte GetRailTrackStatus(TileIndex tile) -{ - uint32 r = GetTileTrackStatus(tile, TRANSPORT_RAIL); - return (byte) (r | r >> 8); -} - - -static void AiCase0(Player *p) -{ - p->ai.state = AIS_REMOVE_TRACK; - p->ai.state_counter = 0; -} - -static void AiCase1(Player *p) -{ - p->ai.cur_veh = NULL; - p->ai.state = AIS_VEH_LOOP; -} - -static void AiStateVehLoop(Player *p) -{ - Vehicle *v; - uint index; - - index = (p->ai.cur_veh == NULL) ? 0 : p->ai.cur_veh->index + 1; - - FOR_ALL_VEHICLES_FROM(v, index) { - if (v->owner != _current_player) continue; - - if ((v->type == VEH_Train && v->subtype == 0) || - v->type == VEH_Road || - (v->type == VEH_Aircraft && v->subtype <= 2) || - v->type == VEH_Ship) { - /* replace engine? */ - if (v->type == VEH_Train && v->engine_type < 3 && - (_price.build_railvehicle >> 3) < p->player_money) { - p->ai.state = AIS_VEH_CHECK_REPLACE_VEHICLE; - p->ai.cur_veh = v; - return; - } - - /* not profitable? */ - if (v->age >= 730 && - v->profit_last_year < _price.station_value * 5 && - v->profit_this_year < _price.station_value * 5) { - p->ai.state_counter = 0; - p->ai.state = AIS_SELL_VEHICLE; - p->ai.cur_veh = v; - return; - } - - /* not reliable? */ - if (v->age >= v->max_age || ( - v->age != 0 && - GetEngine(v->engine_type)->reliability < 35389 - )) { - p->ai.state = AIS_VEH_CHECK_REPLACE_VEHICLE; - p->ai.cur_veh = v; - return; - } - } - } - - p->ai.state = AIS_WANT_NEW_ROUTE; - p->ai.state_counter = 0; -} - -static EngineID AiChooseTrainToBuild(RailType railtype, int32 money, byte flag, TileIndex tile) -{ - EngineID best_veh_index = INVALID_ENGINE; - byte best_veh_score = 0; - int32 ret; - EngineID i; - - for (i = 0; i < NUM_TRAIN_ENGINES; i++) { - const RailVehicleInfo *rvi = RailVehInfo(i); - const Engine* e = GetEngine(i); - - if (!IsCompatibleRail(e->railtype, railtype) || - rvi->flags & RVI_WAGON || - (rvi->flags & RVI_MULTIHEAD && flag & 1) || - !HASBIT(e->player_avail, _current_player) || - e->reliability < 0x8A3D) { - continue; - } - - ret = DoCommand(tile, i, 0, 0, CMD_BUILD_RAIL_VEHICLE); - if (!CmdFailed(ret) && ret <= money && rvi->ai_rank >= best_veh_score) { - best_veh_score = rvi->ai_rank; - best_veh_index = i; - } - } - - return best_veh_index; -} - -static EngineID AiChooseRoadVehToBuild(CargoID cargo, int32 money, TileIndex tile) -{ - EngineID best_veh_index = INVALID_ENGINE; - int32 best_veh_rating = 0; - EngineID i = ROAD_ENGINES_INDEX; - EngineID end = i + NUM_ROAD_ENGINES; - - for (; i != end; i++) { - const RoadVehicleInfo *rvi = RoadVehInfo(i); - const Engine* e = GetEngine(i); - int32 rating; - int32 ret; - - if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D) { - continue; - } - - /* Skip vehicles which can't take our cargo type */ - if (rvi->cargo_type != cargo && !CanRefitTo(i, cargo)) continue; - - /* Rate and compare the engine by speed & capacity */ - rating = rvi->max_speed * rvi->capacity; - if (rating <= best_veh_rating) continue; - - ret = DoCommand(tile, i, 0, 0, CMD_BUILD_ROAD_VEH); - if (CmdFailed(ret)) continue; - - /* Add the cost of refitting */ - if (rvi->cargo_type != cargo) ret += GetRefitCost(i); - if (ret > money) continue; - - best_veh_rating = rating; - best_veh_index = i; - } - - return best_veh_index; -} - -static EngineID AiChooseAircraftToBuild(int32 money, byte flag) -{ - EngineID best_veh_index = INVALID_ENGINE; - int32 best_veh_cost = 0; - EngineID i; - - for (i = AIRCRAFT_ENGINES_INDEX; i != AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; i++) { - const Engine* e = GetEngine(i); - int32 ret; - - if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D) { - continue; - } - - if ((AircraftVehInfo(i)->subtype & AIR_CTOL) != flag) continue; - - ret = DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT); - if (!CmdFailed(ret) && ret <= money && ret >= best_veh_cost) { - best_veh_cost = ret; - best_veh_index = i; - } - } - - return best_veh_index; -} - -static int32 AiGetBasePrice(const Player* p) -{ - int32 base = _price.station_value; - - // adjust base price when more expensive vehicles are available - switch (p->ai.railtype_to_use) { - default: NOT_REACHED(); - case RAILTYPE_RAIL: break; - case RAILTYPE_ELECTRIC: break; - case RAILTYPE_MONO: base = (base * 3) >> 1; break; - case RAILTYPE_MAGLEV: base *= 2; break; - } - - return base; -} - -#if 0 -static EngineID AiChooseShipToBuild(byte cargo, int32 money) -{ - // XXX: not done - return INVALID_ENGINE; -} -#endif - -static EngineID AiChooseRoadVehToReplaceWith(const Player* p, const Vehicle* v) -{ - int32 avail_money = p->player_money + v->value; - return AiChooseRoadVehToBuild(v->cargo_type, avail_money, v->tile); -} - -static EngineID AiChooseAircraftToReplaceWith(const Player* p, const Vehicle* v) -{ - int32 avail_money = p->player_money + v->value; - return AiChooseAircraftToBuild( - avail_money, AircraftVehInfo(v->engine_type)->subtype & AIR_CTOL - ); -} - -static EngineID AiChooseTrainToReplaceWith(const Player* p, const Vehicle* v) -{ - int32 avail_money = p->player_money + v->value; - const Vehicle* u = v; - int num = 0; - - while (++num, u->next != NULL) { - u = u->next; - } - - // XXX: check if a wagon - return AiChooseTrainToBuild(v->u.rail.railtype, avail_money, 0, v->tile); -} - -static EngineID AiChooseShipToReplaceWith(const Player* p, const Vehicle* v) -{ - error("!AiChooseShipToReplaceWith"); - - /* maybe useless, but avoids compiler warning this way */ - return INVALID_ENGINE; -} - -static void AiHandleGotoDepot(Player *p, int cmd) -{ - if (p->ai.cur_veh->current_order.type != OT_GOTO_DEPOT) - DoCommand(0, p->ai.cur_veh->index, 0, DC_EXEC, cmd); - - if (++p->ai.state_counter <= 1387) { - p->ai.state = AIS_VEH_DO_REPLACE_VEHICLE; - return; - } - - if (p->ai.cur_veh->current_order.type == OT_GOTO_DEPOT) { - p->ai.cur_veh->current_order.type = OT_DUMMY; - p->ai.cur_veh->current_order.flags = 0; - InvalidateWindow(WC_VEHICLE_VIEW, p->ai.cur_veh->index); - } -} - -static void AiRestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak) -{ - uint i; - - for (i = 0; bak->order[i].type != OT_NOTHING; i++) { - if (!DoCommandP(0, v->index + (i << 16), PackOrder(&bak->order[i]), NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK)) - break; - } -} - -static void AiHandleReplaceTrain(Player *p) -{ - const Vehicle* v = p->ai.cur_veh; - BackuppedOrders orderbak[1]; - EngineID veh; - - // wait until the vehicle reaches the depot. - if (!IsTileDepotType(v->tile, TRANSPORT_RAIL) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) { - AiHandleGotoDepot(p, CMD_SEND_TRAIN_TO_DEPOT); - return; - } - - veh = AiChooseTrainToReplaceWith(p, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, orderbak); - tile = v->tile; - - if (!CmdFailed(DoCommand(0, v->index, 2, DC_EXEC, CMD_SELL_RAIL_WAGON)) && - !CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE))) { - VehicleID veh = _new_vehicle_id; - AiRestoreVehicleOrders(GetVehicle(veh), orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_TRAIN); - - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceRoadVeh(Player *p) -{ - const Vehicle* v = p->ai.cur_veh; - BackuppedOrders orderbak[1]; - EngineID veh; - - if (!IsRoadVehInDepotStopped(v)) { - AiHandleGotoDepot(p, CMD_SEND_ROADVEH_TO_DEPOT); - return; - } - - veh = AiChooseRoadVehToReplaceWith(p, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, orderbak); - tile = v->tile; - - if (!CmdFailed(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH)) && - !CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) { - VehicleID veh = _new_vehicle_id; - - AiRestoreVehicleOrders(GetVehicle(veh), orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_ROADVEH); - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceAircraft(Player *p) -{ - const Vehicle* v = p->ai.cur_veh; - BackuppedOrders orderbak[1]; - EngineID veh; - - if (!IsAircraftInHangarStopped(v)) { - AiHandleGotoDepot(p, CMD_SEND_AIRCRAFT_TO_HANGAR); - return; - } - - veh = AiChooseAircraftToReplaceWith(p, v); - if (veh != INVALID_ENGINE) { - TileIndex tile; - - BackupVehicleOrders(v, orderbak); - tile = v->tile; - - if (!CmdFailed(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT)) && - !CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) { - VehicleID veh = _new_vehicle_id; - AiRestoreVehicleOrders(GetVehicle(veh), orderbak); - DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT); - - DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - } - } -} - -static void AiHandleReplaceShip(Player *p) -{ - error("!AiHandleReplaceShip"); -} - -typedef EngineID CheckReplaceProc(const Player* p, const Vehicle* v); - -static CheckReplaceProc* const _veh_check_replace_proc[] = { - AiChooseTrainToReplaceWith, - AiChooseRoadVehToReplaceWith, - AiChooseShipToReplaceWith, - AiChooseAircraftToReplaceWith, -}; - -typedef void DoReplaceProc(Player *p); -static DoReplaceProc* const _veh_do_replace_proc[] = { - AiHandleReplaceTrain, - AiHandleReplaceRoadVeh, - AiHandleReplaceShip, - AiHandleReplaceAircraft -}; - -static void AiStateCheckReplaceVehicle(Player *p) -{ - const Vehicle* v = p->ai.cur_veh; - - if (!IsValidVehicle(v) || - v->owner != _current_player || - v->type > VEH_Ship || - _veh_check_replace_proc[v->type - VEH_Train](p, v) == INVALID_ENGINE) { - p->ai.state = AIS_VEH_LOOP; - } else { - p->ai.state_counter = 0; - p->ai.state = AIS_VEH_DO_REPLACE_VEHICLE; - } -} - -static void AiStateDoReplaceVehicle(Player *p) -{ - const Vehicle* v = p->ai.cur_veh; - - p->ai.state = AIS_VEH_LOOP; - // vehicle is not owned by the player anymore, something went very wrong. - if (!IsValidVehicle(v) || v->owner != _current_player) return; - _veh_do_replace_proc[v->type - VEH_Train](p); -} - -typedef struct FoundRoute { - int distance; - CargoID cargo; - void *from; - void *to; -} FoundRoute; - -static Town *AiFindRandomTown(void) -{ - return GetRandomTown(); -} - -static Industry *AiFindRandomIndustry(void) -{ - return GetRandomIndustry(); -} - -static void AiFindSubsidyIndustryRoute(FoundRoute *fr) -{ - uint i; - CargoID cargo; - const Subsidy* s; - Industry* from; - TileIndex to_xy; - - // initially error - fr->distance = -1; - - // Randomize subsidy index.. - i = RandomRange(lengthof(_subsidies) * 3); - if (i >= lengthof(_subsidies)) return; - - s = &_subsidies[i]; - - // Don't want passengers or mail - cargo = s->cargo_type; - if (cargo == CT_INVALID || - cargo == CT_PASSENGERS || - cargo == CT_MAIL || - s->age > 7) { - return; - } - fr->cargo = cargo; - - fr->from = from = GetIndustry(s->from); - - if (cargo == CT_GOODS || cargo == CT_FOOD) { - Town* to_tow = GetTown(s->to); - - if (to_tow->population < (cargo == CT_FOOD ? 200U : 900U)) return; // error - fr->to = to_tow; - to_xy = to_tow->xy; - } else { - Industry* to_ind = GetIndustry(s->to); - - fr->to = to_ind; - to_xy = to_ind->xy; - } - - fr->distance = DistanceManhattan(from->xy, to_xy); -} - -static void AiFindSubsidyPassengerRoute(FoundRoute *fr) -{ - uint i; - const Subsidy* s; - Town *from,*to; - - // initially error - fr->distance = -1; - - // Randomize subsidy index.. - i = RandomRange(lengthof(_subsidies) * 3); - if (i >= lengthof(_subsidies)) return; - - s = &_subsidies[i]; - - // Only want passengers - if (s->cargo_type != CT_PASSENGERS || s->age > 7) return; - fr->cargo = s->cargo_type; - - fr->from = from = GetTown(s->from); - fr->to = to = GetTown(s->to); - - // They must be big enough - if (from->population < 400 || to->population < 400) return; - - fr->distance = DistanceManhattan(from->xy, to->xy); -} - -static void AiFindRandomIndustryRoute(FoundRoute *fr) -{ - Industry* i; - uint32 r; - CargoID cargo; - - // initially error - fr->distance = -1; - - r = Random(); - - // pick a source - fr->from = i = AiFindRandomIndustry(); - if (i == NULL) return; - - // pick a random produced cargo - cargo = i->produced_cargo[0]; - if (r & 1 && i->produced_cargo[1] != CT_INVALID) cargo = i->produced_cargo[1]; - - fr->cargo = cargo; - - // don't allow passengers - if (cargo == CT_INVALID || cargo == CT_PASSENGERS) return; - - if (cargo != CT_GOODS && cargo != CT_FOOD) { - // pick a dest, and see if it can receive - Industry* i2 = AiFindRandomIndustry(); - - if (i2 == NULL || i == i2 || ( - i2->accepts_cargo[0] != cargo && - i2->accepts_cargo[1] != cargo && - i2->accepts_cargo[2] != cargo) - ) { - return; - } - - fr->to = i2; - fr->distance = DistanceManhattan(i->xy, i2->xy); - } else { - // pick a dest town, and see if it's big enough - Town* t = AiFindRandomTown(); - - if (t == NULL || t->population < (cargo == CT_FOOD ? 200U : 900U)) return; - - fr->to = t; - fr->distance = DistanceManhattan(i->xy, t->xy); - } -} - -static void AiFindRandomPassengerRoute(FoundRoute *fr) -{ - Town* source; - Town* dest; - - // initially error - fr->distance = -1; - - fr->from = source = AiFindRandomTown(); - if (source == NULL || source->population < 400) return; - - fr->to = dest = AiFindRandomTown(); - if (dest == NULL || source == dest || dest->population < 400) return; - - fr->distance = DistanceManhattan(source->xy, dest->xy); -} - -// Warn: depends on 'xy' being the first element in both Town and Industry -#define GET_TOWN_OR_INDUSTRY_TILE(p) (((Town*)(p))->xy) - -static bool AiCheckIfRouteIsGood(Player *p, FoundRoute *fr, byte bitmask) -{ - TileIndex from_tile, to_tile; - Station *st; - int dist; - uint same_station = 0; - - // Make sure distance to closest station is < 37 pixels. - from_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->from); - to_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->to); - - dist = 0xFFFF; - FOR_ALL_STATIONS(st) { - int cur; - - if (st->owner != _current_player) continue; - cur = DistanceMax(from_tile, st->xy); - if (cur < dist) dist = cur; - cur = DistanceMax(to_tile, st->xy); - if (cur < dist) dist = cur; - if (to_tile == from_tile && st->xy == to_tile) same_station++; - } - - // To prevent the AI from building ten busstations in the same town, do some calculations - // For each road or airport station, we want 350 of population! - if ((bitmask == 2 || bitmask == 4) && - same_station > 2 && - ((Town*)fr->from)->population < same_station * 350) { - return false; - } - - if (dist != 0xFFFF && dist > 37) return false; - - if (p->ai.route_type_mask != 0 && - !(p->ai.route_type_mask & bitmask) && - !CHANCE16(1, 5)) { - return false; - } - - if (fr->cargo == CT_PASSENGERS || fr->cargo == CT_MAIL) { - const Town* from = fr->from; - const Town* to = fr->to; - - if (from->pct_pass_transported > 0x99 || - to->pct_pass_transported > 0x99) { - return false; - } - - // Make sure it has a reasonably good rating - if (from->ratings[_current_player] < -100 || - to->ratings[_current_player] < -100) { - return false; - } - } else { - const Industry* i = (const Industry*)fr->from; - - if (i->pct_transported[fr->cargo != i->produced_cargo[0]] > 0x99 || - i->total_production[fr->cargo != i->produced_cargo[0]] == 0) { - return false; - } - } - - p->ai.route_type_mask |= bitmask; - return true; -} - -static byte AiGetDirectionBetweenTiles(TileIndex a, TileIndex b) -{ - byte i = (TileX(a) < TileX(b)) ? 1 : 0; - if (TileY(a) >= TileY(b)) i ^= 3; - return i; -} - -static TileIndex AiGetPctTileBetween(TileIndex a, TileIndex b, byte pct) -{ - return TileXY( - TileX(a) + ((TileX(b) - TileX(a)) * pct >> 8), - TileY(a) + ((TileY(b) - TileY(a)) * pct >> 8) - ); -} - -static void AiWantLongIndustryRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 60, 90 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 60, 90 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(p, &fr, 1)) return; - - // Fill the source field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 9; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.unk6 = 1; - p->ai.src.unk7 = 0; - p->ai.src.buildcmd_a = 0x24; - p->ai.src.buildcmd_b = 0xFF; - p->ai.src.direction = AiGetDirectionBetweenTiles( - p->ai.src.spec_tile, - p->ai.dst.spec_tile - ); - p->ai.src.cargo = fr.cargo | 0x80; - - // Fill the dest field - - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 9; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.unk6 = 1; - p->ai.dst.unk7 = 0; - p->ai.dst.buildcmd_a = 0x34; - p->ai.dst.buildcmd_b = 0xFF; - p->ai.dst.direction = AiGetDirectionBetweenTiles( - p->ai.dst.spec_tile, - p->ai.src.spec_tile - ); - p->ai.dst.cargo = fr.cargo; - - // Fill middle field 1 - p->ai.mid1.spec_tile = AiGetPctTileBetween( - p->ai.src.spec_tile, - p->ai.dst.spec_tile, - 0x55 - ); - p->ai.mid1.use_tile = 0; - p->ai.mid1.rand_rng = 6; - p->ai.mid1.cur_building_rule = 0xFF; - p->ai.mid1.unk6 = 2; - p->ai.mid1.unk7 = 1; - p->ai.mid1.buildcmd_a = 0x30; - p->ai.mid1.buildcmd_b = 0xFF; - p->ai.mid1.direction = p->ai.src.direction; - p->ai.mid1.cargo = fr.cargo; - - // Fill middle field 2 - p->ai.mid2.spec_tile = AiGetPctTileBetween( - p->ai.src.spec_tile, - p->ai.dst.spec_tile, - 0xAA - ); - p->ai.mid2.use_tile = 0; - p->ai.mid2.rand_rng = 6; - p->ai.mid2.cur_building_rule = 0xFF; - p->ai.mid2.unk6 = 2; - p->ai.mid2.unk7 = 1; - p->ai.mid2.buildcmd_a = 0xFF; - p->ai.mid2.buildcmd_b = 0xFF; - p->ai.mid2.direction = p->ai.dst.direction; - p->ai.mid2.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_wagons = 3; - p->ai.build_kind = 2; - p->ai.num_build_rec = 4; - p->ai.num_loco_to_build = 2; - p->ai.num_want_fullload = 2; - p->ai.wagon_list[0] = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantMediumIndustryRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 40, 60 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 40, 60 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(p, &fr, 1)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 9; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.unk6 = 1; - p->ai.src.unk7 = 0; - p->ai.src.buildcmd_a = 0x10; - p->ai.src.buildcmd_b = 0xFF; - p->ai.src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - p->ai.src.cargo = fr.cargo | 0x80; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 9; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.unk6 = 1; - p->ai.dst.unk7 = 0; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.buildcmd_b = 0xFF; - p->ai.dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_wagons = 3; - p->ai.build_kind = 1; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 1; - p->ai.num_want_fullload = 1; - p->ai.wagon_list[0] = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantShortIndustryRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(p, &fr, 1)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 9; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.unk6 = 1; - p->ai.src.unk7 = 0; - p->ai.src.buildcmd_a = 0x10; - p->ai.src.buildcmd_b = 0xFF; - p->ai.src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - p->ai.src.cargo = fr.cargo | 0x80; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 9; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.unk6 = 1; - p->ai.dst.unk7 = 0; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.buildcmd_b = 0xFF; - p->ai.dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_wagons = 2; - p->ai.build_kind = 1; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 1; - p->ai.num_want_fullload = 1; - p->ai.wagon_list[0] = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantMailRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 60, 110 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 60, 110 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_MAIL; - if (!AiCheckIfRouteIsGood(p, &fr, 1)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 7; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.unk6 = 1; - p->ai.src.unk7 = 0; - p->ai.src.buildcmd_a = 0x24; - p->ai.src.buildcmd_b = 0xFF; - p->ai.src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - p->ai.src.cargo = fr.cargo; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 7; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.unk6 = 1; - p->ai.dst.unk7 = 0; - p->ai.dst.buildcmd_a = 0x34; - p->ai.dst.buildcmd_b = 0xFF; - p->ai.dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - p->ai.dst.cargo = fr.cargo; - - // Fill middle field 1 - p->ai.mid1.spec_tile = AiGetPctTileBetween( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - 0x55 - ); - p->ai.mid1.use_tile = 0; - p->ai.mid1.rand_rng = 6; - p->ai.mid1.cur_building_rule = 0xFF; - p->ai.mid1.unk6 = 2; - p->ai.mid1.unk7 = 1; - p->ai.mid1.buildcmd_a = 0x30; - p->ai.mid1.buildcmd_b = 0xFF; - p->ai.mid1.direction = p->ai.src.direction; - p->ai.mid1.cargo = fr.cargo; - - // Fill middle field 2 - p->ai.mid2.spec_tile = AiGetPctTileBetween( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - 0xAA - ); - p->ai.mid2.use_tile = 0; - p->ai.mid2.rand_rng = 6; - p->ai.mid2.cur_building_rule = 0xFF; - p->ai.mid2.unk6 = 2; - p->ai.mid2.unk7 = 1; - p->ai.mid2.buildcmd_a = 0xFF; - p->ai.mid2.buildcmd_b = 0xFF; - p->ai.mid2.direction = p->ai.dst.direction; - p->ai.mid2.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_wagons = 3; - p->ai.build_kind = 2; - p->ai.num_build_rec = 4; - p->ai.num_loco_to_build = 2; - p->ai.num_want_fullload = 0; - p->ai.wagon_list[0] = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantPassengerRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 0, 55 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 0, 55 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - if (!AiCheckIfRouteIsGood(p, &fr, 1)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 7; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.unk6 = 1; - p->ai.src.unk7 = 0; - p->ai.src.buildcmd_a = 0x10; - p->ai.src.buildcmd_b = 0xFF; - p->ai.src.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.from), - GET_TOWN_OR_INDUSTRY_TILE(fr.to) - ); - p->ai.src.cargo = fr.cargo; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 7; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.unk6 = 1; - p->ai.dst.unk7 = 0; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.buildcmd_b = 0xFF; - p->ai.dst.direction = AiGetDirectionBetweenTiles( - GET_TOWN_OR_INDUSTRY_TILE(fr.to), - GET_TOWN_OR_INDUSTRY_TILE(fr.from) - ); - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_wagons = 2; - p->ai.build_kind = 1; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 1; - p->ai.num_want_fullload = 0; - p->ai.wagon_list[0] = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - p->ai.state = AIS_BUILD_DEFAULT_RAIL_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantTrainRoute(Player *p) -{ - uint16 r = GB(Random(), 0, 16); - - p->ai.railtype_to_use = GetBestRailtype(p); - - if (r > 0xD000) { - AiWantLongIndustryRoute(p); - } else if (r > 0x6000) { - AiWantMediumIndustryRoute(p); - } else if (r > 0x1000) { - AiWantShortIndustryRoute(p); - } else if (r > 0x800) { - AiWantPassengerRoute(p); - } else { - AiWantMailRoute(p); - } -} - -static void AiWantLongRoadIndustryRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 35, 55 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 35, 55 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(p, &fr, 2)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 9; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.buildcmd_a = 1; - p->ai.src.direction = 0; - p->ai.src.cargo = fr.cargo | 0x80; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 9; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.direction = 0; - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 5; - p->ai.num_want_fullload = 5; - -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantMediumRoadIndustryRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break; - - // try a random one - AiFindRandomIndustryRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 15, 40 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - if (!AiCheckIfRouteIsGood(p, &fr, 2)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 9; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.buildcmd_a = 1; - p->ai.src.direction = 0; - p->ai.src.cargo = fr.cargo | 0x80; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 9; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.direction = 0; - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 3; - p->ai.num_want_fullload = 3; - -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantLongRoadPassengerRoute(Player *p) -{ - int i; - FoundRoute fr; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 55, 180 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 55, 180 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - - if (!AiCheckIfRouteIsGood(p, &fr, 2)) return; - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 10; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.buildcmd_a = 1; - p->ai.src.direction = 0; - p->ai.src.cargo = CT_PASSENGERS; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 10; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.direction = 0; - p->ai.dst.cargo = CT_PASSENGERS; - - // Fill common fields - p->ai.cargo_type = CT_PASSENGERS; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 4; - p->ai.num_want_fullload = 0; - -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantPassengerRouteInsideTown(Player *p) -{ - int i; - FoundRoute fr; - Town *t; - - i = 60; - for (;;) { - // Find a town big enough - t = AiFindRandomTown(); - if (t != NULL && t->population >= 700) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - fr.from = fr.to = t; - - if (!AiCheckIfRouteIsGood(p, &fr, 2)) return; - - // Fill the source field - p->ai.src.spec_tile = t->xy; - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 10; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.buildcmd_a = 1; - p->ai.src.direction = 0; - p->ai.src.cargo = CT_PASSENGERS; - - // Fill the dest field - p->ai.dst.spec_tile = t->xy; - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 10; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.buildcmd_a = 0xFF; - p->ai.dst.direction = 0; - p->ai.dst.cargo = CT_PASSENGERS; - - // Fill common fields - p->ai.cargo_type = CT_PASSENGERS; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 2; - p->ai.num_want_fullload = 0; - -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_BUILD_DEFAULT_ROAD_BLOCKS; - p->ai.state_mode = -1; - p->ai.state_counter = 0; - p->ai.timeout_counter = 0; -} - -static void AiWantRoadRoute(Player *p) -{ - uint16 r = GB(Random(), 0, 16); - - if (r > 0x4000) { - AiWantLongRoadIndustryRoute(p); - } else if (r > 0x2000) { - AiWantMediumRoadIndustryRoute(p); - } else if (r > 0x1000) { - AiWantLongRoadPassengerRoute(p); - } else { - AiWantPassengerRouteInsideTown(p); - } -} - -static void AiWantPassengerAircraftRoute(Player *p) -{ - FoundRoute fr; - int i; - - i = 60; - for (;;) { - // look for one from the subsidy list - AiFindSubsidyPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 0, 95 + 1)) break; - - // try a random one - AiFindRandomPassengerRoute(&fr); - if (IS_INT_INSIDE(fr.distance, 0, 95 + 1)) break; - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - if (!AiCheckIfRouteIsGood(p, &fr, 4)) return; - - - // Fill the source field - p->ai.src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to); - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 12; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.cargo = fr.cargo; - - // Fill the dest field - p->ai.dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from); - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 12; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.cargo = fr.cargo; - - // Fill common fields - p->ai.cargo_type = fr.cargo; - p->ai.build_kind = 0; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 1; - p->ai.num_want_fullload = 1; -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_AIRPORT_STUFF; - p->ai.timeout_counter = 0; -} - -static void AiWantOilRigAircraftRoute(Player *p) -{ - int i; - FoundRoute fr; - Town *t; - Industry *in; - - i = 60; - for (;;) { - // Find a town - t = AiFindRandomTown(); - if (t != NULL) { - // Find a random oil rig industry - in = AiFindRandomIndustry(); - if (in != NULL && in->type == IT_OIL_RIG) { - if (DistanceManhattan(t->xy, in->xy) < 60) - break; - } - } - - // only test 60 times - if (--i == 0) return; - } - - fr.cargo = CT_PASSENGERS; - fr.from = fr.to = t; - - if (!AiCheckIfRouteIsGood(p, &fr, 4)) return; - - // Fill the source field - p->ai.src.spec_tile = t->xy; - p->ai.src.use_tile = 0; - p->ai.src.rand_rng = 12; - p->ai.src.cur_building_rule = 0xFF; - p->ai.src.cargo = CT_PASSENGERS; - - // Fill the dest field - p->ai.dst.spec_tile = in->xy; - p->ai.dst.use_tile = 0; - p->ai.dst.rand_rng = 5; - p->ai.dst.cur_building_rule = 0xFF; - p->ai.dst.cargo = CT_PASSENGERS; - - // Fill common fields - p->ai.cargo_type = CT_PASSENGERS; - p->ai.build_kind = 1; - p->ai.num_build_rec = 2; - p->ai.num_loco_to_build = 1; - p->ai.num_want_fullload = 0; -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.order_list_blocks[0] = 0; - p->ai.order_list_blocks[1] = 1; - p->ai.order_list_blocks[2] = 255; - - p->ai.state = AIS_AIRPORT_STUFF; - p->ai.timeout_counter = 0; -} - -static void AiWantAircraftRoute(Player *p) -{ - uint16 r = (uint16)Random(); - - if (r >= 0x2AAA || _date < 0x3912 + DAYS_TILL_ORIGINAL_BASE_YEAR) { - AiWantPassengerAircraftRoute(p); - } else { - AiWantOilRigAircraftRoute(p); - } -} - -static void AiWantShipRoute(Player *p) -{ - // XXX -// error("AiWaitShipRoute"); -} - - - -static void AiStateWantNewRoute(Player *p) -{ - uint16 r; - int i; - - if (p->player_money < AiGetBasePrice(p) * 500) { - p->ai.state = AIS_0; - return; - } - - i = 200; - for (;;) { - r = (uint16)Random(); - - if (_patches.ai_disable_veh_train && - _patches.ai_disable_veh_roadveh && - _patches.ai_disable_veh_aircraft && - _patches.ai_disable_veh_ship) { - return; - } - - if (r < 0x7626) { - if (_patches.ai_disable_veh_train) continue; - AiWantTrainRoute(p); - } else if (r < 0xC4EA) { - if (_patches.ai_disable_veh_roadveh) continue; - AiWantRoadRoute(p); - } else if (r < 0xD89B) { - if (_patches.ai_disable_veh_aircraft) continue; - AiWantAircraftRoute(p); - } else { - if (_patches.ai_disable_veh_ship) continue; - AiWantShipRoute(p); - } - - // got a route? - if (p->ai.state != AIS_WANT_NEW_ROUTE) break; - - // time out? - if (--i == 0) { - if (++p->ai.state_counter == 556) p->ai.state = AIS_0; - break; - } - } -} - -static bool AiCheckTrackResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint rad = (_patches.modified_catchment) ? CA_TRAIN : 4; - - for (; p->mode != 4; p++) { - AcceptedCargo values; - TileIndex tile2; - uint w; - uint h; - - if (p->mode != 1) continue; - - tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - w = GB(p->attr, 1, 3); - h = GB(p->attr, 4, 3); - - if (p->attr & 1) uintswap(w, h); - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, w, h, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, w, h, rad); - if (!(values[cargo] & ~7)) - return false; - if (cargo != CT_MAIL) - return true; - return !!((values[cargo]>>1) & ~7); - } - } - - return true; -} - -static int32 AiDoBuildDefaultRailTrack(TileIndex tile, const AiDefaultBlockData* p, RailType railtype, byte flag) -{ - int32 ret; - int32 total_cost = 0; - Town *t = NULL; - int rating = 0; - int i,j,k; - - for (;;) { - // This will seldomly overflow for valid reasons. Mask it to be on the safe side. - uint c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)); - - _cleared_town = NULL; - - if (p->mode < 2) { - if (p->mode == 0) { - // Depot - ret = DoCommand(c, railtype, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT); - } else { - // Station - ret = DoCommand(c, (p->attr&1) | (p->attr>>4)<<8 | (p->attr>>1&7)<<16, railtype, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION); - } - - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - -clear_town_stuff:; - if (_cleared_town != NULL) { - if (t != NULL && t != _cleared_town) - return CMD_ERROR; - t = _cleared_town; - rating += _cleared_town_rating; - } - } else if (p->mode == 2) { - // Rail - if (IsTileType(c, MP_RAILWAY)) return CMD_ERROR; - - j = p->attr; - k = 0; - - // Build the rail - for (i = 0; i != 6; i++, j >>= 1) { - if (j&1) { - k = i; - ret = DoCommand(c, railtype, i, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - } - } - - /* signals too? */ - if (j & 3) { - // Can't build signals on a road. - if (IsTileType(c, MP_STREET)) return CMD_ERROR; - - if (flag & DC_EXEC) { - j = 4 - j; - do { - ret = DoCommand(c, k, 0, flag, CMD_BUILD_SIGNALS); - } while (--j); - } else { - ret = _price.build_signals; - } - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - } - } else if (p->mode == 3) { - //Clear stuff and then build single rail. - if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR; - ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret + _price.build_rail; - - if (flag & DC_EXEC) { - DoCommand(c, railtype, p->attr&1, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_SINGLE_RAIL); - } - - goto clear_town_stuff; - } else { - // Unk - break; - } - - p++; - } - - if (!(flag & DC_EXEC)) { - if (t != NULL && rating > t->ratings[_current_player]) { - return CMD_ERROR; - } - } - - return total_cost; -} - -// Returns rule and cost -static int AiBuildDefaultRailTrack(TileIndex tile, byte p0, byte p1, byte p2, byte p3, byte dir, byte cargo, RailType railtype, int32* cost) -{ - int i; - const AiDefaultRailBlock *p; - - for (i = 0; (p = _default_rail_track_data[i]) != NULL; i++) { - if (p->p0 == p0 && p->p1 == p1 && p->p2 == p2 && p->p3 == p3 && - (p->dir == 0xFF || p->dir == dir || ((p->dir - 1) & 3) == dir)) { - *cost = AiDoBuildDefaultRailTrack(tile, p->data, railtype, DC_NO_TOWN_RATING); - if (!CmdFailed(*cost) && AiCheckTrackResources(tile, p->data, cargo)) - return i; - } - } - - return -1; -} - -static const byte _terraform_up_flags[] = { - 14, 13, 12, 11, - 10, 9, 8, 7, - 6, 5, 4, 3, - 2, 1, 0, 1, - 2, 1, 4, 1, - 2, 1, 8, 1, - 2, 1, 4, 2, - 2, 1 -}; - -static const byte _terraform_down_flags[] = { - 1, 2, 3, 4, - 5, 6, 1, 8, - 9, 10, 8, 12, - 4, 2, 0, 0, - 1, 2, 3, 4, - 5, 6, 2, 8, - 9, 10, 1, 12, - 8, 4 -}; - -static void AiDoTerraformLand(TileIndex tile, int dir, int unk, int mode) -{ - PlayerID old_player; - uint32 r; - Slope slope; - uint h; - - old_player = _current_player; - _current_player = OWNER_NONE; - - r = Random(); - - unk &= (int)r; - - do { - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - r >>= 2; - if (r & 2) { - dir++; - if (r & 1) dir -= 2; - } - dir &= 3; - } while (--unk >= 0); - - slope = GetTileSlope(tile, &h); - - if (slope != SLOPE_FLAT) { - if (mode > 0 || (mode == 0 && !(r & 0xC))) { - // Terraform up - DoCommand(tile, _terraform_up_flags[slope - 1], 1, - DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND); - } else if (h != 0) { - // Terraform down - DoCommand(tile, _terraform_down_flags[slope - 1], 0, - DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND); - } - } - - _current_player = old_player; -} - -static void AiStateBuildDefaultRailBlocks(Player *p) -{ - uint i; - int j; - AiBuildRec *aib; - int rule; - int32 cost; - - // time out? - if (++p->ai.timeout_counter == 1388) { - p->ai.state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // do the following 8 times - for (i = 0; i < 8; i++) { - // check if we can build the default track - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the track can be build there. - rule = AiBuildDefaultRailTrack(aib->use_tile, - p->ai.build_kind, p->ai.num_wagons, - aib->unk6, aib->unk7, - aib->direction, aib->cargo, - p->ai.railtype_to_use, - &cost - ); - - if (rule == -1) { - // cannot build, terraform after a while - if (p->ai.state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode); - } - // also try the other terraform direction - if (++p->ai.state_counter >= 1000) { - p->ai.state_counter = 0; - p->ai.state_mode = -p->ai.state_mode; - } - } else if (CheckPlayerHasMoney(cost)) { - int32 r; - // player has money, build it. - aib->cur_building_rule = rule; - - r = AiDoBuildDefaultRailTrack( - aib->use_tile, - _default_rail_track_data[rule]->data, - p->ai.railtype_to_use, - DC_EXEC | DC_NO_TOWN_RATING - ); - assert(!CmdFailed(r)); - } - } while (++aib,--j); - } - - // check if we're done with all of them - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib,--j); - - // yep, all are done. switch state to the rail building state. - p->ai.state = AIS_BUILD_RAIL; - p->ai.state_mode = 255; -} - -static TileIndex AiGetEdgeOfDefaultRailBlock(byte rule, TileIndex tile, byte cmd, int *dir) -{ - const AiDefaultBlockData *p = _default_rail_track_data[rule]->data; - - while (p->mode != 3 || !((--cmd) & 0x80)) p++; - - return tile + ToTileIndexDiff(p->tileoffs) - TileOffsByDiagDir(*dir = p->attr); -} - -typedef struct AiRailPathFindData { - TileIndex tile; - TileIndex tile2; - int count; - bool flag; -} AiRailPathFindData; - -static bool AiEnumFollowTrack(TileIndex tile, AiRailPathFindData *a, int track, uint length, byte *state) -{ - if (a->flag) return true; - - if (length > 20 || tile == a->tile) { - a->flag = true; - return true; - } - - if (DistanceMax(tile, a->tile2) < 4) a->count++; - - return false; -} - -static bool AiDoFollowTrack(const Player* p) -{ - AiRailPathFindData arpfd; - - arpfd.tile = p->ai.start_tile_a; - arpfd.tile2 = p->ai.cur_tile_a; - arpfd.flag = false; - arpfd.count = 0; - FollowTrack(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a), 0x2000 | TRANSPORT_RAIL, p->ai.cur_dir_a^2, - (TPFEnumProc*)AiEnumFollowTrack, NULL, &arpfd); - return arpfd.count > 8; -} - -typedef struct AiRailFinder { - TileIndex final_tile; - byte final_dir; - byte depth; - byte recursive_mode; - byte cur_best_dir; - byte best_dir; - byte cur_best_depth; - byte best_depth; - uint cur_best_dist; - const byte *best_ptr; - uint best_dist; - TileIndex cur_best_tile, best_tile; - TileIndex bridge_end_tile; - Player *player; -} AiRailFinder; - -static const byte _ai_table_15[4][8] = { - {0, 0, 4, 3, 3, 1, 128 + 0, 64}, - {1, 1, 2, 0, 4, 2, 128 + 1, 65}, - {0, 2, 2, 3, 5, 1, 128 + 2, 66}, - {1, 3, 5, 0, 3, 2, 128 + 3, 67} -}; - -static const byte _dir_table_1[] = { 3, 9, 12, 6}; -static const byte _dir_table_2[] = {12, 6, 3, 9}; - - -static bool AiIsTileBanned(const Player* p, TileIndex tile, byte val) -{ - int i; - - for (i = 0; i != p->ai.banned_tile_count; i++) { - if (p->ai.banned_tiles[i] == tile && p->ai.banned_val[i] == val) { - return true; - } - } - return false; -} - -static void AiBanTile(Player* p, TileIndex tile, byte val) -{ - uint i; - - for (i = lengthof(p->ai.banned_tiles) - 1; i != 0; i--) { - p->ai.banned_tiles[i] = p->ai.banned_tiles[i - 1]; - p->ai.banned_val[i] = p->ai.banned_val[i - 1]; - } - - p->ai.banned_tiles[0] = tile; - p->ai.banned_val[0] = val; - - if (p->ai.banned_tile_count != lengthof(p->ai.banned_tiles)) { - p->ai.banned_tile_count++; - } -} - -static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, int dir); - -static bool AiCheckRailPathBetter(AiRailFinder *arf, const byte *p) -{ - bool better = false; - - if (arf->recursive_mode < 1) { - // Mode is 0. This means destination has not been found yet. - // If the found path is shorter than the current one, remember it. - if (arf->cur_best_dist < arf->best_dist) { - arf->best_dir = arf->cur_best_dir; - arf->best_dist = arf->cur_best_dist; - arf->best_ptr = p; - arf->best_tile = arf->cur_best_tile; - better = true; - } - } else if (arf->recursive_mode > 1) { - // Mode is 2. - if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = 0; - arf->best_ptr = p; - arf->best_tile = 0; - better = true; - } - } - arf->recursive_mode = 0; - arf->cur_best_dist = (uint)-1; - arf->cur_best_depth = 0xff; - - return better; -} - -static inline void AiCheckBuildRailBridgeHere(AiRailFinder *arf, TileIndex tile, const byte *p) -{ - Slope tileh; - uint z; - bool flag; - - int dir2 = p[0] & 3; - - tileh = GetTileSlope(tile, &z); - if (tileh == _dir_table_1[dir2] || (tileh == SLOPE_FLAT && z != 0)) { - TileIndex tile_new = tile; - - // Allow bridges directly over bottom tiles - flag = z == 0; - for (;;) { - TileType type; - - if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible! - tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2)); - type = GetTileType(tile_new); - - if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) { - if (!flag) return; - break; - } - if (type != MP_WATER && type != MP_RAILWAY && type != MP_STREET) return; - flag = true; - } - - // Is building a (rail)bridge possible at this place (type doesn't matter)? - if (CmdFailed(DoCommand(tile_new, tile, 0 | arf->player->ai.railtype_to_use << 8, DC_AUTO, CMD_BUILD_BRIDGE))) { - return; - } - AiBuildRailRecursive(arf, tile_new, dir2); - - // At the bottom depth, check if the new path is better than the old one. - if (arf->depth == 1) { - if (AiCheckRailPathBetter(arf, p)) arf->bridge_end_tile = tile_new; - } - } -} - -static inline void AiCheckBuildRailTunnelHere(AiRailFinder *arf, TileIndex tile, const byte *p) -{ - uint z; - - if (GetTileSlope(tile, &z) == _dir_table_2[p[0] & 3] && z != 0) { - int32 cost = DoCommand(tile, arf->player->ai.railtype_to_use, 0, DC_AUTO, CMD_BUILD_TUNNEL); - - if (!CmdFailed(cost) && cost <= (arf->player->player_money>>4)) { - AiBuildRailRecursive(arf, _build_tunnel_endtile, p[0]&3); - if (arf->depth == 1) AiCheckRailPathBetter(arf, p); - } - } -} - - -static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, int dir) -{ - const byte *p; - - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - // Reached destination? - if (tile == arf->final_tile) { - if (arf->final_dir != (dir^2)) { - if (arf->recursive_mode != 2) arf->recursive_mode = 1; - } else if (arf->recursive_mode != 2) { - arf->recursive_mode = 2; - arf->cur_best_depth = arf->depth; - } else { - if (arf->depth < arf->cur_best_depth) arf->cur_best_depth = arf->depth; - } - return; - } - - // Depth too deep? - if (arf->depth >= 4) { - uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile); - - if (dist < arf->cur_best_dist) { - // Store the tile that is closest to the final position. - arf->cur_best_depth = arf->depth; - arf->cur_best_dist = dist; - arf->cur_best_tile = tile; - arf->cur_best_dir = dir; - } - return; - } - - // Increase recursion depth - arf->depth++; - - // Grab pointer to list of stuff that is possible to build - p = _ai_table_15[dir]; - - // Try to build a single rail in all directions. - if (GetTileZ(tile) == 0) { - p += 6; - } else { - do { - // Make sure the tile is not in the list of banned tiles and that a rail can be built here. - if (!AiIsTileBanned(arf->player, tile, p[0]) && - !CmdFailed(DoCommand(tile, arf->player->ai.railtype_to_use, p[0], DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL))) { - AiBuildRailRecursive(arf, tile, p[1]); - } - - // At the bottom depth? - if (arf->depth == 1) AiCheckRailPathBetter(arf, p); - - p += 2; - } while (!(p[0]&0x80)); - } - - AiCheckBuildRailBridgeHere(arf, tile, p); - AiCheckBuildRailTunnelHere(arf, tile, p+1); - - arf->depth--; -} - - -static const byte _dir_table_3[]= {0x25, 0x2A, 0x19, 0x16}; - -static void AiBuildRailConstruct(Player *p) -{ - AiRailFinder arf; - int i; - - // Check too much lookahead? - if (AiDoFollowTrack(p)) { - p->ai.state_counter = (Random()&0xE)+6; // Destruct this amount of blocks - p->ai.state_mode = 1; // Start destruct - - // Ban this tile and don't reach it for a while. - AiBanTile(p, p->ai.cur_tile_a, FindFirstBit(GetRailTrackStatus(p->ai.cur_tile_a))); - return; - } - - // Setup recursive finder and call it. - arf.player = p; - arf.final_tile = p->ai.cur_tile_b; - arf.final_dir = p->ai.cur_dir_b; - arf.depth = 0; - arf.recursive_mode = 0; - arf.best_ptr = NULL; - arf.cur_best_dist = (uint)-1; - arf.cur_best_depth = 0xff; - arf.best_dist = (uint)-1; - arf.best_depth = 0xff; - arf.cur_best_tile = 0; - arf.best_tile = 0; - AiBuildRailRecursive(&arf, p->ai.cur_tile_a, p->ai.cur_dir_a); - - // Reached destination? - if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) { - p->ai.state_mode = 255; - return; - } - - // Didn't find anything to build? - if (arf.best_ptr == NULL) { - // Terraform some - for (i = 0; i != 5; i++) { - AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0); - } - - if (++p->ai.state_counter == 21) { - p->ai.state_counter = 40; - p->ai.state_mode = 1; - - // Ban this tile - AiBanTile(p, p->ai.cur_tile_a, FindFirstBit(GetRailTrackStatus(p->ai.cur_tile_a))); - } - return; - } - - p->ai.cur_tile_a += TileOffsByDiagDir(p->ai.cur_dir_a); - - if (arf.best_ptr[0] & 0x80) { - int i; - int32 bridge_len = GetBridgeLength(arf.bridge_end_tile, p->ai.cur_tile_a); - - /* Figure out which (rail)bridge type to build - * start with best bridge, then go down to worse and worse bridges - * unnecessary to check for worst bridge (i=0), since AI will always build - * that. AI is so fucked up that fixing this small thing will probably not - * solve a thing - */ - for (i = MAX_BRIDGES - 1; i != 0; i--) { - if (CheckBridge_Stuff(i, bridge_len)) { - int32 cost = DoCommand(arf.bridge_end_tile, p->ai.cur_tile_a, i | (p->ai.railtype_to_use << 8), DC_AUTO, CMD_BUILD_BRIDGE); - if (!CmdFailed(cost) && cost < (p->player_money >> 5)) break; - } - } - - // Build it - DoCommand(arf.bridge_end_tile, p->ai.cur_tile_a, i | (p->ai.railtype_to_use << 8), DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE); - - p->ai.cur_tile_a = arf.bridge_end_tile; - p->ai.state_counter = 0; - } else if (arf.best_ptr[0] & 0x40) { - // tunnel - DoCommand(p->ai.cur_tile_a, p->ai.railtype_to_use, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL); - p->ai.cur_tile_a = _build_tunnel_endtile; - p->ai.state_counter = 0; - } else { - // rail - p->ai.cur_dir_a = arf.best_ptr[1]; - DoCommand(p->ai.cur_tile_a, p->ai.railtype_to_use, arf.best_ptr[0], - DC_EXEC | DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL); - p->ai.state_counter = 0; - } - - if (arf.best_tile != 0) { - for (i = 0; i != 2; i++) { - AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0); - } - } -} - -static bool AiRemoveTileAndGoForward(Player *p) -{ - byte b; - int bit; - const byte *ptr; - TileIndex tile = p->ai.cur_tile_a; - TileIndex tilenew; - - if (IsTileType(tile, MP_TUNNELBRIDGE)) { - if (IsTunnel(tile)) { - // Clear the tunnel and continue at the other side of it. - if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) - return false; - p->ai.cur_tile_a = TILE_MASK(_build_tunnel_endtile - TileOffsByDiagDir(p->ai.cur_dir_a)); - return true; - } else { - // Check if the bridge points in the right direction. - // This is not really needed the first place AiRemoveTileAndGoForward is called. - if (DiagDirToAxis(GetBridgeRampDirection(tile)) != (p->ai.cur_dir_a & 1U)) return false; - - tile = GetOtherBridgeEnd(tile); - - tilenew = TILE_MASK(tile - TileOffsByDiagDir(p->ai.cur_dir_a)); - // And clear the bridge. - if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) - return false; - p->ai.cur_tile_a = tilenew; - return true; - } - } - - // Find the railtype at the position. Quit if no rail there. - b = GetRailTrackStatus(tile) & _dir_table_3[p->ai.cur_dir_a]; - if (b == 0) return false; - - // Convert into a bit position that CMD_REMOVE_SINGLE_RAIL expects. - bit = FindFirstBit(b); - - // Then remove and signals if there are any. - if (IsTileType(tile, MP_RAILWAY) && - GetRailTileType(tile) == RAIL_TILE_SIGNALS) { - DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_SIGNALS); - } - - // And also remove the rail. - if (CmdFailed(DoCommand(tile, 0, bit, DC_EXEC, CMD_REMOVE_SINGLE_RAIL))) - return false; - - // Find the direction at the other edge of the rail. - ptr = _ai_table_15[p->ai.cur_dir_a ^ 2]; - while (ptr[0] != bit) ptr += 2; - p->ai.cur_dir_a = ptr[1] ^ 2; - - // And then also switch tile. - p->ai.cur_tile_a = TILE_MASK(p->ai.cur_tile_a - TileOffsByDiagDir(p->ai.cur_dir_a)); - - return true; -} - - -static void AiBuildRailDestruct(Player *p) -{ - // Decrease timeout. - if (!--p->ai.state_counter) { - p->ai.state_mode = 2; - p->ai.state_counter = 0; - } - - // Don't do anything if the destination is already reached. - if (p->ai.cur_tile_a == p->ai.start_tile_a) return; - - AiRemoveTileAndGoForward(p); -} - - -static void AiBuildRail(Player *p) -{ - switch (p->ai.state_mode) { - case 0: // Construct mode, build new rail. - AiBuildRailConstruct(p); - break; - - case 1: // Destruct mode, destroy the rail currently built. - AiBuildRailDestruct(p); - break; - - case 2: { - uint i; - - // Terraform some and then try building again. - for (i = 0; i != 4; i++) { - AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0); - } - - if (++p->ai.state_counter == 4) { - p->ai.state_counter = 0; - p->ai.state_mode = 0; - } - } - - default: break; - } -} - -static void AiStateBuildRail(Player *p) -{ - int num; - AiBuildRec *aib; - byte cmd; - TileIndex tile; - int dir; - - // time out? - if (++p->ai.timeout_counter == 1388) { - p->ai.state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // Currently building a rail between two points? - if (p->ai.state_mode != 255) { - AiBuildRail(p); - - // Alternate between edges - swap_tile(&p->ai.start_tile_a, &p->ai.start_tile_b); - swap_tile(&p->ai.cur_tile_a, &p->ai.cur_tile_b); - swap_byte(&p->ai.start_dir_a, &p->ai.start_dir_b); - swap_byte(&p->ai.cur_dir_a, &p->ai.cur_dir_b); - return; - } - - // Now, find two new points to build between - num = p->ai.num_build_rec; - aib = &p->ai.src; - - for (;;) { - cmd = aib->buildcmd_a; - aib->buildcmd_a = 255; - if (cmd != 255) break; - - cmd = aib->buildcmd_b; - aib->buildcmd_b = 255; - if (cmd != 255) break; - - aib++; - if (--num == 0) { - p->ai.state = AIS_BUILD_RAIL_VEH; - p->ai.state_counter = 0; // timeout - return; - } - } - - // Find first edge to build from. - tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, cmd&3, &dir); - p->ai.start_tile_a = tile; - p->ai.cur_tile_a = tile; - p->ai.start_dir_a = dir; - p->ai.cur_dir_a = dir; - DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir&1)?1:0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL); - - assert(TILE_MASK(tile) != 0xFF00); - - // Find second edge to build to - aib = (&p->ai.src) + ((cmd >> 4)&0xF); - tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, (cmd>>2)&3, &dir); - p->ai.start_tile_b = tile; - p->ai.cur_tile_b = tile; - p->ai.start_dir_b = dir; - p->ai.cur_dir_b = dir; - DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir&1)?1:0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL); - - assert(TILE_MASK(tile) != 0xFF00); - - // And setup state. - p->ai.state_mode = 2; - p->ai.state_counter = 0; - p->ai.banned_tile_count = 0; -} - -static StationID AiGetStationIdByDef(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _default_rail_track_data[id]->data; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static EngineID AiFindBestWagon(CargoID cargo, RailType railtype) -{ - EngineID best_veh_index = INVALID_ENGINE; - EngineID i; - uint16 best_capacity = 0; - uint16 best_speed = 0; - uint speed; - - for (i = 0; i < NUM_TRAIN_ENGINES; i++) { - const RailVehicleInfo *rvi = RailVehInfo(i); - const Engine* e = GetEngine(i); - - if (!IsCompatibleRail(e->railtype, railtype) || - !(rvi->flags & RVI_WAGON) || - !HASBIT(e->player_avail, _current_player)) { - continue; - } - - if (rvi->cargo_type != cargo) continue; - - /* max_speed of 0 indicates no speed limit */ - speed = rvi->max_speed == 0 ? 0xFFFF : rvi->max_speed; - - if (rvi->capacity >= best_capacity && speed >= best_speed) { - best_capacity = rvi->capacity; - best_speed = best_speed; - best_veh_index = i; - } - } - - return best_veh_index; -} - -static void AiStateBuildRailVeh(Player *p) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - EngineID veh; - int i; - CargoID cargo; - int32 cost; - Vehicle *v; - VehicleID loco_id; - - ptr = _default_rail_track_data[p->ai.src.cur_building_rule]->data; - while (ptr->mode != 0) ptr++; - - tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - - cargo = p->ai.cargo_type; - for (i = 0;;) { - if (p->ai.wagon_list[i] == INVALID_VEHICLE) { - veh = AiFindBestWagon(cargo, p->ai.railtype_to_use); - /* veh will return INVALID_ENGINE if no suitable wagon is available. - * We shall treat this in the same way as having no money */ - if (veh == INVALID_ENGINE) goto handle_nocash; - cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE); - if (CmdFailed(cost)) goto handle_nocash; - p->ai.wagon_list[i] = _new_vehicle_id; - p->ai.wagon_list[i + 1] = INVALID_VEHICLE; - return; - } - if (cargo == CT_MAIL) cargo = CT_PASSENGERS; - if (++i == p->ai.num_wagons * 2 - 1) break; - } - - // Which locomotive to build? - veh = AiChooseTrainToBuild(p->ai.railtype_to_use, p->player_money, cargo != CT_PASSENGERS ? 1 : 0, tile); - if (veh == INVALID_ENGINE) { -handle_nocash: - // after a while, if AI still doesn't have cash, get out of this block by selling the wagons. - if (++p->ai.state_counter == 1000) { - for (i = 0; p->ai.wagon_list[i] != INVALID_VEHICLE; i++) { - cost = DoCommand(tile, p->ai.wagon_list[i], 0, DC_EXEC, CMD_SELL_RAIL_WAGON); - assert(!CmdFailed(cost)); - } - p->ai.state = AIS_0; - } - return; - } - - // Try to build the locomotive - cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE); - assert(!CmdFailed(cost)); - loco_id = _new_vehicle_id; - - // Sell a vehicle if the train is double headed. - v = GetVehicle(loco_id); - if (v->next != NULL) { - i = p->ai.wagon_list[p->ai.num_wagons * 2 - 2]; - p->ai.wagon_list[p->ai.num_wagons * 2 - 2] = INVALID_VEHICLE; - DoCommand(tile, i, 0, DC_EXEC, CMD_SELL_RAIL_WAGON); - } - - // Move the wagons onto the train - for (i = 0; p->ai.wagon_list[i] != INVALID_VEHICLE; i++) { - DoCommand(tile, p->ai.wagon_list[i] | (loco_id << 16), 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE); - } - - for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) { - const AiBuildRec* aib = &p->ai.src + p->ai.order_list_blocks[i]; - bool is_pass = ( - p->ai.cargo_type == CT_PASSENGERS || - p->ai.cargo_type == CT_MAIL || - (_opt.landscape == LT_NORMAL && p->ai.cargo_type == CT_VALUABLES) - ); - Order order; - - order.type = OT_GOTO_STATION; - order.flags = 0; - order.dest = AiGetStationIdByDef(aib->use_tile, aib->cur_building_rule); - - if (!is_pass && i == 1) order.flags |= OF_UNLOAD; - if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) - order.flags |= OF_FULL_LOAD; - - DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_TRAIN); - - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--; - - if (--p->ai.num_loco_to_build != 0) { -// p->ai.loco_id = INVALID_VEHICLE; - p->ai.wagon_list[0] = INVALID_VEHICLE; - } else { - p->ai.state = AIS_0; - } -} - -static void AiStateDeleteRailBlocks(Player *p) -{ - const AiBuildRec* aib = &p->ai.src; - uint num = p->ai.num_build_rec; - - do { - const AiDefaultBlockData* b; - - if (aib->cur_building_rule == 255) continue; - for (b = _default_rail_track_data[aib->cur_building_rule]->data; b->mode != 4; b++) { - DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } while (++aib,--num); - - p->ai.state = AIS_0; -} - -static bool AiCheckRoadResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint values[NUM_CARGO]; - int rad; - - if (_patches.modified_catchment) { - rad = CA_TRUCK; // Same as CA_BUS at the moment? - } else { // change that at some point? - rad = 4; - } - - for (;; p++) { - if (p->mode == 4) { - return true; - } else if (p->mode == 1) { - TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, 1, 1, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, 1, 1, rad); - return (values[cargo]&~7) != 0; - } - } - } -} - -static bool _want_road_truck_station; -static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag); - -// Returns rule and cost -static int AiFindBestDefaultRoadBlock(TileIndex tile, byte direction, byte cargo, int32 *cost) -{ - int i; - const AiDefaultRoadBlock *p; - - _want_road_truck_station = (cargo & 0x7F) != CT_PASSENGERS; - - for (i = 0; (p = _road_default_block_data[i]) != NULL; i++) { - if (p->dir == direction) { - *cost = AiDoBuildDefaultRoadBlock(tile, p->data, 0); - if (!CmdFailed(*cost) && AiCheckRoadResources(tile, p->data, cargo)) - return i; - } - } - - return -1; -} - -static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag) -{ - int32 ret; - int32 total_cost = 0; - Town *t = NULL; - int rating = 0; - int roadflag = 0; - - for (;p->mode != 4;p++) { - TileIndex c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)); - - _cleared_town = NULL; - - if (p->mode == 2) { - if (IsTileType(c, MP_STREET) && - GetRoadTileType(c) == ROAD_TILE_NORMAL && - (GetRoadBits(c) & p->attr) != 0) { - roadflag |= 2; - - // all bits are already built? - if ((GetRoadBits(c) & p->attr) == p->attr) continue; - } - - ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - - continue; - } - - if (p->mode == 0) { - // Depot - ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_DEPOT); - goto clear_town_stuff; - } else if (p->mode == 1) { - if (_want_road_truck_station) { - // Truck station - ret = DoCommand(c, p->attr, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); - } else { - // Bus station - ret = DoCommand(c, p->attr, RS_BUS, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); - } -clear_town_stuff:; - - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - - if (_cleared_town != NULL) { - if (t != NULL && t != _cleared_town) return CMD_ERROR; - t = _cleared_town; - rating += _cleared_town_rating; - } - } else if (p->mode == 3) { - if (flag & DC_EXEC) continue; - - if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR; - - if (!IsTileType(c, MP_STREET) || GetRoadTileType(c) != ROAD_TILE_NORMAL) { - ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR); - if (CmdFailed(ret)) return CMD_ERROR; - } - - } - } - - if (!_want_road_truck_station && !(roadflag & 2)) return CMD_ERROR; - - if (!(flag & DC_EXEC)) { - if (t != NULL && rating > t->ratings[_current_player]) return CMD_ERROR; - } - return total_cost; -} - -// Make sure the blocks are not too close to each other -static bool AiCheckBlockDistances(Player *p, TileIndex tile) -{ - const AiBuildRec* aib = &p->ai.src; - uint num = p->ai.num_build_rec; - - do { - if (aib->cur_building_rule != 255) { - if (DistanceManhattan(aib->use_tile, tile) < 9) return false; - } - } while (++aib, --num); - - return true; -} - - -static void AiStateBuildDefaultRoadBlocks(Player *p) -{ - uint i; - int j; - AiBuildRec *aib; - int rule; - int32 cost; - - // time out? - if (++p->ai.timeout_counter == 1388) { - p->ai.state = AIS_DELETE_RAIL_BLOCKS; - return; - } - - // do the following 8 times - for (i = 0; i != 8; i++) { - // check if we can build the default track - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the road can be built there. - rule = AiFindBestDefaultRoadBlock( - aib->use_tile, aib->direction, aib->cargo, &cost - ); - - if (rule == -1) { - // cannot build, terraform after a while - if (p->ai.state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode); - } - // also try the other terraform direction - if (++p->ai.state_counter >= 1000) { - p->ai.state_counter = 0; - p->ai.state_mode = -p->ai.state_mode; - } - } else if (CheckPlayerHasMoney(cost) && AiCheckBlockDistances(p,aib->use_tile)) { - int32 r; - - // player has money, build it. - aib->cur_building_rule = rule; - - r = AiDoBuildDefaultRoadBlock( - aib->use_tile, - _road_default_block_data[rule]->data, - DC_EXEC | DC_NO_TOWN_RATING - ); - assert(!CmdFailed(r)); - } - } while (++aib,--j); - } - - // check if we're done with all of them - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib,--j); - - // yep, all are done. switch state to the rail building state. - p->ai.state = AIS_BUILD_ROAD; - p->ai.state_mode = 255; -} - -typedef struct { - TileIndex final_tile; - byte final_dir; - byte depth; - byte recursive_mode; - byte cur_best_dir; - byte best_dir; - byte cur_best_depth; - byte best_depth; - uint cur_best_dist; - const byte *best_ptr; - uint best_dist; - TileIndex cur_best_tile, best_tile; - TileIndex bridge_end_tile; - Player *player; -} AiRoadFinder; - -typedef struct AiRoadEnum { - TileIndex dest; - TileIndex best_tile; - int best_track; - uint best_dist; -} AiRoadEnum; - -static const byte _dir_by_track[] = { - 0, 1, 0, 1, 2, 1, - 0, 0, - 2, 3, 3, 2, 3, 0, -}; - -static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, int dir); - -static bool AiCheckRoadPathBetter(AiRoadFinder *arf, const byte *p) -{ - bool better = false; - - if (arf->recursive_mode < 1) { - // Mode is 0. This means destination has not been found yet. - // If the found path is shorter than the current one, remember it. - if (arf->cur_best_dist < arf->best_dist || - (arf->cur_best_dist == arf->best_dist && arf->cur_best_depth < arf->best_depth)) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = arf->cur_best_dist; - arf->best_dir = arf->cur_best_dir; - arf->best_ptr = p; - arf->best_tile = arf->cur_best_tile; - better = true; - } - } else if (arf->recursive_mode > 1) { - // Mode is 2. - if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) { - arf->best_depth = arf->cur_best_depth; - arf->best_dist = 0; - arf->best_ptr = p; - arf->best_tile = 0; - better = true; - } - } - arf->recursive_mode = 0; - arf->cur_best_dist = (uint)-1; - arf->cur_best_depth = 0xff; - - return better; -} - - -static bool AiEnumFollowRoad(TileIndex tile, AiRoadEnum *a, int track, uint length, byte *state) -{ - uint dist = DistanceManhattan(tile, a->dest); - - if (dist <= a->best_dist) { - TileIndex tile2 = TILE_MASK(tile + TileOffsByDiagDir(_dir_by_track[track])); - - if (IsTileType(tile2, MP_STREET) && GetRoadTileType(tile2) == ROAD_TILE_NORMAL) { - a->best_dist = dist; - a->best_tile = tile; - a->best_track = track; - } - } - - return false; -} - -static const uint16 _ai_road_table_and[4] = { - 0x1009, - 0x16, - 0x520, - 0x2A00, -}; - -static bool AiCheckRoadFinished(Player *p) -{ - AiRoadEnum are; - TileIndex tile; - int dir = p->ai.cur_dir_a; - uint32 bits; - int i; - - are.dest = p->ai.cur_tile_b; - tile = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(dir)); - - if (IsRoadStopTile(tile) || IsTileDepotType(tile, TRANSPORT_ROAD)) return false; - bits = GetTileTrackStatus(tile, TRANSPORT_ROAD) & _ai_road_table_and[dir]; - if (bits == 0) return false; - - are.best_dist = (uint)-1; - - for_each_bit(i, bits) { - FollowTrack(tile, 0x3000 | TRANSPORT_ROAD, _dir_by_track[i], (TPFEnumProc*)AiEnumFollowRoad, NULL, &are); - } - - if (DistanceManhattan(tile, are.dest) <= are.best_dist) return false; - - if (are.best_dist == 0) return true; - - p->ai.cur_tile_a = are.best_tile; - p->ai.cur_dir_a = _dir_by_track[are.best_track]; - return false; -} - - -static bool AiBuildRoadHelper(TileIndex tile, int flags, int type) -{ - static const RoadBits _road_bits[] = { - ROAD_X, - ROAD_Y, - ROAD_NW | ROAD_NE, - ROAD_SW | ROAD_SE, - ROAD_NW | ROAD_SW, - ROAD_SE | ROAD_NE - }; - return !CmdFailed(DoCommand(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD)); -} - -static inline void AiCheckBuildRoadBridgeHere(AiRoadFinder *arf, TileIndex tile, const byte *p) -{ - Slope tileh; - uint z; - bool flag; - - int dir2 = p[0] & 3; - - tileh = GetTileSlope(tile, &z); - if (tileh == _dir_table_1[dir2] || (tileh == SLOPE_FLAT && z != 0)) { - TileIndex tile_new = tile; - - // Allow bridges directly over bottom tiles - flag = z == 0; - for (;;) { - TileType type; - - if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible! - tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2)); - type = GetTileType(tile_new); - - if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile, NULL) != SLOPE_FLAT) { - // Allow a bridge if either we have a tile that's water, rail or street, - // or if we found an up tile. - if (!flag) return; - break; - } - if (type != MP_WATER && type != MP_RAILWAY && type != MP_STREET) return; - flag = true; - } - - // Is building a (rail)bridge possible at this place (type doesn't matter)? - if (CmdFailed(DoCommand(tile_new, tile, 0x8000, DC_AUTO, CMD_BUILD_BRIDGE))) - return; - AiBuildRoadRecursive(arf, tile_new, dir2); - - // At the bottom depth, check if the new path is better than the old one. - if (arf->depth == 1) { - if (AiCheckRoadPathBetter(arf, p)) arf->bridge_end_tile = tile_new; - } - } -} - -static inline void AiCheckBuildRoadTunnelHere(AiRoadFinder *arf, TileIndex tile, const byte *p) -{ - uint z; - - if (GetTileSlope(tile, &z) == _dir_table_2[p[0] & 3] && z != 0) { - int32 cost = DoCommand(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL); - - if (!CmdFailed(cost) && cost <= (arf->player->player_money>>4)) { - AiBuildRoadRecursive(arf, _build_tunnel_endtile, p[0]&3); - if (arf->depth == 1) AiCheckRoadPathBetter(arf, p); - } - } -} - - - -static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, int dir) -{ - const byte *p; - - tile = TILE_MASK(tile + TileOffsByDiagDir(dir)); - - // Reached destination? - if (tile == arf->final_tile) { - if ((arf->final_dir^2) == dir) { - arf->recursive_mode = 2; - arf->cur_best_depth = arf->depth; - } - return; - } - - // Depth too deep? - if (arf->depth >= 4) { - uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile); - if (dist < arf->cur_best_dist) { - // Store the tile that is closest to the final position. - arf->cur_best_dist = dist; - arf->cur_best_tile = tile; - arf->cur_best_dir = dir; - arf->cur_best_depth = arf->depth; - } - return; - } - - // Increase recursion depth - arf->depth++; - - // Grab pointer to list of stuff that is possible to build - p = _ai_table_15[dir]; - - // Try to build a single rail in all directions. - if (GetTileZ(tile) == 0) { - p += 6; - } else { - do { - // Make sure that a road can be built here. - if (AiBuildRoadHelper(tile, DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, p[0])) { - AiBuildRoadRecursive(arf, tile, p[1]); - } - - // At the bottom depth? - if (arf->depth == 1) AiCheckRoadPathBetter(arf, p); - - p += 2; - } while (!(p[0] & 0x80)); - } - - AiCheckBuildRoadBridgeHere(arf, tile, p); - AiCheckBuildRoadTunnelHere(arf, tile, p+1); - - arf->depth--; -} - - -static void AiBuildRoadConstruct(Player *p) -{ - AiRoadFinder arf; - int i; - TileIndex tile; - - // Reached destination? - if (AiCheckRoadFinished(p)) { - p->ai.state_mode = 255; - return; - } - - // Setup recursive finder and call it. - arf.player = p; - arf.final_tile = p->ai.cur_tile_b; - arf.final_dir = p->ai.cur_dir_b; - arf.depth = 0; - arf.recursive_mode = 0; - arf.best_ptr = NULL; - arf.cur_best_dist = (uint)-1; - arf.cur_best_depth = 0xff; - arf.best_dist = (uint)-1; - arf.best_depth = 0xff; - arf.cur_best_tile = 0; - arf.best_tile = 0; - AiBuildRoadRecursive(&arf, p->ai.cur_tile_a, p->ai.cur_dir_a); - - // Reached destination? - if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) { - p->ai.state_mode = 255; - return; - } - - // Didn't find anything to build? - if (arf.best_ptr == NULL) { - // Terraform some -do_some_terraform: - for (i = 0; i != 5; i++) - AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0); - - if (++p->ai.state_counter == 21) { - p->ai.state_mode = 1; - - p->ai.cur_tile_a = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a)); - p->ai.cur_dir_a ^= 2; - p->ai.state_counter = 0; - } - return; - } - - tile = TILE_MASK(p->ai.cur_tile_a + TileOffsByDiagDir(p->ai.cur_dir_a)); - - if (arf.best_ptr[0]&0x80) { - int i; - int32 bridge_len; - p->ai.cur_tile_a = arf.bridge_end_tile; - bridge_len = GetBridgeLength(tile, p->ai.cur_tile_a); // tile - - /* Figure out what (road)bridge type to build - * start with best bridge, then go down to worse and worse bridges - * unnecessary to check for worse bridge (i=0), since AI will always build that. - *AI is so fucked up that fixing this small thing will probably not solve a thing - */ - for (i = 10; i != 0; i--) { - if (CheckBridge_Stuff(i, bridge_len)) { - int32 cost = DoCommand(tile, p->ai.cur_tile_a, i + (0x80 << 8), DC_AUTO, CMD_BUILD_BRIDGE); - if (!CmdFailed(cost) && cost < (p->player_money >> 5)) break; - } - } - - // Build it - DoCommand(tile, p->ai.cur_tile_a, i + (0x80 << 8), DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE); - - p->ai.state_counter = 0; - } else if (arf.best_ptr[0]&0x40) { - // tunnel - DoCommand(tile, 0x200, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL); - p->ai.cur_tile_a = _build_tunnel_endtile; - p->ai.state_counter = 0; - } else { - // road - if (!AiBuildRoadHelper(tile, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, arf.best_ptr[0])) - goto do_some_terraform; - - p->ai.cur_dir_a = arf.best_ptr[1]; - p->ai.cur_tile_a = tile; - p->ai.state_counter = 0; - } - - if (arf.best_tile != 0) { - for (i = 0; i != 2; i++) - AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0); - } -} - - -static void AiBuildRoad(Player *p) -{ - if (p->ai.state_mode < 1) { - // Construct mode, build new road. - AiBuildRoadConstruct(p); - } else if (p->ai.state_mode == 1) { - // Destruct mode, not implemented for roads. - p->ai.state_mode = 2; - p->ai.state_counter = 0; - } else if (p->ai.state_mode == 2) { - uint i; - - // Terraform some and then try building again. - for (i = 0; i != 4; i++) { - AiDoTerraformLand(p->ai.cur_tile_a, p->ai.cur_dir_a, 3, 0); - } - - if (++p->ai.state_counter == 4) { - p->ai.state_counter = 0; - p->ai.state_mode = 0; - } - } -} - -static TileIndex AiGetRoadBlockEdge(byte rule, TileIndex tile, int *dir) -{ - const AiDefaultBlockData *p = _road_default_block_data[rule]->data; - while (p->mode != 1) p++; - *dir = p->attr; - return TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); -} - - -static void AiStateBuildRoad(Player *p) -{ - int num; - AiBuildRec *aib; - byte cmd; - TileIndex tile; - int dir; - - // time out? - if (++p->ai.timeout_counter == 1388) { - p->ai.state = AIS_DELETE_ROAD_BLOCKS; - return; - } - - // Currently building a road between two points? - if (p->ai.state_mode != 255) { - AiBuildRoad(p); - - // Alternate between edges - swap_tile(&p->ai.start_tile_a, &p->ai.start_tile_b); - swap_tile(&p->ai.cur_tile_a, &p->ai.cur_tile_b); - swap_byte(&p->ai.start_dir_a, &p->ai.start_dir_b); - swap_byte(&p->ai.cur_dir_a, &p->ai.cur_dir_b); - - return; - } - - // Now, find two new points to build between - num = p->ai.num_build_rec; - aib = &p->ai.src; - - for (;;) { - cmd = aib->buildcmd_a; - aib->buildcmd_a = 255; - if (cmd != 255) break; - - aib++; - if (--num == 0) { - p->ai.state = AIS_BUILD_ROAD_VEHICLES; - return; - } - } - - // Find first edge to build from. - tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir); - p->ai.start_tile_a = tile; - p->ai.cur_tile_a = tile; - p->ai.start_dir_a = dir; - p->ai.cur_dir_a = dir; - - // Find second edge to build to - aib = (&p->ai.src) + (cmd&0xF); - tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir); - p->ai.start_tile_b = tile; - p->ai.cur_tile_b = tile; - p->ai.start_dir_b = dir; - p->ai.cur_dir_b = dir; - - // And setup state. - p->ai.state_mode = 2; - p->ai.state_counter = 0; - p->ai.banned_tile_count = 0; -} - -static StationID AiGetStationIdFromRoadBlock(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _road_default_block_data[id]->data; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static void AiStateBuildRoadVehicles(Player *p) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - VehicleID loco_id; - EngineID veh; - uint i; - - ptr = _road_default_block_data[p->ai.src.cur_building_rule]->data; - for (; ptr->mode != 0; ptr++) {} - tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - veh = AiChooseRoadVehToBuild(p->ai.cargo_type, p->player_money, tile); - if (veh == INVALID_ENGINE) { - p->ai.state = AIS_0; - return; - } - - if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) return; - - loco_id = _new_vehicle_id; - - if (GetVehicle(loco_id)->cargo_type != p->ai.cargo_type) { - /* Cargo type doesn't match, so refit it */ - if (CmdFailed(DoCommand(tile, loco_id, p->ai.cargo_type, DC_EXEC, CMD_REFIT_ROAD_VEH))) { - /* Refit failed... sell the vehicle */ - DoCommand(tile, loco_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - return; - } - } - - for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) { - const AiBuildRec* aib = &p->ai.src + p->ai.order_list_blocks[i]; - bool is_pass = ( - p->ai.cargo_type == CT_PASSENGERS || - p->ai.cargo_type == CT_MAIL || - (_opt.landscape == LT_NORMAL && p->ai.cargo_type == CT_VALUABLES) - ); - Order order; - - order.type = OT_GOTO_STATION; - order.flags = 0; - order.dest = AiGetStationIdFromRoadBlock(aib->use_tile, aib->cur_building_rule); - - if (!is_pass && i == 1) order.flags |= OF_UNLOAD; - if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) - order.flags |= OF_FULL_LOAD; - - DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH); - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--; - if (--p->ai.num_loco_to_build == 0) p->ai.state = AIS_0; -} - -static void AiStateDeleteRoadBlocks(Player *p) -{ - const AiBuildRec* aib = &p->ai.src; - uint num = p->ai.num_build_rec; - - do { - const AiDefaultBlockData* b; - - if (aib->cur_building_rule == 255) continue; - for (b = _road_default_block_data[aib->cur_building_rule]->data; b->mode != 4; b++) { - if (b->mode > 1) continue; - DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } while (++aib,--num); - - p->ai.state = AIS_0; -} - - -static void AiStateAirportStuff(Player *p) -{ - const Station* st; - byte acc_planes; - int i; - AiBuildRec *aib; - byte rule; - - // Here we look for an airport we could use instead of building a new - // one. If we find such an aiport for any waypoint, - // AiStateBuildDefaultAirportBlocks() will kindly skip that one when - // building the waypoints. - - i = 0; - do { - // We do this all twice - once for the source (town in the case - // of oilrig route) and then for the destination (oilrig in the - // case of oilrig route). - aib = &p->ai.src + i; - - FOR_ALL_STATIONS(st) { - // Is this an airport? - if (!(st->facilities & FACIL_AIRPORT)) continue; - - // Do we own the airport? (Oilrigs aren't owned, though.) - if (st->owner != OWNER_NONE && st->owner != _current_player) continue; - - acc_planes = GetAirport(st->airport_type)->acc_planes; - - // Dismiss heliports, unless we are checking an oilrig. - if (acc_planes == HELICOPTERS_ONLY && (p->ai.build_kind != 1 || i != 1)) - continue; - - // Dismiss country airports if we are doing the other - // endpoint of an oilrig route. - if (acc_planes == AIRCRAFT_ONLY && (p->ai.build_kind == 1 && i == 0)) - continue; - - // Dismiss airports too far away. - if (DistanceMax(st->airport_tile, aib->spec_tile) > aib->rand_rng) - continue; - - // It's ideal airport, let's take it! - - /* XXX: This part is utterly broken - rule should - * contain number of the rule appropriate for the - * airport type (country, town, ...), see - * _airport_default_block_data (rule is just an index - * in this array). But the only difference between the - * currently existing two rules (rule 0 - town and rule - * 1 - country) is the attr field which is used only - * when building new airports - and that's irrelevant - * for us. So using just about any rule will suffice - * here for now (some of the new airport types would be - * broken because they will probably need different - * tileoff values etc), no matter that - * IsHangarTile() makes no sense. --pasky */ - if (acc_planes == HELICOPTERS_ONLY) { - /* Heliports should have maybe own rulesets but - * OTOH we don't want AI to pick them up when - * looking for a suitable airport type to build. - * So any of rules 0 or 1 would do for now. The - * original rule number was 2 but that's a bug - * because we have no such rule. */ - rule = 1; - } else { - rule = IsHangarTile(st->airport_tile); - } - - aib->cur_building_rule = rule; - aib->use_tile = st->airport_tile; - break; - } - } while (++i != p->ai.num_build_rec); - - p->ai.state = AIS_BUILD_DEFAULT_AIRPORT_BLOCKS; - p->ai.state_mode = 255; - p->ai.state_counter = 0; -} - -static int32 AiDoBuildDefaultAirportBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag) -{ - int32 total_cost = 0, ret; - - for (; p->mode == 0; p++) { - if (!HASBIT(_avail_aircraft, p->attr)) return CMD_ERROR; - ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr,0,flag | DC_AUTO | DC_NO_WATER,CMD_BUILD_AIRPORT); - if (CmdFailed(ret)) return CMD_ERROR; - total_cost += ret; - } - - return total_cost; -} - -static bool AiCheckAirportResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo) -{ - uint values[NUM_CARGO]; - int rad; - - if (_patches.modified_catchment) { - rad = CA_AIR_LARGE; // I Have NFI what airport the - } else { // AI is going to build here - rad = 4; - } - - for (; p->mode == 0; p++) { - TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)); - const AirportFTAClass* airport = GetAirport(p->attr); - uint w = airport->size_x; - uint h = airport->size_y; - - if (cargo & 0x80) { - GetProductionAroundTiles(values, tile2, w, h, rad); - return values[cargo & 0x7F] != 0; - } else { - GetAcceptanceAroundTiles(values, tile2, w, h, rad); - return values[cargo] >= 8; - } - } - return true; -} - -static int AiFindBestDefaultAirportBlock(TileIndex tile, byte cargo, byte heli, int32 *cost) -{ - const AiDefaultBlockData *p; - uint i; - - for (i = 0; (p = _airport_default_block_data[i]) != NULL; i++) { - // If we are doing a helicopter service, avoid building - // airports where they can't land. - if (heli && GetAirport(p->attr)->acc_planes == AIRCRAFT_ONLY) continue; - - *cost = AiDoBuildDefaultAirportBlock(tile, p, 0); - if (!CmdFailed(*cost) && AiCheckAirportResources(tile, p, cargo)) - return i; - } - return -1; -} - -static void AiStateBuildDefaultAirportBlocks(Player *p) -{ - int i, j; - AiBuildRec *aib; - int rule; - int32 cost; - - // time out? - if (++p->ai.timeout_counter == 1388) { - p->ai.state = AIS_0; - return; - } - - // do the following 8 times - i = 8; - do { - // check if we can build the default - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - // this item has already been built? - if (aib->cur_building_rule != 255) continue; - - // adjust the coordinate randomly, - // to make sure that we find a position. - aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng); - - // check if the aircraft stuff can be built there. - rule = AiFindBestDefaultAirportBlock(aib->use_tile, aib->cargo, p->ai.build_kind, &cost); - -// SetRedErrorSquare(aib->use_tile); - - if (rule == -1) { - // cannot build, terraform after a while - if (p->ai.state_counter >= 600) { - AiDoTerraformLand(aib->use_tile, Random()&3, 3, (int8)p->ai.state_mode); - } - // also try the other terraform direction - if (++p->ai.state_counter >= 1000) { - p->ai.state_counter = 0; - p->ai.state_mode = -p->ai.state_mode; - } - } else if (CheckPlayerHasMoney(cost) && AiCheckBlockDistances(p,aib->use_tile)) { - // player has money, build it. - int32 r; - - aib->cur_building_rule = rule; - - r = AiDoBuildDefaultAirportBlock( - aib->use_tile, - _airport_default_block_data[rule], - DC_EXEC | DC_NO_TOWN_RATING - ); - assert(!CmdFailed(r)); - } - } while (++aib,--j); - } while (--i); - - // check if we're done with all of them - aib = &p->ai.src; - j = p->ai.num_build_rec; - do { - if (aib->cur_building_rule == 255) return; - } while (++aib,--j); - - // yep, all are done. switch state. - p->ai.state = AIS_BUILD_AIRCRAFT_VEHICLES; -} - -static StationID AiGetStationIdFromAircraftBlock(TileIndex tile, int id) -{ - const AiDefaultBlockData *p = _airport_default_block_data[id]; - while (p->mode != 1) p++; - return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs))); -} - -static void AiStateBuildAircraftVehicles(Player *p) -{ - const AiDefaultBlockData *ptr; - TileIndex tile; - EngineID veh; - int i; - VehicleID loco_id; - - ptr = _airport_default_block_data[p->ai.src.cur_building_rule]; - for (; ptr->mode != 0; ptr++) {} - - tile = TILE_ADD(p->ai.src.use_tile, ToTileIndexDiff(ptr->tileoffs)); - - veh = AiChooseAircraftToBuild(p->player_money, p->ai.build_kind != 0 ? 0 : AIR_CTOL); - if (veh == INVALID_ENGINE) return; - - /* XXX - Have the AI pick the hangar terminal in an airport. Eg get airport-type - * and offset to the FIRST depot because the AI picks the st->xy tile */ - tile += ToTileIndexDiff(GetAirport(GetStationByTile(tile)->airport_type)->airport_depots[0]); - if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) return; - loco_id = _new_vehicle_id; - - for (i = 0; p->ai.order_list_blocks[i] != 0xFF; i++) { - AiBuildRec *aib = (&p->ai.src) + p->ai.order_list_blocks[i]; - bool is_pass = (p->ai.cargo_type == CT_PASSENGERS || p->ai.cargo_type == CT_MAIL); - Order order; - - order.type = OT_GOTO_STATION; - order.flags = 0; - order.dest = AiGetStationIdFromAircraftBlock(aib->use_tile, aib->cur_building_rule); - - if (!is_pass && i == 1) order.flags |= OF_UNLOAD; - if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) - order.flags |= OF_FULL_LOAD; - - DoCommand(0, loco_id + (i << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - } - - DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT); - - DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT); - - if (p->ai.num_want_fullload != 0) p->ai.num_want_fullload--; - - if (--p->ai.num_loco_to_build == 0) p->ai.state = AIS_0; -} - -static void AiStateCheckShipStuff(Player *p) -{ - // XXX - error("!AiStateCheckShipStuff"); -} - -static void AiStateBuildDefaultShipBlocks(Player *p) -{ - // XXX - error("!AiStateBuildDefaultShipBlocks"); -} - -static void AiStateDoShipStuff(Player *p) -{ - // XXX - error("!AiStateDoShipStuff"); -} - -static void AiStateSellVeh(Player *p) -{ - Vehicle *v = p->ai.cur_veh; - - if (v->owner == _current_player) { - if (v->type == VEH_Train) { - - if (!IsTileDepotType(v->tile, TRANSPORT_RAIL) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) { - if (v->current_order.type != OT_GOTO_DEPOT) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_TRAIN_TO_DEPOT); - goto going_to_depot; - } - - // Sell whole train - DoCommand(v->tile, v->index, 1, DC_EXEC, CMD_SELL_RAIL_WAGON); - - } else if (v->type == VEH_Road) { - if (!IsRoadVehInDepotStopped(v)) { - if (v->current_order.type != OT_GOTO_DEPOT) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT); - goto going_to_depot; - } - - DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - } else if (v->type == VEH_Aircraft) { - if (!IsAircraftInHangarStopped(v)) { - if (v->current_order.type != OT_GOTO_DEPOT) - DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR); - goto going_to_depot; - } - - DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT); - } else if (v->type == VEH_Ship) { - // XXX: not implemented - error("!v->type == VEH_Ship"); - } - } - - goto return_to_loop; -going_to_depot:; - if (++p->ai.state_counter <= 832) return; - - if (v->current_order.type == OT_GOTO_DEPOT) { - v->current_order.type = OT_DUMMY; - v->current_order.flags = 0; - InvalidateWindow(WC_VEHICLE_VIEW, v->index); - } -return_to_loop:; - p->ai.state = AIS_VEH_LOOP; -} - -static void AiStateRemoveStation(Player *p) -{ - // Remove stations that aren't in use by any vehicle - byte *in_use; - const Order *ord; - const Station *st; - TileIndex tile; - - // Go to this state when we're done. - p->ai.state = AIS_1; - - // Get a list of all stations that are in use by a vehicle - in_use = malloc(GetMaxStationIndex() + 1); - memset(in_use, 0, GetMaxStationIndex() + 1); - FOR_ALL_ORDERS(ord) { - if (ord->type == OT_GOTO_STATION) in_use[ord->dest] = 1; - } - - // Go through all stations and delete those that aren't in use - FOR_ALL_STATIONS(st) { - if (st->owner == _current_player && !in_use[st->index] && - ( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != 0) || - (st->truck_stops != NULL && (tile = st->truck_stops->xy)) != 0 || - (tile = st->train_tile) != 0 || - (tile = st->dock_tile) != 0 || - (tile = st->airport_tile) != 0)) { - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } - - free(in_use); -} - -static void AiRemovePlayerRailOrRoad(Player *p, TileIndex tile) -{ - TrackBits rails; - - if (IsTileType(tile, MP_RAILWAY)) { - if (!IsTileOwner(tile, _current_player)) return; - - if (IsPlainRailTile(tile)) { -is_rail_crossing:; - rails = GetRailTrackStatus(tile); - - if (rails == TRACK_BIT_HORZ || rails == TRACK_BIT_VERT) return; - - if (rails & TRACK_BIT_3WAY_NE) { -pos_0: - if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(1, 0))) & TRACK_BIT_3WAY_SW) == 0) { - p->ai.cur_dir_a = 0; - p->ai.cur_tile_a = tile; - p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_SE) { -pos_1: - if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(0, 1))) & TRACK_BIT_3WAY_NW) == 0) { - p->ai.cur_dir_a = 1; - p->ai.cur_tile_a = tile; - p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_SW) { -pos_2: - if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(1, 0))) & TRACK_BIT_3WAY_NE) == 0) { - p->ai.cur_dir_a = 2; - p->ai.cur_tile_a = tile; - p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - - if (rails & TRACK_BIT_3WAY_NW) { -pos_3: - if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(0, 1))) & TRACK_BIT_3WAY_SE) == 0) { - p->ai.cur_dir_a = 3; - p->ai.cur_tile_a = tile; - p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE; - return; - } - } - } else { - static const byte _depot_bits[] = {0x19,0x16,0x25,0x2A}; - - DiagDirection dir = GetRailDepotDirection(tile); - - if (GetRailTrackStatus(tile + TileOffsByDiagDir(dir)) & _depot_bits[dir]) - return; - - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - } - } else if (IsTileType(tile, MP_STREET)) { - if (!IsTileOwner(tile, _current_player)) return; - - if (IsLevelCrossing(tile)) goto is_rail_crossing; - - if (GetRoadTileType(tile) == ROAD_TILE_DEPOT) { - DiagDirection dir; - TileIndex t; - - // Check if there are any stations around. - t = tile + TileDiffXY(-1, 0); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return; - - t = tile + TileDiffXY(1, 0); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return; - - t = tile + TileDiffXY(0, -1); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return; - - t = tile + TileDiffXY(0, 1); - if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_player)) return; - - dir = GetRoadDepotDirection(tile); - - DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - DoCommand( - TILE_MASK(tile + TileOffsByDiagDir(dir)), - DiagDirToRoadBits(ReverseDiagDir(dir)), - 0, - DC_EXEC, - CMD_REMOVE_ROAD); - } - } else if (IsTileType(tile, MP_TUNNELBRIDGE)) { - if (!IsTileOwner(tile, _current_player) || - !IsBridge(tile) || - GetBridgeTransportType(tile) != TRANSPORT_RAIL) { - return; - } - - rails = 0; - - switch (GetBridgeRampDirection(tile)) { - default: - case DIAGDIR_NE: goto pos_2; - case DIAGDIR_SE: goto pos_3; - case DIAGDIR_SW: goto pos_0; - case DIAGDIR_NW: goto pos_1; - } - } -} - -static void AiStateRemoveTrack(Player *p) -{ - /* Was 1000 for standard 8x8 maps. */ - int num = MapSizeX() * 4; - - do { - TileIndex tile = ++p->ai.state_counter; - - // Iterated all tiles? - if (tile >= MapSize()) { - p->ai.state = AIS_REMOVE_STATION; - return; - } - - // Remove player stuff in that tile - AiRemovePlayerRailOrRoad(p, tile); - if (p->ai.state != AIS_REMOVE_TRACK) return; - } while (--num); -} - -static void AiStateRemoveSingleRailTile(Player *p) -{ - // Remove until we can't remove more. - if (!AiRemoveTileAndGoForward(p)) p->ai.state = AIS_REMOVE_TRACK; -} - -static AiStateAction * const _ai_actions[] = { - AiCase0, - AiCase1, - AiStateVehLoop, - AiStateCheckReplaceVehicle, - AiStateDoReplaceVehicle, - AiStateWantNewRoute, - - AiStateBuildDefaultRailBlocks, - AiStateBuildRail, - AiStateBuildRailVeh, - AiStateDeleteRailBlocks, - - AiStateBuildDefaultRoadBlocks, - AiStateBuildRoad, - AiStateBuildRoadVehicles, - AiStateDeleteRoadBlocks, - - AiStateAirportStuff, - AiStateBuildDefaultAirportBlocks, - AiStateBuildAircraftVehicles, - - AiStateCheckShipStuff, - AiStateBuildDefaultShipBlocks, - AiStateDoShipStuff, - - AiStateSellVeh, - AiStateRemoveStation, - AiStateRemoveTrack, - - AiStateRemoveSingleRailTile -}; - -extern void ShowBuyCompanyDialog(uint player); - -static void AiHandleTakeover(Player *p) -{ - if (p->bankrupt_timeout != 0) { - p->bankrupt_timeout -= 8; - if (p->bankrupt_timeout > 0) return; - p->bankrupt_timeout = 0; - DeleteWindowById(WC_BUY_COMPANY, _current_player); - if (IsLocalPlayer()) { - AskExitToGameMenu(); - return; - } - if (IsHumanPlayer(_current_player)) return; - } - - if (p->bankrupt_asked == 255) return; - - { - uint asked = p->bankrupt_asked; - Player *pp, *best_pl = NULL; - int32 best_val = -1; - uint old_p; - - // Ask the guy with the highest performance hist. - FOR_ALL_PLAYERS(pp) { - if (pp->is_active && - !(asked&1) && - pp->bankrupt_asked == 0 && - best_val < pp->old_economy[1].performance_history) { - best_val = pp->old_economy[1].performance_history; - best_pl = pp; - } - asked>>=1; - } - - // Asked all players? - if (best_val == -1) { - p->bankrupt_asked = 255; - return; - } - - SETBIT(p->bankrupt_asked, best_pl->index); - - if (best_pl->index == _local_player) { - p->bankrupt_timeout = 4440; - ShowBuyCompanyDialog(_current_player); - return; - } - if (IsHumanPlayer(best_pl->index)) return; - - // Too little money for computer to buy it? - if (best_pl->player_money >> 1 >= p->bankrupt_value) { - // Computer wants to buy it. - old_p = _current_player; - _current_player = p->index; - DoCommand(0, old_p, 0, DC_EXEC, CMD_BUY_COMPANY); - _current_player = old_p; - } - } -} - -static void AiAdjustLoan(const Player* p) -{ - int32 base = AiGetBasePrice(p); - - if (p->player_money > base * 1400) { - // Decrease loan - if (p->current_loan != 0) { - DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN); - } - } else if (p->player_money < base * 500) { - // Increase loan - if (p->current_loan < _economy.max_loan && - p->num_valid_stat_ent >= 2 && - -(p->old_economy[0].expenses+p->old_economy[1].expenses) < base * 60) { - DoCommand(0, 0, 0, DC_EXEC, CMD_INCREASE_LOAN); - } - } -} - -static void AiBuildCompanyHQ(Player *p) -{ - TileIndex tile; - - if (p->location_of_house == 0 && - p->last_build_coordinate != 0) { - tile = AdjustTileCoordRandomly(p->last_build_coordinate, 8); - DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ); - } -} - - -void AiDoGameLoop(Player *p) -{ - if (p->bankrupt_asked != 0) { - AiHandleTakeover(p); - return; - } - - // Ugly hack to make sure the service interval of the AI is good, not looking - // to the patch-setting - // Also, it takes into account the setting if the service-interval is in days - // or in % - _ai_service_interval = _patches.servint_ispercent?80:180; - - if (IsHumanPlayer(_current_player)) return; - - AiAdjustLoan(p); - AiBuildCompanyHQ(p); - -#if 0 - { - static byte old_state = 99; - static bool hasdots = false; - char *_ai_state_names[]={ - "AiCase0", - "AiCase1", - "AiStateVehLoop", - "AiStateCheckReplaceVehicle", - "AiStateDoReplaceVehicle", - "AiStateWantNewRoute", - "AiStateBuildDefaultRailBlocks", - "AiStateBuildRail", - "AiStateBuildRailVeh", - "AiStateDeleteRailBlocks", - "AiStateBuildDefaultRoadBlocks", - "AiStateBuildRoad", - "AiStateBuildRoadVehicles", - "AiStateDeleteRoadBlocks", - "AiStateAirportStuff", - "AiStateBuildDefaultAirportBlocks", - "AiStateBuildAircraftVehicles", - "AiStateCheckShipStuff", - "AiStateBuildDefaultShipBlocks", - "AiStateDoShipStuff", - "AiStateSellVeh", - "AiStateRemoveStation", - "AiStateRemoveTrack", - "AiStateRemoveSingleRailTile" - }; - - if (p->ai.state != old_state) { - if (hasdots) - printf("\n"); - hasdots=false; - printf("AiState: %s\n", _ai_state_names[old_state=p->ai.state]); - } else { - printf("."); - hasdots=true; - } - } -#endif - - _ai_actions[p->ai.state](p); -} diff --git a/ai/default/default.h b/ai/default/default.h deleted file mode 100644 --- a/ai/default/default.h +++ /dev/null @@ -1,8 +0,0 @@ -/* $Id$ */ - -#ifndef DEFAULT_H -#define DEFAULT_H - -void AiDoGameLoop(Player*); - -#endif diff --git a/ai/trolly/build.c b/ai/trolly/build.c deleted file mode 100644 --- a/ai/trolly/build.c +++ /dev/null @@ -1,324 +0,0 @@ -/* $Id$ */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../functions.h" -#include "../../map.h" -#include "../../road_map.h" -#include "../../tile.h" -#include "../../vehicle.h" -#include "../../command.h" -#include "trolly.h" -#include "../../engine.h" -#include "../../station.h" -#include "../../variables.h" -#include "../../bridge.h" -#include "../ai.h" - -// Build HQ -// Params: -// tile : tile where HQ is going to be build -bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile) -{ - if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ))) - return false; - AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ); - return true; -} - - -// Build station -// Params: -// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station -// tile : tile where station is going to be build -// length : in case of AI_TRAIN: length of station -// numtracks : in case of AI_TRAIN: tracks of station -// direction : the direction of the station -// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it) -int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag) -{ - if (type == AI_TRAIN) - return AI_DoCommand(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION); - - if (type == AI_BUS) - return AI_DoCommand(tile, direction, RS_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); - - return AI_DoCommand(tile, direction, RS_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); -} - - -// Builds a brdige. The second best out of the ones available for this player -// Params: -// tile_a : starting point -// tile_b : end point -// flag : flag passed to DoCommand -int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag) -{ - int bridge_type, bridge_len, type, type2; - - // Find a good bridgetype (the best money can buy) - bridge_len = GetBridgeLength(tile_a, tile_b); - type = type2 = 0; - for (bridge_type = MAX_BRIDGES-1; bridge_type >= 0; bridge_type--) { - if (CheckBridge_Stuff(bridge_type, bridge_len)) { - type2 = type; - type = bridge_type; - // We found two bridges, exit - if (type2 != 0) break; - } - } - // There is only one bridge that can be built - if (type2 == 0 && type != 0) type2 = type; - - // Now, simply, build the bridge! - if (p->ainew.tbt == AI_TRAIN) { - return AI_DoCommand(tile_a, tile_b, (0x00 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE); - } else { - return AI_DoCommand(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE); - } -} - - -// Build the route part by part -// Basicly what this function do, is build that amount of parts of the route -// that go in the same direction. It sets 'part' to the last part of the route builded. -// The return value is the cost for the builded parts -// -// Params: -// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder -// part : Which part we need to build -// -// TODO: skip already builded road-pieces (e.g.: cityroad) -int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag) -{ - int part = PathFinderInfo->position; - byte *route_extra = PathFinderInfo->route_extra; - TileIndex *route = PathFinderInfo->route; - int dir; - int old_dir = -1; - int cost = 0; - int res; - // We need to calculate the direction with the parent of the parent.. so we skip - // the first pieces and the last piece - if (part < 1) part = 1; - // When we are done, stop it - if (part >= PathFinderInfo->route_length - 1) { - PathFinderInfo->position = -2; - return 0; - } - - - if (PathFinderInfo->rail_or_road) { - // Tunnel code - if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) { - cost += AI_DoCommand(route[part], 0, 0, flag, CMD_BUILD_TUNNEL); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]); - return 0; - } - return cost; - } - // Bridge code - if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) { - cost += AiNew_Build_Bridge(p, route[part], route[part-1], flag); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part - 1]); - return 0; - } - return cost; - } - - // Build normal rail - // Keep it doing till we go an other way - if (route_extra[part - 1] == 0 && route_extra[part] == 0) { - while (route_extra[part] == 0) { - // Get the current direction - dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]); - // Is it the same as the last one? - if (old_dir != -1 && old_dir != dir) break; - old_dir = dir; - // Build the tile - res = AI_DoCommand(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(res)) { - // Problem.. let's just abort it all! - p->ainew.state = AI_STATE_NOTHING; - return 0; - } - cost += res; - // Go to the next tile - part++; - // Check if it is still in range.. - if (part >= PathFinderInfo->route_length - 1) break; - } - part--; - } - // We want to return the last position, so we go back one - PathFinderInfo->position = part; - } else { - // Tunnel code - if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) { - cost += AI_DoCommand(route[part], 0x200, 0, flag, CMD_BUILD_TUNNEL); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]); - return 0; - } - return cost; - } - // Bridge code - if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) { - cost += AiNew_Build_Bridge(p, route[part], route[part+1], flag); - PathFinderInfo->position++; - // TODO: problems! - if (CmdFailed(cost)) { - DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part + 1]); - return 0; - } - return cost; - } - - // Build normal road - // Keep it doing till we go an other way - // EnsureNoVehicle makes sure we don't build on a tile where a vehicle is. This way - // it will wait till the vehicle is gone.. - if (route_extra[part-1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) { - while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) { - // Get the current direction - dir = AiNew_GetRoadDirection(route[part-1], route[part], route[part+1]); - // Is it the same as the last one? - if (old_dir != -1 && old_dir != dir) break; - old_dir = dir; - // There is already some road, and it is a bridge.. don't build!!! - if (!IsTileType(route[part], MP_TUNNELBRIDGE)) { - // Build the tile - res = AI_DoCommand(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD); - // Currently, we ignore CMD_ERRORs! - if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_STREET) && !EnsureNoVehicle(route[part])) { - // Problem.. let's just abort it all! - DEBUG(ai, 0, "[BuidPath] route building failed at tile 0x%X, aborting", route[part]); - p->ainew.state = AI_STATE_NOTHING; - return 0; - } - - if (!CmdFailed(res)) cost += res; - } - // Go to the next tile - part++; - // Check if it is still in range.. - if (part >= PathFinderInfo->route_length - 1) break; - } - part--; - // We want to return the last position, so we go back one - } - if (!EnsureNoVehicle(route[part]) && flag == DC_EXEC) part--; - PathFinderInfo->position = part; - } - - return cost; -} - - -// This functions tries to find the best vehicle for this type of cargo -// It returns INVALID_ENGINE if not suitable engine is found -EngineID AiNew_PickVehicle(Player *p) -{ - if (p->ainew.tbt == AI_TRAIN) { - // Not supported yet - return INVALID_ENGINE; - } else { - EngineID best_veh_index = INVALID_ENGINE; - int32 best_veh_rating = 0; - EngineID start = ROAD_ENGINES_INDEX; - EngineID end = ROAD_ENGINES_INDEX + NUM_ROAD_ENGINES; - EngineID i; - - /* Loop through all road vehicles */ - for (i = start; i != end; i++) { - const RoadVehicleInfo *rvi = RoadVehInfo(i); - const Engine* e = GetEngine(i); - int32 rating; - int32 ret; - - /* Skip vehicles which can't take our cargo type */ - if (rvi->cargo_type != p->ainew.cargo && !CanRefitTo(i, p->ainew.cargo)) continue; - - // Is it availiable? - // Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY - if (!HASBIT(e->player_avail, _current_player) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue; - - /* Rate and compare the engine by speed & capacity */ - rating = rvi->max_speed * rvi->capacity; - if (rating <= best_veh_rating) continue; - - // Can we build it? - ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH); - if (CmdFailed(ret)) continue; - - best_veh_rating = rating; - best_veh_index = i; - } - - return best_veh_index; - } -} - - -void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - Player* p = GetPlayer(_current_player); - - if (success) { - p->ainew.state = AI_STATE_GIVE_ORDERS; - p->ainew.veh_id = _new_vehicle_id; - - if (GetVehicle(p->ainew.veh_id)->cargo_type != p->ainew.cargo) { - /* Cargo type doesn't match, so refit it */ - if (CmdFailed(DoCommand(tile, p->ainew.veh_id, p->ainew.cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) { - /* Refit failed, so sell the vehicle */ - DoCommand(tile, p->ainew.veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - p->ainew.state = AI_STATE_NOTHING; - } - } - } else { - /* XXX this should be handled more gracefully */ - p->ainew.state = AI_STATE_NOTHING; - } -} - - -// Builds the best vehicle possible -int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag) -{ - EngineID i = AiNew_PickVehicle(p); - - if (i == INVALID_ENGINE) return CMD_ERROR; - if (p->ainew.tbt == AI_TRAIN) return CMD_ERROR; - - if (flag & DC_EXEC) { - return AI_DoCommandCc(tile, i, 0, flag, CMD_BUILD_ROAD_VEH, CcAI); - } else { - return AI_DoCommand(tile, i, 0, flag, CMD_BUILD_ROAD_VEH); - } -} - -int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag) -{ - int ret, ret2; - if (p->ainew.tbt == AI_TRAIN) { - return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT); - } else { - ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT); - if (CmdFailed(ret)) return ret; - // Try to build the road from the depot - ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - // If it fails, ignore it.. - if (CmdFailed(ret2)) return ret; - return ret + ret2; - } -} diff --git a/ai/trolly/pathfinder.c b/ai/trolly/pathfinder.c deleted file mode 100644 --- a/ai/trolly/pathfinder.c +++ /dev/null @@ -1,510 +0,0 @@ -/* $Id$ */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../bridge_map.h" -#include "../../debug.h" -#include "../../functions.h" -#include "../../map.h" -#include "../../tile.h" -#include "../../command.h" -#include "trolly.h" -#include "../../depot.h" -#include "../../tunnel_map.h" -#include "../../bridge.h" -#include "../ai.h" - -#define TEST_STATION_NO_DIR 0xFF - -// Tests if a station can be build on the given spot -// TODO: make it train compatible -static bool TestCanBuildStationHere(TileIndex tile, byte dir) -{ - Player *p = GetPlayer(_current_player); - - if (dir == TEST_STATION_NO_DIR) { - int32 ret; - // TODO: currently we only allow spots that can be access from al 4 directions... - // should be fixed!!! - for (dir = 0; dir < 4; dir++) { - ret = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST); - if (!CmdFailed(ret)) return true; - } - return false; - } - - // return true if command succeeded, so the inverse of CmdFailed() - return !CmdFailed(AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST)); -} - - -static bool IsRoad(TileIndex tile) -{ - return - // MP_STREET, but not a road depot? - (IsTileType(tile, MP_STREET) && !IsTileDepotType(tile, TRANSPORT_ROAD)) || - (IsTileType(tile, MP_TUNNELBRIDGE) && ( - (IsTunnel(tile) && GetTunnelTransportType(tile) == TRANSPORT_ROAD) || - (IsBridge(tile) && GetBridgeTransportType(tile) == TRANSPORT_ROAD) - )); -} - - -// Checks if a tile 'a' is between the tiles 'b' and 'c' -#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c)) - - -// Check if the current tile is in our end-area -static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current) -{ - const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target; - - // It is not allowed to have a station on the end of a bridge or tunnel ;) - if (current->path.node.user_data[0] != 0) return AYSTAR_DONE; - if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) - if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES)) - if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile))) - return AYSTAR_FOUND_END_NODE; - - return AYSTAR_DONE; -} - - -// Calculates the hash -// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64) -static uint AiPathFinder_Hash(uint key1, uint key2) -{ - return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5); -} - - -// Clear the memory of all the things -static void AyStar_AiPathFinder_Free(AyStar *aystar) -{ - AyStarMain_Free(aystar); - free(aystar); -} - - -static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current); -static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current); - - -// This creates the AiPathFinder -AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo) -{ - PathNode start_node; - uint x; - uint y; - // Create AyStar - AyStar *result = malloc(sizeof(AyStar)); - init_AyStar(result, AiPathFinder_Hash, 1 << 10); - // Set the function pointers - result->CalculateG = AyStar_AiPathFinder_CalculateG; - result->CalculateH = AyStar_AiPathFinder_CalculateH; - result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck; - result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode; - result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours; - - result->free = AyStar_AiPathFinder_Free; - - // Set some information - result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK; - result->max_path_cost = 0; - result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES; - - // Set the user_data to the PathFinderInfo - result->user_target = PathFinderInfo; - - // Set the start node - start_node.parent = NULL; - start_node.node.direction = 0; - start_node.node.user_data[0] = 0; - - // Now we add all the starting tiles - for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) { - for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) { - start_node.node.tile = TileXY(x, y); - result->addstart(result, &start_node.node, 0); - } - } - - return result; -} - - -// To reuse AyStar we sometimes have to clean all the memory -void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo) -{ - PathNode start_node; - uint x; - uint y; - - aystar->clear(aystar); - - // Set the user_data to the PathFinderInfo - aystar->user_target = PathFinderInfo; - - // Set the start node - start_node.parent = NULL; - start_node.node.direction = 0; - start_node.node.user_data[0] = 0; - start_node.node.tile = PathFinderInfo->start_tile_tl; - - // Now we add all the starting tiles - for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) { - for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) { - TileIndex tile = TileXY(x, y); - - if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue; - if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue; - start_node.node.tile = tile; - aystar->addstart(aystar, &start_node.node, 0); - } - } -} - - -// The h-value, simple calculation -static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) -{ - const Ai_PathFinderInfo* PathFinderInfo = aystar->user_target; - int r, r2; - - if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) { - // The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate - r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction)); - r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction)); - } else { - // No direction, so just get the fastest route to the station - r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl); - r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br); - } - // See if the bottomright is faster than the topleft.. - if (r2 < r) r = r2; - return r * AI_PATHFINDER_H_MULTIPLER; -} - - -// We found the end.. let's get the route back and put it in an array -static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current) -{ - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - uint i = 0; - PathNode *parent = ¤t->path; - - do { - PathFinderInfo->route_extra[i] = parent->node.user_data[0]; - PathFinderInfo->route[i++] = parent->node.tile; - if (i > lengthof(PathFinderInfo->route)) { - // We ran out of space for the PathFinder - DEBUG(ai, 0, "No more space in pathfinder route[] array"); - PathFinderInfo->route_length = -1; // -1 indicates out of space - return; - } - parent = parent->parent; - } while (parent != NULL); - PathFinderInfo->route_length = i; - DEBUG(ai, 1, "Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash)); -} - - -// What tiles are around us. -static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current) -{ - uint i; - int ret; - int dir; - - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - - aystar->num_neighbours = 0; - - // Go through all surrounding tiles and check if they are within the limits - for (i = 0; i < 4; i++) { - TileIndex ctile = current->path.node.tile; // Current tile - TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile - - if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 && - TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) { - // We also directly test if the current tile can connect to this tile.. - // We do this simply by just building the tile! - - // If the next step is a bridge, we have to enter it the right way - if (!PathFinderInfo->rail_or_road && IsRoad(atile)) { - if (IsTileType(atile, MP_TUNNELBRIDGE)) { - if (IsTunnel(atile)) { - if (GetTunnelDirection(atile) != i) continue; - } else { - if ((_m[atile].m5 & 1U) != DiagDirToAxis(i)) continue; - } - } - } - // But also if we are on a bridge, we can only move a certain direction - if (!PathFinderInfo->rail_or_road && IsRoad(ctile)) { - if (IsTileType(ctile, MP_TUNNELBRIDGE)) { - // An existing bridge/tunnel... let's test the direction ;) - if ((_m[ctile].m5 & 1U) != (i & 1)) continue; - } - } - - if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 || - (AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) { - // We are a bridge/tunnel, how cool!! - // This means we can only point forward.. get the direction from the user_data - if (i != (current->path.node.user_data[0] >> 8)) continue; - } - dir = 0; - - // First, check if we have a parent - if (current->path.parent == NULL && current->path.node.user_data[0] == 0) { - // If not, this means we are at the starting station - if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) { - // We do need a direction? - if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) { - // We are not pointing the right way, invalid tile - continue; - } - } - } else if (current->path.node.user_data[0] == 0) { - if (PathFinderInfo->rail_or_road) { - // Rail check - dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile); - ret = AI_DoCommand(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL); - if (CmdFailed(ret)) continue; -#ifdef AI_PATHFINDER_NO_90DEGREES_TURN - if (current->path.parent->parent != NULL) { - // Check if we don't make a 90degree curve - int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile); - if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) { - continue; - } - } -#endif - } else { - // Road check - dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile); - if (IsRoad(ctile)) { - if (IsTileType(ctile, MP_TUNNELBRIDGE)) { - // We have a bridge, how nicely! We should mark it... - dir = 0; - } else { - // It already has road.. check if we miss any bits! - if ((_m[ctile].m5 & dir) != dir) { - // We do miss some pieces :( - dir &= ~_m[ctile].m5; - } else { - dir = 0; - } - } - } - // Only destruct things if it is MP_CLEAR of MP_TREES - if (dir != 0) { - ret = AI_DoCommand(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD); - if (CmdFailed(ret)) continue; - } - } - } - - // The tile can be connected - aystar->neighbours[aystar->num_neighbours].tile = atile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = 0; - aystar->neighbours[aystar->num_neighbours++].direction = 0; - } - } - - // Next step, check for bridges and tunnels - if (current->path.parent != NULL && current->path.node.user_data[0] == 0) { - // First we get the dir from this tile and his parent - DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile); - // It means we can only walk with the track, so the bridge has to be in the same direction - TileIndex tile = current->path.node.tile; - TileIndex new_tile = tile; - Slope tileh = GetTileSlope(tile, NULL); - - // Bridges can only be build on land that is not flat - // And if there is a road or rail blocking - if (tileh != SLOPE_FLAT || - (PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_STREET)) || - (!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) { - for (;;) { - new_tile += TileOffsByDiagDir(dir); - - // Precheck, is the length allowed? - if (!CheckBridge_Stuff(0, GetBridgeLength(tile, new_tile))) break; - - // Check if we hit the station-tile.. we don't like that! - if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break; - - // Try building the bridge.. - ret = AI_DoCommand(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE); - if (CmdFailed(ret)) continue; - // We can build a bridge here.. add him to the neighbours - aystar->neighbours[aystar->num_neighbours].tile = new_tile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8); - aystar->neighbours[aystar->num_neighbours++].direction = 0; - // We can only have 12 neighbours, and we need 1 left for tunnels - if (aystar->num_neighbours == 11) break; - } - } - - // Next, check for tunnels! - // Tunnels can only be built on slopes corresponding to the direction - // For now, we check both sides for this tile.. terraforming gives fuzzy result - if ((dir == DIAGDIR_NE && tileh == SLOPE_NE) || - (dir == DIAGDIR_SE && tileh == SLOPE_SE) || - (dir == DIAGDIR_SW && tileh == SLOPE_SW) || - (dir == DIAGDIR_NW && tileh == SLOPE_NW)) { - // Now simply check if a tunnel can be build - ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL); - tileh = GetTileSlope(_build_tunnel_endtile, NULL); - if (!CmdFailed(ret) && (tileh == SLOPE_SW || tileh == SLOPE_SE || tileh == SLOPE_NW || tileh == SLOPE_NE)) { - aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile; - aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8); - aystar->neighbours[aystar->num_neighbours++].direction = 0; - } - } - } -} - - -extern uint GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c -extern uint GetRoadFoundation(Slope tileh, uint bits); // XXX function declaration in .c -extern uint GetBridgeFoundation(Slope tileh, Axis); // XXX function declaration in .c -enum { - BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12, -}; - -// The most important function: it calculates the g-value -static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent) -{ - Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target; - int r, res = 0; - Slope tileh = GetTileSlope(current->tile, NULL); - Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL); - - // Check if we hit the end-tile - if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) { - // We are at the end-tile, check if we had a direction or something... - if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction) { - // We are not pointing the right way, invalid tile - return AYSTAR_INVALID_NODE; - } - // If it was valid, drop out.. we don't build on the endtile - return 0; - } - - // Give everything a small penalty - res += AI_PATHFINDER_PENALTY; - - if (!PathFinderInfo->rail_or_road) { - // Road has the lovely advantage it can use other road... check if - // the current tile is road, and if so, give a good bonus - if (IsRoad(current->tile)) { - res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS; - } - } - - // We should give a penalty when the tile is going up or down.. this is one way to do so! - // Too bad we have to count it from the parent.. but that is not so bad. - // We also dislike long routes on slopes, since they do not look too realistic - // when there is a flat land all around, they are more expensive to build, and - // especially they essentially block the ability to connect or cross the road - // from one side. - if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) { - // Skip if the tile was from a bridge or tunnel - if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) { - if (PathFinderInfo->rail_or_road) { - r = GetRailFoundation(parent_tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile)); - // Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information.. - if (r >= 15 || (r == 0 && HASBIT(BRIDGE_NO_FOUNDATION, tileh))) { - res += AI_PATHFINDER_TILE_GOES_UP_PENALTY; - } else { - res += AI_PATHFINDER_FOUNDATION_PENALTY; - } - } else { - if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) { - r = GetRoadFoundation(parent_tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile)); - if (r >= 15 || r == 0) { - res += AI_PATHFINDER_TILE_GOES_UP_PENALTY; - } else { - res += AI_PATHFINDER_FOUNDATION_PENALTY; - } - } - } - } - } - - // Are we part of a tunnel? - if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) { - // Tunnels are very expensive when build on long routes.. - // Ironicly, we are using BridgeCode here ;) - r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile); - res += r + (r >> 8); - } - - // Are we part of a bridge? - if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) { - // That means for every length a penalty - res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile); - // Check if we are going up or down, first for the starting point - // In user_data[0] is at the 8th bit the direction - if (!HASBIT(BRIDGE_NO_FOUNDATION, parent_tileh)) { - if (GetBridgeFoundation(parent_tileh, (current->user_data[0] >> 8) & 1) < 15) { - res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - } - } - // Second for the end point - if (!HASBIT(BRIDGE_NO_FOUNDATION, tileh)) { - if (GetBridgeFoundation(tileh, (current->user_data[0] >> 8) & 1) < 15) { - res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - } - } - if (parent_tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - if (tileh == SLOPE_FLAT) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY; - } - - // To prevent the AI from taking the fastest way in tiles, but not the fastest way - // in speed, we have to give a good penalty to direction changing - // This way, we get almost the fastest way in tiles, and a very good speed on the track - if (!PathFinderInfo->rail_or_road) { - if (parent->path.parent != NULL && - AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) { - // When road exists, we don't like turning, but its free, so don't be to piggy about it - if (IsRoad(parent->path.node.tile)) { - res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY; - } else { - res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY; - } - } - } else { - // For rail we have 1 exeption: diagonal rail.. - // So we fetch 2 raildirection. That of the current one, and of the one before that - if (parent->path.parent != NULL && parent->path.parent->parent != NULL) { - int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile); - int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile); - // First, see if we are on diagonal path, that is better than straight path - if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS; - - // First see if they are different - if (dir1 != dir2) { - // dir 2 and 3 are 1 diagonal track, and 4 and 5. - if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) { - // It is not, so we changed of direction - res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY; - } - if (parent->path.parent->parent->parent != NULL) { - int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile); - // Check if we changed 3 tiles of direction in 3 tiles.. bad!!! - if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) { - res += AI_PATHFINDER_CURVE_PENALTY; - } - } - } - } - } - - return (res < 0) ? 0 : res; -} diff --git a/ai/trolly/shared.c b/ai/trolly/shared.c deleted file mode 100644 --- a/ai/trolly/shared.c +++ /dev/null @@ -1,118 +0,0 @@ -/* $Id$ */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../map.h" -#include "trolly.h" -#include "../../vehicle.h" - -int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c) -{ - // 0 = vert - // 1 = horz - // 2 = dig up-left - // 3 = dig down-right - // 4 = dig down-left - // 5 = dig up-right - - uint x1 = TileX(tile_a); - uint x2 = TileX(tile_b); - uint x3 = TileX(tile_c); - - uint y1 = TileY(tile_a); - uint y2 = TileY(tile_b); - uint y3 = TileY(tile_c); - - if (y1 == y2 && y2 == y3) return 0; - if (x1 == x2 && x2 == x3) return 1; - if (y2 > y1) return x2 > x3 ? 2 : 4; - if (x2 > x1) return y2 > y3 ? 2 : 5; - if (y1 > y2) return x2 > x3 ? 5 : 3; - if (x1 > x2) return y2 > y3 ? 4 : 3; - - return 0; -} - -int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c) -{ - int x1, x2, x3; - int y1, y2, y3; - int r; - - x1 = TileX(tile_a); - x2 = TileX(tile_b); - x3 = TileX(tile_c); - - y1 = TileY(tile_a); - y2 = TileY(tile_b); - y3 = TileY(tile_c); - - r = 0; - - if (x1 < x2) r += 8; - if (y1 < y2) r += 1; - if (x1 > x2) r += 2; - if (y1 > y2) r += 4; - - if (x2 < x3) r += 2; - if (y2 < y3) r += 4; - if (x2 > x3) r += 8; - if (y2 > y3) r += 1; - - return r; -} - -// Get's the direction between 2 tiles seen from tile_a -DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b) -{ - if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE; - if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW; - if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW; - return DIAGDIR_NE; -} - - -// This functions looks up if this vehicle is special for this AI -// and returns his flag -uint AiNew_GetSpecialVehicleFlag(Player* p, Vehicle* v) -{ - uint i; - - for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) { - if (p->ainew.special_vehicles[i].veh_id == v->index) { - return p->ainew.special_vehicles[i].flag; - } - } - - // Not found :( - return 0; -} - - -bool AiNew_SetSpecialVehicleFlag(Player* p, Vehicle* v, uint flag) -{ - int new_id = -1; - uint i; - - for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) { - if (p->ainew.special_vehicles[i].veh_id == v->index) { - p->ainew.special_vehicles[i].flag |= flag; - return true; - } - if (new_id == -1 && - p->ainew.special_vehicles[i].veh_id == 0 && - p->ainew.special_vehicles[i].flag == 0) { - new_id = i; - } - } - - // Out of special_vehicle spots :s - if (new_id == -1) { - DEBUG(ai, 1, "special_vehicles list is too small"); - return false; - } - p->ainew.special_vehicles[new_id].veh_id = v->index; - p->ainew.special_vehicles[new_id].flag = flag; - return true; -} diff --git a/ai/trolly/trolly.c b/ai/trolly/trolly.c deleted file mode 100644 --- a/ai/trolly/trolly.c +++ /dev/null @@ -1,1353 +0,0 @@ -/* $Id$ */ - -/* - * This AI was created as a direct reaction to the big demand for some good AIs - * in OTTD. Too bad it never left alpha-stage, and it is considered dead in its - * current form. - * By the time of writing this, we, the creator of this AI and a good friend of - * mine, are designing a whole new AI-system that allows us to create AIs - * easier and without all the fuzz we encountered while I was working on this - * AI. By the time that system is finished, you can expect that this AI will - * dissapear, because it is pretty obselete and bad programmed. - * - * Meanwhile I wish you all much fun with this AI; if you are interested as - * AI-developer in this AI, I advise you not stare too long to some code, some - * things in here really are... strange ;) But in either way: enjoy :) - * - * -- TrueLight :: 2005-09-01 - */ - -#include "../../stdafx.h" -#include "../../openttd.h" -#include "../../debug.h" -#include "../../functions.h" -#include "../../road_map.h" -#include "../../station_map.h" -#include "../../table/strings.h" -#include "../../map.h" -#include "../../tile.h" -#include "../../command.h" -#include "trolly.h" -#include "../../town.h" -#include "../../industry.h" -#include "../../station.h" -#include "../../engine.h" -#include "../../gui.h" -#include "../../depot.h" -#include "../../vehicle.h" -#include "../../date.h" -#include "../ai.h" - -// This function is called after StartUp. It is the init of an AI -static void AiNew_State_FirstTime(Player *p) -{ - // This assert is used to protect those function from misuse - // You have quickly a small mistake in the state-array - // With that, everything would go wrong. Finding that, is almost impossible - // With this assert, that problem can never happen. - assert(p->ainew.state == AI_STATE_FIRST_TIME); - // We first have to init some things - - if (_current_player == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0); - - // The PathFinder (AyStar) - // TODO: Maybe when an AI goes bankrupt, this is de-init - // or when coming from a savegame.. should be checked out! - p->ainew.path_info.start_tile_tl = 0; - p->ainew.path_info.start_tile_br = 0; - p->ainew.path_info.end_tile_tl = 0; - p->ainew.path_info.end_tile_br = 0; - p->ainew.pathfinder = new_AyStar_AiPathFinder(12, &p->ainew.path_info); - - p->ainew.idle = 0; - p->ainew.last_vehiclecheck_date = _date; - - // We ALWAYS start with a bus route.. just some basic money ;) - p->ainew.action = AI_ACTION_BUS_ROUTE; - - // Let's popup the news, and after that, start building.. - p->ainew.state = AI_STATE_WAKE_UP; -} - - -// This function just waste some time -// It keeps it more real. The AI can build on such tempo no normal user -// can ever keep up with that. The competitor_speed already delays a bit -// but after the AI finished a track it really needs to go to sleep. -// -// Let's say, we sleep between one and three days if the AI is put on Very Fast. -// This means that on Very Slow it will be between 16 and 48 days.. slow enough? -static void AiNew_State_Nothing(Player *p) -{ - assert(p->ainew.state == AI_STATE_NOTHING); - // If we are done idling, start over again - if (p->ainew.idle == 0) p->ainew.idle = AI_RandomRange(DAY_TICKS * 2) + DAY_TICKS; - if (--p->ainew.idle == 0) { - // We are done idling.. what you say? Let's do something! - // I mean.. the next tick ;) - p->ainew.state = AI_STATE_WAKE_UP; - } -} - - -// This function picks out a task we are going to do. -// Currently supported: -// - Make new route -// - Check route -// - Build HQ -static void AiNew_State_WakeUp(Player *p) -{ - int32 money; - int c; - assert(p->ainew.state == AI_STATE_WAKE_UP); - // First, check if we have a HQ - if (p->location_of_house == 0) { - // We have no HQ yet, build one on a random place - // Random till we found a place for it! - // TODO: this should not be on a random place.. - AiNew_Build_CompanyHQ(p, AI_Random() % MapSize()); - // Enough for now, but we want to come back here the next time - // so we do not change any status - return; - } - - money = p->player_money - AI_MINIMUM_MONEY; - - // Let's pick an action! - if (p->ainew.action == AI_ACTION_NONE) { - c = AI_Random() & 0xFF; - if (p->current_loan > 0 && - p->old_economy[1].income > AI_MINIMUM_INCOME_FOR_LOAN && - c < 10) { - p->ainew.action = AI_ACTION_REPAY_LOAN; - } else if (p->ainew.last_vehiclecheck_date + AI_DAYS_BETWEEN_VEHICLE_CHECKS < _date) { - // Check all vehicles once in a while - p->ainew.action = AI_ACTION_CHECK_ALL_VEHICLES; - p->ainew.last_vehiclecheck_date = _date; - } else if (c < 100 && !_patches.ai_disable_veh_roadveh) { - // Do we have any spots for road-vehicles left open? - if (GetFreeUnitNumber(VEH_Road) <= _patches.max_roadveh) { - if (c < 85) { - p->ainew.action = AI_ACTION_TRUCK_ROUTE; - } else { - p->ainew.action = AI_ACTION_BUS_ROUTE; - } - } -#if 0 - } else if (c < 200 && !_patches.ai_disable_veh_train) { - if (GetFreeUnitNumber(VEH_Train) <= _patches.max_trains) { - p->ainew.action = AI_ACTION_TRAIN_ROUTE; - } -#endif - } - - p->ainew.counter = 0; - } - - if (p->ainew.counter++ > AI_MAX_TRIES_FOR_SAME_ROUTE) { - p->ainew.action = AI_ACTION_NONE; - return; - } - - if (_patches.ai_disable_veh_roadveh && ( - p->ainew.action == AI_ACTION_BUS_ROUTE || - p->ainew.action == AI_ACTION_TRUCK_ROUTE - )) { - p->ainew.action = AI_ACTION_NONE; - return; - } - - if (p->ainew.action == AI_ACTION_REPAY_LOAN && - money > AI_MINIMUM_LOAN_REPAY_MONEY) { - // We start repaying some money.. - p->ainew.state = AI_STATE_REPAY_MONEY; - return; - } - - if (p->ainew.action == AI_ACTION_CHECK_ALL_VEHICLES) { - p->ainew.state = AI_STATE_CHECK_ALL_VEHICLES; - return; - } - - // It is useless to start finding a route if we don't have enough money - // to build the route anyway.. - if (p->ainew.action == AI_ACTION_BUS_ROUTE && - money > AI_MINIMUM_BUS_ROUTE_MONEY) { - if (GetFreeUnitNumber(VEH_Road) > _patches.max_roadveh) { - p->ainew.action = AI_ACTION_NONE; - return; - } - p->ainew.cargo = AI_NEED_CARGO; - p->ainew.state = AI_STATE_LOCATE_ROUTE; - p->ainew.tbt = AI_BUS; // Bus-route - return; - } - if (p->ainew.action == AI_ACTION_TRUCK_ROUTE && - money > AI_MINIMUM_TRUCK_ROUTE_MONEY) { - if (GetFreeUnitNumber(VEH_Road) > _patches.max_roadveh) { - p->ainew.action = AI_ACTION_NONE; - return; - } - p->ainew.cargo = AI_NEED_CARGO; - p->ainew.last_id = 0; - p->ainew.state = AI_STATE_LOCATE_ROUTE; - p->ainew.tbt = AI_TRUCK; - return; - } - - p->ainew.state = AI_STATE_NOTHING; -} - - -static void AiNew_State_ActionDone(Player *p) -{ - p->ainew.action = AI_ACTION_NONE; - p->ainew.state = AI_STATE_NOTHING; -} - - -// Check if a city or industry is good enough to start a route there -static bool AiNew_Check_City_or_Industry(Player *p, int ic, byte type) -{ - if (type == AI_CITY) { - const Town* t = GetTown(ic); - const Station* st; - uint count = 0; - int j = 0; - - // We don't like roadconstructions, don't even true such a city - if (t->road_build_months != 0) return false; - - // Check if the rating in a city is high enough - // If not, take a chance if we want to continue - if (t->ratings[_current_player] < 0 && AI_CHANCE16(1,4)) return false; - - if (t->max_pass - t->act_pass < AI_CHECKCITY_NEEDED_CARGO && !AI_CHANCE16(1,AI_CHECKCITY_CITY_CHANCE)) return false; - - // Check if we have build a station in this town the last 6 months - // else we don't do it. This is done, because stat updates can be slow - // and sometimes it takes up to 4 months before the stats are corectly. - // This way we don't get 12 busstations in one city of 100 population ;) - FOR_ALL_STATIONS(st) { - // Do we own it? - if (st->owner == _current_player) { - // Are we talking busses? - if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) != FACIL_BUS_STOP) continue; - // Is it the same city as we are in now? - if (st->town != t) continue; - // When was this station build? - if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false; - // Cound the amount of stations in this city that we own - count++; - } else { - // We do not own it, request some info about the station - // we want to know if this station gets the same good. If so, - // we want to know its rating. If it is too high, we are not going - // to build there - if (!st->goods[CT_PASSENGERS].last_speed) continue; - // Is it around our city - if (DistanceManhattan(st->xy, t->xy) > 10) continue; - // It does take this cargo.. what is his rating? - if (st->goods[CT_PASSENGERS].rating < AI_CHECKCITY_CARGO_RATING) continue; - j++; - // When this is the first station, we build a second with no problem ;) - if (j == 1) continue; - // The rating is high.. second station... - // a little chance that we still continue - // But if there are 3 stations of this size, we never go on... - if (j == 2 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue; - // We don't like this station :( - return false; - } - } - - // We are about to add one... - count++; - // Check if we the city can provide enough cargo for this amount of stations.. - if (count * AI_CHECKCITY_CARGO_PER_STATION > t->max_pass) return false; - - // All check are okay, so we can build here! - return true; - } - if (type == AI_INDUSTRY) { - const Industry* i = GetIndustry(ic); - const Station* st; - int count = 0; - int j = 0; - - if (i->town != NULL && i->town->ratings[_current_player] < 0 && AI_CHANCE16(1,4)) return false; - - // No limits on delevering stations! - // Or for industry that does not give anything yet - if (i->produced_cargo[0] == CT_INVALID || i->total_production[0] == 0) return true; - - if (i->total_production[0] - i->total_transported[0] < AI_CHECKCITY_NEEDED_CARGO) return false; - - // Check if we have build a station in this town the last 6 months - // else we don't do it. This is done, because stat updates can be slow - // and sometimes it takes up to 4 months before the stats are corectly. - FOR_ALL_STATIONS(st) { - // Do we own it? - if (st->owner == _current_player) { - // Are we talking trucks? - if (p->ainew.tbt == AI_TRUCK && (FACIL_TRUCK_STOP & st->facilities) != FACIL_TRUCK_STOP) continue; - // Is it the same city as we are in now? - if (st->town != i->town) continue; - // When was this station build? - if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false; - // Cound the amount of stations in this city that we own - count++; - } else { - // We do not own it, request some info about the station - // we want to know if this station gets the same good. If so, - // we want to know its rating. If it is too high, we are not going - // to build there - if (i->produced_cargo[0] == CT_INVALID) continue; - // It does not take this cargo - if (!st->goods[i->produced_cargo[0]].last_speed) continue; - // Is it around our industry - if (DistanceManhattan(st->xy, i->xy) > 5) continue; - // It does take this cargo.. what is his rating? - if (st->goods[i->produced_cargo[0]].rating < AI_CHECKCITY_CARGO_RATING) continue; - j++; - // The rating is high.. a little chance that we still continue - // But if there are 2 stations of this size, we never go on... - if (j == 1 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue; - // We don't like this station :( - return false; - } - } - - // We are about to add one... - count++; - // Check if we the city can provide enough cargo for this amount of stations.. - if (count * AI_CHECKCITY_CARGO_PER_STATION > i->total_production[0]) return false; - - // All check are okay, so we can build here! - return true; - } - - return true; -} - - -// This functions tries to locate a good route -static void AiNew_State_LocateRoute(Player *p) -{ - assert(p->ainew.state == AI_STATE_LOCATE_ROUTE); - // For now, we only support PASSENGERS, CITY and BUSSES - - // We don't have a route yet - if (p->ainew.cargo == AI_NEED_CARGO) { - p->ainew.new_cost = 0; // No cost yet - p->ainew.temp = -1; - // Reset the counter - p->ainew.counter = 0; - - p->ainew.from_ic = -1; - p->ainew.to_ic = -1; - if (p->ainew.tbt == AI_BUS) { - // For now we only have a passenger route - p->ainew.cargo = CT_PASSENGERS; - - // Find a route to cities - p->ainew.from_type = AI_CITY; - p->ainew.to_type = AI_CITY; - } else if (p->ainew.tbt == AI_TRUCK) { - p->ainew.cargo = AI_NO_CARGO; - - p->ainew.from_type = AI_INDUSTRY; - p->ainew.to_type = AI_INDUSTRY; - } - - // Now we are doing initing, we wait one tick - return; - } - - // Increase the counter and abort if it is taking too long! - p->ainew.counter++; - if (p->ainew.counter > AI_LOCATE_ROUTE_MAX_COUNTER) { - // Switch back to doing nothing! - p->ainew.state = AI_STATE_NOTHING; - return; - } - - // We are going to locate a city from where we are going to connect - if (p->ainew.from_ic == -1) { - if (p->ainew.temp == -1) { - // First, we pick a random spot to search from - if (p->ainew.from_type == AI_CITY) { - p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1); - } else { - p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1); - } - } - - if (!AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.from_type)) { - // It was not a valid city - // increase the temp with one, and return. We will come back later here - // to try again - p->ainew.temp++; - if (p->ainew.from_type == AI_CITY) { - if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0; - } else { - if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0; - } - - // Don't do an attempt if we are trying the same id as the last time... - if (p->ainew.last_id == p->ainew.temp) return; - p->ainew.last_id = p->ainew.temp; - - return; - } - - // We found a good city/industry, save the data of it - p->ainew.from_ic = p->ainew.temp; - - // Start the next tick with finding a to-city - p->ainew.temp = -1; - return; - } - - // Find a to-city - if (p->ainew.temp == -1) { - // First, we pick a random spot to search to - if (p->ainew.to_type == AI_CITY) { - p->ainew.temp = AI_RandomRange(GetMaxTownIndex() + 1); - } else { - p->ainew.temp = AI_RandomRange(GetMaxIndustryIndex() + 1); - } - } - - // The same city is not allowed - // Also check if the city is valid - if (p->ainew.temp != p->ainew.from_ic && AiNew_Check_City_or_Industry(p, p->ainew.temp, p->ainew.to_type)) { - // Maybe it is valid.. - - /* We need to know if they are not to far apart from eachother.. - * We do that by checking how much cargo we have to move and how long the - * route is. - */ - - if (p->ainew.from_type == AI_CITY && p->ainew.tbt == AI_BUS) { - const Town* town_from = GetTown(p->ainew.from_ic); - const Town* town_temp = GetTown(p->ainew.temp); - uint distance = DistanceManhattan(town_from->xy, town_temp->xy); - int max_cargo; - - max_cargo = town_from->max_pass + town_temp->max_pass; - max_cargo -= town_from->act_pass + town_temp->act_pass; - - // max_cargo is now the amount of cargo we can move between the two cities - // If it is more than the distance, we allow it - if (distance <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) { - // We found a good city/industry, save the data of it - p->ainew.to_ic = p->ainew.temp; - p->ainew.state = AI_STATE_FIND_STATION; - - DEBUG(ai, 1, "[LocateRoute] found bus-route of %d tiles long (from %d to %d)", - distance, - p->ainew.from_ic, - p->ainew.temp - ); - - p->ainew.from_tile = 0; - p->ainew.to_tile = 0; - - return; - } - } else if (p->ainew.tbt == AI_TRUCK) { - const Industry* ind_from = GetIndustry(p->ainew.from_ic); - const Industry* ind_temp = GetIndustry(p->ainew.temp); - bool found = false; - int max_cargo = 0; - uint i; - - // TODO: in max_cargo, also check other cargo (beside [0]) - // First we check if the from_ic produces cargo that this ic accepts - if (ind_from->produced_cargo[0] != CT_INVALID && ind_from->total_production[0] != 0) { - for (i = 0; i < lengthof(ind_temp->accepts_cargo); i++) { - if (ind_temp->accepts_cargo[i] == CT_INVALID) break; - if (ind_from->produced_cargo[0] == ind_temp->accepts_cargo[i]) { - // Found a compatible industry - max_cargo = ind_from->total_production[0] - ind_from->total_transported[0]; - found = true; - p->ainew.from_deliver = true; - p->ainew.to_deliver = false; - break; - } - } - } - if (!found && ind_temp->produced_cargo[0] != CT_INVALID && ind_temp->total_production[0] != 0) { - // If not check if the current ic produces cargo that the from_ic accepts - for (i = 0; i < lengthof(ind_from->accepts_cargo); i++) { - if (ind_from->accepts_cargo[i] == CT_INVALID) break; - if (ind_temp->produced_cargo[0] == ind_from->accepts_cargo[i]) { - // Found a compatbiel industry - found = true; - max_cargo = ind_temp->total_production[0] - ind_temp->total_transported[0]; - p->ainew.from_deliver = false; - p->ainew.to_deliver = true; - break; - } - } - } - if (found) { - // Yeah, they are compatible!!! - // Check the length against the amount of goods - uint distance = DistanceManhattan(ind_from->xy, ind_temp->xy); - - if (distance > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE && - distance <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) { - p->ainew.to_ic = p->ainew.temp; - if (p->ainew.from_deliver) { - p->ainew.cargo = ind_from->produced_cargo[0]; - } else { - p->ainew.cargo = ind_temp->produced_cargo[0]; - } - p->ainew.state = AI_STATE_FIND_STATION; - - DEBUG(ai, 1, "[LocateRoute] found truck-route of %d tiles long (from %d to %d)", - distance, - p->ainew.from_ic, - p->ainew.temp - ); - - p->ainew.from_tile = 0; - p->ainew.to_tile = 0; - - return; - } - } - } - } - - // It was not a valid city - // increase the temp with one, and return. We will come back later here - // to try again - p->ainew.temp++; - if (p->ainew.to_type == AI_CITY) { - if (p->ainew.temp > GetMaxTownIndex()) p->ainew.temp = 0; - } else { - if (p->ainew.temp > GetMaxIndustryIndex()) p->ainew.temp = 0; - } - - // Don't do an attempt if we are trying the same id as the last time... - if (p->ainew.last_id == p->ainew.temp) return; - p->ainew.last_id = p->ainew.temp; -} - - -// Check if there are not more than a certain amount of vehicles pointed to a certain -// station. This to prevent 10 busses going to one station, which gives... problems ;) -static bool AiNew_CheckVehicleStation(Player *p, Station *st) -{ - int count = 0; - Vehicle *v; - - // Also check if we don't have already a lot of busses to this city... - FOR_ALL_VEHICLES(v) { - if (v->owner == _current_player) { - const Order *order; - - FOR_VEHICLE_ORDERS(v, order) { - if (order->type == OT_GOTO_STATION && GetStation(order->dest) == st) { - // This vehicle has this city in its list - count++; - } - } - } - } - - if (count > AI_CHECK_MAX_VEHICLE_PER_STATION) return false; - return true; -} - -// This function finds a good spot for a station -static void AiNew_State_FindStation(Player *p) -{ - TileIndex tile; - Station *st; - int count = 0; - EngineID i; - TileIndex new_tile = 0; - byte direction = 0; - Town *town = NULL; - assert(p->ainew.state == AI_STATE_FIND_STATION); - - if (p->ainew.from_tile == 0) { - // First we scan for a station in the from-city - if (p->ainew.from_type == AI_CITY) { - town = GetTown(p->ainew.from_ic); - tile = town->xy; - } else { - tile = GetIndustry(p->ainew.from_ic)->xy; - } - } else if (p->ainew.to_tile == 0) { - // Second we scan for a station in the to-city - if (p->ainew.to_type == AI_CITY) { - town = GetTown(p->ainew.to_ic); - tile = town->xy; - } else { - tile = GetIndustry(p->ainew.to_ic)->xy; - } - } else { - // Unsupported request - // Go to FIND_PATH - p->ainew.temp = -1; - p->ainew.state = AI_STATE_FIND_PATH; - return; - } - - // First, we are going to look at the stations that already exist inside the city - // If there is enough cargo left in the station, we take that station - // If that is not possible, and there are more than 2 stations in the city, abort - i = AiNew_PickVehicle(p); - // Euhmz, this should not happen _EVER_ - // Quit finding a route... - if (i == INVALID_ENGINE) { - p->ainew.state = AI_STATE_NOTHING; - return; - } - - FOR_ALL_STATIONS(st) { - if (st->owner == _current_player) { - if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) { - if (st->town == town) { - // Check how much cargo there is left in the station - if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) { - if (AiNew_CheckVehicleStation(p, st)) { - // We did found a station that was good enough! - new_tile = st->xy; - direction = GetRoadStopDir(st->xy); - break; - } - } - count++; - } - } - } - } - // We are going to add a new station... - if (new_tile == 0) count++; - // No more than 2 stations allowed in a city - // This is because only the best 2 stations of one cargo do get any cargo - if (count > 2) { - p->ainew.state = AI_STATE_NOTHING; - return; - } - - if (new_tile == 0 && p->ainew.tbt == AI_BUS) { - uint x, y, i = 0; - int r; - uint best; - uint accepts[NUM_CARGO]; - TileIndex found_spot[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE*4]; - uint found_best[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE*4]; - // To find a good spot we scan a range from the center, a get the point - // where we get the most cargo and where it is buildable. - // TODO: also check for station of myself and make sure we are not - // taking eachothers passangers away (bad result when it does not) - for (x = TileX(tile) - AI_FINDSTATION_TILE_RANGE; x <= TileX(tile) + AI_FINDSTATION_TILE_RANGE; x++) { - for (y = TileY(tile) - AI_FINDSTATION_TILE_RANGE; y <= TileY(tile) + AI_FINDSTATION_TILE_RANGE; y++) { - new_tile = TileXY(x, y); - if (IsTileType(new_tile, MP_CLEAR) || IsTileType(new_tile, MP_TREES)) { - // This tile we can build on! - // Check acceptance - // XXX - Get the catchment area - GetAcceptanceAroundTiles(accepts, new_tile, 1, 1, 4); - // >> 3 == 0 means no cargo - if (accepts[p->ainew.cargo] >> 3 == 0) continue; - // See if we can build the station - r = AiNew_Build_Station(p, p->ainew.tbt, new_tile, 0, 0, 0, DC_QUERY_COST); - if (CmdFailed(r)) continue; - // We can build it, so add it to found_spot - found_spot[i] = new_tile; - found_best[i++] = accepts[p->ainew.cargo]; - } - } - } - - // If i is still zero, we did not find anything - if (i == 0) { - p->ainew.state = AI_STATE_NOTHING; - return; - } - - // Go through all the found_best and check which has the highest value - best = 0; - new_tile = 0; - - for (x = 0; x < i; x++) { - if (found_best[x] > best || - (found_best[x] == best && DistanceManhattan(tile, new_tile) > DistanceManhattan(tile, found_spot[x]))) { - new_tile = found_spot[x]; - best = found_best[x]; - } - } - - // See how much it is going to cost us... - r = AiNew_Build_Station(p, p->ainew.tbt, new_tile, 0, 0, 0, DC_QUERY_COST); - p->ainew.new_cost += r; - - direction = AI_PATHFINDER_NO_DIRECTION; - } else if (new_tile == 0 && p->ainew.tbt == AI_TRUCK) { - // Truck station locater works differently.. a station can be on any place - // as long as it is in range. So we give back code AI_STATION_RANGE - // so the pathfinder routine can work it out! - new_tile = AI_STATION_RANGE; - direction = AI_PATHFINDER_NO_DIRECTION; - } - - if (p->ainew.from_tile == 0) { - p->ainew.from_tile = new_tile; - p->ainew.from_direction = direction; - // Now we found thisone, go in for to_tile - return; - } else if (p->ainew.to_tile == 0) { - p->ainew.to_tile = new_tile; - p->ainew.to_direction = direction; - // K, done placing stations! - p->ainew.temp = -1; - p->ainew.state = AI_STATE_FIND_PATH; - return; - } -} - - -// We try to find a path between 2 points -static void AiNew_State_FindPath(Player *p) -{ - int r; - assert(p->ainew.state == AI_STATE_FIND_PATH); - - // First time, init some data - if (p->ainew.temp == -1) { - // Init path_info - if (p->ainew.from_tile == AI_STATION_RANGE) { - const Industry* i = GetIndustry(p->ainew.from_ic); - - // For truck routes we take a range around the industry - p->ainew.path_info.start_tile_tl = i->xy - TileDiffXY(1, 1); - p->ainew.path_info.start_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1); - p->ainew.path_info.start_direction = p->ainew.from_direction; - } else { - p->ainew.path_info.start_tile_tl = p->ainew.from_tile; - p->ainew.path_info.start_tile_br = p->ainew.from_tile; - p->ainew.path_info.start_direction = p->ainew.from_direction; - } - - if (p->ainew.to_tile == AI_STATION_RANGE) { - const Industry* i = GetIndustry(p->ainew.to_ic); - - p->ainew.path_info.end_tile_tl = i->xy - TileDiffXY(1, 1); - p->ainew.path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1); - p->ainew.path_info.end_direction = p->ainew.to_direction; - } else { - p->ainew.path_info.end_tile_tl = p->ainew.to_tile; - p->ainew.path_info.end_tile_br = p->ainew.to_tile; - p->ainew.path_info.end_direction = p->ainew.to_direction; - } - - p->ainew.path_info.rail_or_road = (p->ainew.tbt == AI_TRAIN); - - // First, clean the pathfinder with our new begin and endpoints - clean_AyStar_AiPathFinder(p->ainew.pathfinder, &p->ainew.path_info); - - p->ainew.temp = 0; - } - - // Start the pathfinder - r = p->ainew.pathfinder->main(p->ainew.pathfinder); - switch (r) { - case AYSTAR_NO_PATH: - DEBUG(ai, 1, "No route found by pathfinder"); - // Start all over again - p->ainew.state = AI_STATE_NOTHING; - break; - - case AYSTAR_FOUND_END_NODE: // We found the end-point - p->ainew.temp = -1; - p->ainew.state = AI_STATE_FIND_DEPOT; - break; - - // In any other case, we are still busy finding the route - default: break; - } -} - - -// This function tries to locate a good place for a depot! -static void AiNew_State_FindDepot(Player *p) -{ - // To place the depot, we walk through the route, and if we find a lovely spot (MP_CLEAR, MP_TREES), we place it there.. - // Simple, easy, works! - // To make the depot stand in the middle of the route, we start from the center.. - // But first we walk through the route see if we can find a depot that is ours - // this keeps things nice ;) - int g, i, r; - DiagDirection j; - TileIndex tile; - assert(p->ainew.state == AI_STATE_FIND_DEPOT); - - p->ainew.depot_tile = 0; - - for (i=2;iainew.path_info.route_length-2;i++) { - tile = p->ainew.path_info.route[i]; - for (j = 0; j < 4; j++) { - TileIndex t = tile + TileOffsByDiagDir(j); - - if (IsTileType(t, MP_STREET) && - GetRoadTileType(t) == ROAD_TILE_DEPOT && - IsTileOwner(t, _current_player) && - GetRoadDepotDirection(t) == ReverseDiagDir(j)) { - p->ainew.depot_tile = t; - p->ainew.depot_direction = ReverseDiagDir(j); - p->ainew.state = AI_STATE_VERIFY_ROUTE; - return; - } - } - } - - // This routine let depot finding start in the middle, and work his way to the stations - // It makes depot placing nicer :) - i = p->ainew.path_info.route_length / 2; - g = 1; - while (i > 1 && i < p->ainew.path_info.route_length - 2) { - i += g; - g *= -1; - (g < 0?g--:g++); - - if (p->ainew.path_info.route_extra[i] != 0 || p->ainew.path_info.route_extra[i+1] != 0) { - // Bridge or tunnel.. we can't place a depot there - continue; - } - - tile = p->ainew.path_info.route[i]; - - for (j = 0; j < 4; j++) { - TileIndex t = tile + TileOffsByDiagDir(j); - - // It may not be placed on the road/rail itself - // And because it is not build yet, we can't see it on the tile.. - // So check the surrounding tiles :) - if (t == p->ainew.path_info.route[i - 1] || - t == p->ainew.path_info.route[i + 1]) { - continue; - } - // Not around a bridge? - if (p->ainew.path_info.route_extra[i] != 0) continue; - if (IsTileType(tile, MP_TUNNELBRIDGE)) continue; - // Is the terrain clear? - if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) { - // If the current tile is on a slope then we do not allow this - if (GetTileSlope(tile, NULL) != SLOPE_FLAT) continue; - // Check if everything went okay.. - r = AiNew_Build_Depot(p, t, ReverseDiagDir(j), 0); - if (CmdFailed(r)) continue; - // Found a spot! - p->ainew.new_cost += r; - p->ainew.depot_tile = t; - p->ainew.depot_direction = ReverseDiagDir(j); // Reverse direction - p->ainew.state = AI_STATE_VERIFY_ROUTE; - return; - } - } - } - - // Failed to find a depot? - p->ainew.state = AI_STATE_NOTHING; -} - - -// This function calculates how many vehicles there are needed on this -// traject. -// It works pretty simple: get the length, see how much we move around -// and hussle that, and you know how many vehicles there are needed. -// It returns the cost for the vehicles -static int AiNew_HowManyVehicles(Player *p) -{ - if (p->ainew.tbt == AI_BUS) { - // For bus-routes we look at the time before we are back in the station - EngineID i; - int length, tiles_a_day; - int amount; - i = AiNew_PickVehicle(p); - if (i == INVALID_ENGINE) return 0; - // Passenger run.. how long is the route? - length = p->ainew.path_info.route_length; - // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done! - tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16; - // We want a vehicle in a station once a month at least, so, calculate it! - // (the * 2 is because we have 2 stations ;)) - amount = length * 2 * 2 / tiles_a_day / 30; - if (amount == 0) amount = 1; - return amount; - } else if (p->ainew.tbt == AI_TRUCK) { - // For truck-routes we look at the cargo - EngineID i; - int length, amount, tiles_a_day; - int max_cargo; - i = AiNew_PickVehicle(p); - if (i == INVALID_ENGINE) return 0; - // Passenger run.. how long is the route? - length = p->ainew.path_info.route_length; - // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done! - tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16; - if (p->ainew.from_deliver) { - max_cargo = GetIndustry(p->ainew.from_ic)->total_production[0]; - } else { - max_cargo = GetIndustry(p->ainew.to_ic)->total_production[0]; - } - - // This is because moving 60% is more than we can dream of! - max_cargo *= 0.6; - // We want all the cargo to be gone in a month.. so, we know the cargo it delivers - // we know what the vehicle takes with him, and we know the time it takes him - // to get back here.. now let's do some math! - amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity; - amount += 1; - return amount; - } else { - // Currently not supported - return 0; - } -} - - -// This function checks: -// - If the route went okay -// - Calculates the amount of money needed to build the route -// - Calculates how much vehicles needed for the route -static void AiNew_State_VerifyRoute(Player *p) -{ - int res, i; - assert(p->ainew.state == AI_STATE_VERIFY_ROUTE); - - // Let's calculate the cost of the path.. - // new_cost already contains the cost of the stations - p->ainew.path_info.position = -1; - - do { - p->ainew.path_info.position++; - p->ainew.new_cost += AiNew_Build_RoutePart(p, &p->ainew.path_info, DC_QUERY_COST); - } while (p->ainew.path_info.position != -2); - - // Now we know the price of build station + path. Now check how many vehicles - // we need and what the price for that will be - res = AiNew_HowManyVehicles(p); - // If res == 0, no vehicle was found, or an other problem did occour - if (res == 0) { - p->ainew.state = AI_STATE_NOTHING; - return; - } - p->ainew.amount_veh = res; - p->ainew.cur_veh = 0; - - // Check how much it it going to cost us.. - for (i=0;iainew.new_cost += AiNew_Build_Vehicle(p, 0, DC_QUERY_COST); - } - - // Now we know how much the route is going to cost us - // Check if we have enough money for it! - if (p->ainew.new_cost > p->player_money - AI_MINIMUM_MONEY) { - // Too bad.. - DEBUG(ai, 1, "Insufficient funds to build route (%d)", p->ainew.new_cost); - p->ainew.state = AI_STATE_NOTHING; - return; - } - - // Now we can build the route, check the direction of the stations! - if (p->ainew.from_direction == AI_PATHFINDER_NO_DIRECTION) { - p->ainew.from_direction = AiNew_GetDirection(p->ainew.path_info.route[p->ainew.path_info.route_length-1], p->ainew.path_info.route[p->ainew.path_info.route_length-2]); - } - if (p->ainew.to_direction == AI_PATHFINDER_NO_DIRECTION) { - p->ainew.to_direction = AiNew_GetDirection(p->ainew.path_info.route[0], p->ainew.path_info.route[1]); - } - if (p->ainew.from_tile == AI_STATION_RANGE) - p->ainew.from_tile = p->ainew.path_info.route[p->ainew.path_info.route_length-1]; - if (p->ainew.to_tile == AI_STATION_RANGE) - p->ainew.to_tile = p->ainew.path_info.route[0]; - - p->ainew.state = AI_STATE_BUILD_STATION; - p->ainew.temp = 0; - - DEBUG(ai, 1, "The route is set and buildable, building 0x%X to 0x%X...", p->ainew.from_tile, p->ainew.to_tile); -} - - -// Build the stations -static void AiNew_State_BuildStation(Player *p) -{ - int res = 0; - assert(p->ainew.state == AI_STATE_BUILD_STATION); - if (p->ainew.temp == 0) { - if (!IsTileType(p->ainew.from_tile, MP_STATION)) - res = AiNew_Build_Station(p, p->ainew.tbt, p->ainew.from_tile, 0, 0, p->ainew.from_direction, DC_EXEC); - } else { - if (!IsTileType(p->ainew.to_tile, MP_STATION)) - res = AiNew_Build_Station(p, p->ainew.tbt, p->ainew.to_tile, 0, 0, p->ainew.to_direction, DC_EXEC); - p->ainew.state = AI_STATE_BUILD_PATH; - } - if (CmdFailed(res)) { - DEBUG(ai, 0, "[BuildStation] station could not be built (0x%X)", p->ainew.to_tile); - p->ainew.state = AI_STATE_NOTHING; - // If the first station _was_ build, destroy it - if (p->ainew.temp != 0) - AI_DoCommand(p->ainew.from_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); - return; - } - p->ainew.temp++; -} - - -// Build the path -static void AiNew_State_BuildPath(Player *p) -{ - assert(p->ainew.state == AI_STATE_BUILD_PATH); - // p->ainew.temp is set to -1 when this function is called for the first time - if (p->ainew.temp == -1) { - DEBUG(ai, 1, "Starting to build new path"); - // Init the counter - p->ainew.counter = (4 - _opt.diff.competitor_speed) * AI_BUILDPATH_PAUSE + 1; - // Set the position to the startingplace (-1 because in a minute we do ++) - p->ainew.path_info.position = -1; - // And don't do this again - p->ainew.temp = 0; - } - // Building goes very fast on normal rate, so we are going to slow it down.. - // By let the counter count from AI_BUILDPATH_PAUSE to 0, we have a nice way :) - if (--p->ainew.counter != 0) return; - p->ainew.counter = (4 - _opt.diff.competitor_speed) * AI_BUILDPATH_PAUSE + 1; - - // Increase the building position - p->ainew.path_info.position++; - // Build route - AiNew_Build_RoutePart(p, &p->ainew.path_info, DC_EXEC); - if (p->ainew.path_info.position == -2) { - // This means we are done building! - - if (p->ainew.tbt == AI_TRUCK && !_patches.roadveh_queue) { - // If they not queue, they have to go up and down to try again at a station... - // We don't want that, so try building some road left or right of the station - int dir1, dir2, dir3; - TileIndex tile; - int i, ret; - for (i=0;i<2;i++) { - if (i == 0) { - tile = p->ainew.from_tile + TileOffsByDiagDir(p->ainew.from_direction); - dir1 = p->ainew.from_direction - 1; - if (dir1 < 0) dir1 = 3; - dir2 = p->ainew.from_direction + 1; - if (dir2 > 3) dir2 = 0; - dir3 = p->ainew.from_direction; - } else { - tile = p->ainew.to_tile + TileOffsByDiagDir(p->ainew.to_direction); - dir1 = p->ainew.to_direction - 1; - if (dir1 < 0) dir1 = 3; - dir2 = p->ainew.to_direction + 1; - if (dir2 > 3) dir2 = 0; - dir3 = p->ainew.to_direction; - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir1)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - dir1 = TileOffsByDiagDir(dir1); - if (IsTileType(tile + dir1, MP_CLEAR) || IsTileType(tile + dir1, MP_TREES)) { - ret = AI_DoCommand(tile+dir1, AiNew_GetRoadDirection(tile, tile+dir1, tile+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - if (IsTileType(tile + dir1 + dir1, MP_CLEAR) || IsTileType(tile + dir1 + dir1, MP_TREES)) - AI_DoCommand(tile+dir1+dir1, AiNew_GetRoadDirection(tile+dir1, tile+dir1+dir1, tile+dir1+dir1+dir1), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - dir2 = TileOffsByDiagDir(dir2); - if (IsTileType(tile + dir2, MP_CLEAR) || IsTileType(tile + dir2, MP_TREES)) { - ret = AI_DoCommand(tile+dir2, AiNew_GetRoadDirection(tile, tile+dir2, tile+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - if (IsTileType(tile + dir2 + dir2, MP_CLEAR) || IsTileType(tile + dir2 + dir2, MP_TREES)) - AI_DoCommand(tile+dir2+dir2, AiNew_GetRoadDirection(tile+dir2, tile+dir2+dir2, tile+dir2+dir2+dir2), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - - ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - dir3 = TileOffsByDiagDir(dir3); - if (IsTileType(tile + dir3, MP_CLEAR) || IsTileType(tile + dir3, MP_TREES)) { - ret = AI_DoCommand(tile+dir3, AiNew_GetRoadDirection(tile, tile+dir3, tile+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - if (!CmdFailed(ret)) { - if (IsTileType(tile + dir3 + dir3, MP_CLEAR) || IsTileType(tile + dir3 + dir3, MP_TREES)) - AI_DoCommand(tile+dir3+dir3, AiNew_GetRoadDirection(tile+dir3, tile+dir3+dir3, tile+dir3+dir3+dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD); - } - } - } - } - } - - DEBUG(ai, 1, "Finished building path, cost: %d", p->ainew.new_cost); - p->ainew.state = AI_STATE_BUILD_DEPOT; - } -} - - -// Builds the depot -static void AiNew_State_BuildDepot(Player *p) -{ - int res = 0; - assert(p->ainew.state == AI_STATE_BUILD_DEPOT); - - if (IsTileType(p->ainew.depot_tile, MP_STREET) && GetRoadTileType(p->ainew.depot_tile) == ROAD_TILE_DEPOT) { - if (IsTileOwner(p->ainew.depot_tile, _current_player)) { - // The depot is already built - p->ainew.state = AI_STATE_BUILD_VEHICLE; - return; - } else { - // There is a depot, but not of our team! :( - p->ainew.state = AI_STATE_NOTHING; - return; - } - } - - // There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p) - if (!EnsureNoVehicle(p->ainew.depot_tile + TileOffsByDiagDir(p->ainew.depot_direction))) - return; - - res = AiNew_Build_Depot(p, p->ainew.depot_tile, p->ainew.depot_direction, DC_EXEC); - if (CmdFailed(res)) { - DEBUG(ai, 0, "[BuildDepot] depot could not be built (0x%X)", p->ainew.depot_tile); - p->ainew.state = AI_STATE_NOTHING; - return; - } - - p->ainew.state = AI_STATE_BUILD_VEHICLE; - p->ainew.idle = 10; - p->ainew.veh_main_id = INVALID_VEHICLE; -} - - -// Build vehicles -static void AiNew_State_BuildVehicle(Player *p) -{ - int res; - assert(p->ainew.state == AI_STATE_BUILD_VEHICLE); - - // Check if we need to build a vehicle - if (p->ainew.amount_veh == 0) { - // Nope, we are done! - // This means: we are all done! The route is open.. go back to NOTHING - // He will idle some time and it will all start over again.. :) - p->ainew.state = AI_STATE_ACTION_DONE; - return; - } - if (--p->ainew.idle != 0) return; - // It is realistic that the AI can only build 1 vehicle a day.. - // This makes sure of that! - p->ainew.idle = AI_BUILD_VEHICLE_TIME_BETWEEN; - - // Build the vehicle - res = AiNew_Build_Vehicle(p, p->ainew.depot_tile, DC_EXEC); - if (CmdFailed(res)) { - // This happens when the AI can't build any more vehicles! - p->ainew.state = AI_STATE_NOTHING; - return; - } - // Increase the current counter - p->ainew.cur_veh++; - // Decrease the total counter - p->ainew.amount_veh--; - // Go give some orders! - p->ainew.state = AI_STATE_WAIT_FOR_BUILD; -} - - -// Put the stations in the order list -static void AiNew_State_GiveOrders(Player *p) -{ - int idx; - Order order; - - assert(p->ainew.state == AI_STATE_GIVE_ORDERS); - - if (p->ainew.veh_main_id != INVALID_VEHICLE) { - AI_DoCommand(0, p->ainew.veh_id + (p->ainew.veh_main_id << 16), 0, DC_EXEC, CMD_CLONE_ORDER); - - p->ainew.state = AI_STATE_START_VEHICLE; - return; - } else { - p->ainew.veh_main_id = p->ainew.veh_id; - } - - // Very handy for AI, goto depot.. but yeah, it needs to be activated ;) - if (_patches.gotodepot) { - idx = 0; - order.type = OT_GOTO_DEPOT; - order.flags = OF_UNLOAD; - order.dest = GetDepotByTile(p->ainew.depot_tile)->index; - AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - } - - idx = 0; - order.type = OT_GOTO_STATION; - order.flags = 0; - order.dest = GetStationIndex(p->ainew.to_tile); - if (p->ainew.tbt == AI_TRUCK && p->ainew.to_deliver) - order.flags |= OF_FULL_LOAD; - AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - - idx = 0; - order.type = OT_GOTO_STATION; - order.flags = 0; - order.dest = GetStationIndex(p->ainew.from_tile); - if (p->ainew.tbt == AI_TRUCK && p->ainew.from_deliver) - order.flags |= OF_FULL_LOAD; - AI_DoCommand(0, p->ainew.veh_id + (idx << 16), PackOrder(&order), DC_EXEC, CMD_INSERT_ORDER); - - // Start the engines! - p->ainew.state = AI_STATE_START_VEHICLE; -} - - -// Start the vehicle -static void AiNew_State_StartVehicle(Player *p) -{ - assert(p->ainew.state == AI_STATE_START_VEHICLE); - - // Skip the first order if it is a second vehicle - // This to make vehicles go different ways.. - if (p->ainew.cur_veh & 1) - AI_DoCommand(0, p->ainew.veh_id, 0, DC_EXEC, CMD_SKIP_ORDER); - - // 3, 2, 1... go! (give START_STOP command ;)) - AI_DoCommand(0, p->ainew.veh_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH); - // Try to build an other vehicle (that function will stop building when needed) - p->ainew.idle = 10; - p->ainew.state = AI_STATE_BUILD_VEHICLE; -} - - -// Repays money -static void AiNew_State_RepayMoney(Player *p) -{ - uint i; - - for (i = 0; i < AI_LOAN_REPAY; i++) { - AI_DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN); - } - p->ainew.state = AI_STATE_ACTION_DONE; -} - - -static void AiNew_CheckVehicle(Player *p, Vehicle *v) -{ - // When a vehicle is under the 6 months, we don't check for anything - if (v->age < 180) return; - - // When a vehicle is older then 1 year, it should make money... - if (v->age > 360) { - // If both years together are not more than AI_MINIMUM_ROUTE_PROFIT, - // it is not worth the line I guess... - if (v->profit_last_year + v->profit_this_year < AI_MINIMUM_ROUTE_PROFIT || - (v->reliability * 100 >> 16) < 40) { - // There is a possibility that the route is fucked up... - if (v->cargo_days > AI_VEHICLE_LOST_DAYS) { - // The vehicle is lost.. check the route, or else, get the vehicle - // back to a depot - // TODO: make this piece of code - } - - - // We are already sending him back - if (AiNew_GetSpecialVehicleFlag(p, v) & AI_VEHICLEFLAG_SELL) { - if (v->type == VEH_Road && IsTileDepotType(v->tile, TRANSPORT_ROAD) && - (v->vehstatus&VS_STOPPED)) { - // We are at the depot, sell the vehicle - AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH); - } - return; - } - - if (!AiNew_SetSpecialVehicleFlag(p, v, AI_VEHICLEFLAG_SELL)) return; - { - int ret = 0; - if (v->type == VEH_Road) - ret = AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT); - // This means we can not find a depot :s - // if (CmdFailed(ret)) - } - } - } -} - - -// Checks all vehicles if they are still valid and make money and stuff -static void AiNew_State_CheckAllVehicles(Player *p) -{ - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->owner != p->index) continue; - // Currently, we only know how to handle road-vehicles - if (v->type != VEH_Road) continue; - - AiNew_CheckVehicle(p, v); - } - - p->ainew.state = AI_STATE_ACTION_DONE; -} - - -// Using the technique simular to the original AI -// Keeps things logical -// It really should be in the same order as the AI_STATE's are! -static AiNew_StateFunction* const _ainew_state[] = { - NULL, - AiNew_State_FirstTime, - AiNew_State_Nothing, - AiNew_State_WakeUp, - AiNew_State_LocateRoute, - AiNew_State_FindStation, - AiNew_State_FindPath, - AiNew_State_FindDepot, - AiNew_State_VerifyRoute, - AiNew_State_BuildStation, - AiNew_State_BuildPath, - AiNew_State_BuildDepot, - AiNew_State_BuildVehicle, - NULL, - AiNew_State_GiveOrders, - AiNew_State_StartVehicle, - AiNew_State_RepayMoney, - AiNew_State_CheckAllVehicles, - AiNew_State_ActionDone, - NULL, -}; - -static void AiNew_OnTick(Player *p) -{ - if (_ainew_state[p->ainew.state] != NULL) - _ainew_state[p->ainew.state](p); -} - - -void AiNewDoGameLoop(Player *p) -{ - if (p->ainew.state == AI_STATE_STARTUP) { - // The AI just got alive! - p->ainew.state = AI_STATE_FIRST_TIME; - p->ainew.tick = 0; - - // Only startup the AI - return; - } - - // We keep a ticker. We use it for competitor_speed - p->ainew.tick++; - - // If we come here, we can do a tick.. do so! - AiNew_OnTick(p); -} diff --git a/ai/trolly/trolly.h b/ai/trolly/trolly.h deleted file mode 100644 --- a/ai/trolly/trolly.h +++ /dev/null @@ -1,262 +0,0 @@ -/* $Id$ */ - -#ifndef AI_TROLLY_H -#define AI_TROLLY_H - -#include "../../aystar.h" -#include "../../player.h" - -/* - * These defines can be altered to change the behavoir of the AI - * - * WARNING: - * This can also alter the AI in a negative way. I will never claim these settings - * are perfect, but don't change them if you don't know what the effect is. - */ - -// How many times it the H multiplied. The higher, the more it will go straight to the -// end point. The lower, how more it will find the route with the lowest cost. -// also: the lower, the longer it takes before route is calculated.. -#define AI_PATHFINDER_H_MULTIPLER 100 - -// How many loops may AyStar do before it stops -// 0 = infinite -#define AI_PATHFINDER_LOOPS_PER_TICK 5 - -// How long may the AI search for one route? -// 0 = infinite -// This number is the number of tiles tested. -// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks -// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts -// (that is: if the AI is on VERY FAST! :p -#define AI_PATHFINDER_MAX_SEARCH_NODES 5000 - -// If you enable this, the AI is not allowed to make 90degree turns -#define AI_PATHFINDER_NO_90DEGREES_TURN - -// Below are defines for the g-calculation - -// Standard penalty given to a tile -#define AI_PATHFINDER_PENALTY 150 -// The penalty given to a tile that is going up -#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450 -// The penalty given to a tile which would have to use fundation -#define AI_PATHFINDER_FOUNDATION_PENALTY 100 -// Changing direction is a penalty, to prevent curved ways (with that: slow ways) -#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200 -// Same penalty, only for when road already exists -#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50 -// A diagonal track cost the same as a straigh, but a diagonal is faster... so give -// a bonus for using diagonal track -#ifdef AI_PATHFINDER_NO_90DEGREES_TURN -#define AI_PATHFINDER_DIAGONAL_BONUS 95 -#else -#define AI_PATHFINDER_DIAGONAL_BONUS 75 -#endif -// If a roadblock already exists, it gets a bonus -#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140 -// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation -#define AI_PATHFINDER_CURVE_PENALTY 200 - -// Penalty a bridge gets per length -#define AI_PATHFINDER_BRIDGE_PENALTY 180 -// The penalty for a bridge going up -#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000 - -// Tunnels are expensive... -// Because of that, every tile the cost is increased with 1/8th of his value -// This is also true if you are building a tunnel yourself -#define AI_PATHFINDER_TUNNEL_PENALTY 350 - -/* - * Ai_New defines - */ - -// How long may we search cities and industry for a new route? -#define AI_LOCATE_ROUTE_MAX_COUNTER 200 - -// How many days must there be between building the first station and the second station -// within one city. This number is in days and should be more than 4 months. -#define AI_CHECKCITY_DATE_BETWEEN 180 - -// How many cargo is needed for one station in a city? -#define AI_CHECKCITY_CARGO_PER_STATION 60 -// How much cargo must there not be used in a city before we can build a new station? -#define AI_CHECKCITY_NEEDED_CARGO 50 -// When there is already a station which takes the same good and the rating of that -// city is higher then this numer, we are not going to attempt to build anything -// there -#define AI_CHECKCITY_CARGO_RATING 50 -// But, there is a chance of 1 out of this number, that we do ;) -#define AI_CHECKCITY_CARGO_RATING_CHANCE 5 -// If a city is too small to contain a station, there is a small chance -// that we still do so.. just to make the city bigger! -#define AI_CHECKCITY_CITY_CHANCE 5 - -// This number indicates for every unit of cargo, how many tiles two stations maybe be away -// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units -// of the cargo in the other station, both stations can be 96 units away from eachother, if the -// next number is 0.4. -#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4 -#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7 -// In whole tiles, the minimum distance for a truck route -#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30 - -// The amount of tiles in a square from -X to +X that is scanned for a station spot -// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot -// Safe values are between 15 and 5 -#define AI_FINDSTATION_TILE_RANGE 10 - -// Building on normal speed goes very fast. Idle this amount of ticks between every -// building part. It is calculated like this: (4 - competitor_speed) * num + 1 -// where competitor_speed is between 0 (very slow) to 4 (very fast) -#define AI_BUILDPATH_PAUSE 10 - -// Minimum % of reliabilty a vehicle has to have before the AI buys it -#define AI_VEHICLE_MIN_RELIABILTY 60 - -// The minimum amount of money a player should always have -#define AI_MINIMUM_MONEY 15000 - -// If the most cheap route is build, how much is it going to cost.. -// This is to prevent the AI from trying to build a route which can not be paid for -#define AI_MINIMUM_BUS_ROUTE_MONEY 25000 -#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000 - -// The minimum amount of money before we are going to repay any money -#define AI_MINIMUM_LOAN_REPAY_MONEY 40000 -// How many repays do we do if we have enough money to do so? -// Every repay is 10000 -#define AI_LOAN_REPAY 2 -// How much income must we have before paying back a loan? Month-based (and looked at the last month) -#define AI_MINIMUM_INCOME_FOR_LOAN 7000 - -// If there is time as much cargo in the station then the vehicle can handle -// reuse the station instead of building a new one! -#define AI_STATION_REUSE_MULTIPLER 2 - -// No more than this amount of vehicles per station.. -#define AI_CHECK_MAX_VEHICLE_PER_STATION 10 - -// How many thick between building 2 vehicles -#define AI_BUILD_VEHICLE_TIME_BETWEEN DAY_TICKS - -// How many days must there between vehicle checks -// The more often, the less non-money-making lines there will be -// but the unfair it may seem to a human player -#define AI_DAYS_BETWEEN_VEHICLE_CHECKS 30 - -// How money profit does a vehicle needs to make to stay in order -// This is the profit of this year + profit of last year -// But also for vehicles that are just one year old. In other words: -// Vehicles of 2 years do easier meet this setting then vehicles -// of one year. This is a very good thing. New vehicles are filtered, -// while old vehicles stay longer, because we do get less in return. -#define AI_MINIMUM_ROUTE_PROFIT 1000 - -// A vehicle is considered lost when he his cargo is more than 180 days old -#define AI_VEHICLE_LOST_DAYS 180 - -// How many times may the AI try to find a route before it gives up -#define AI_MAX_TRIES_FOR_SAME_ROUTE 8 - -/* - * End of defines - */ - -// This stops 90degrees curves -static const byte _illegal_curves[6] = { - 255, 255, // Horz and vert, don't have the effect - 5, // upleft and upright are not valid - 4, // downright and downleft are not valid - 2, // downleft and upleft are not valid - 3, // upright and downright are not valid -}; - -enum { - AI_STATE_STARTUP = 0, - AI_STATE_FIRST_TIME, - AI_STATE_NOTHING, - AI_STATE_WAKE_UP, - AI_STATE_LOCATE_ROUTE, - AI_STATE_FIND_STATION, - AI_STATE_FIND_PATH, - AI_STATE_FIND_DEPOT, - AI_STATE_VERIFY_ROUTE, - AI_STATE_BUILD_STATION, - AI_STATE_BUILD_PATH, - AI_STATE_BUILD_DEPOT, - AI_STATE_BUILD_VEHICLE, - AI_STATE_WAIT_FOR_BUILD, - AI_STATE_GIVE_ORDERS, - AI_STATE_START_VEHICLE, - AI_STATE_REPAY_MONEY, - AI_STATE_CHECK_ALL_VEHICLES, - AI_STATE_ACTION_DONE, - AI_STATE_STOP, // Temporary function to stop the AI -}; - -// Used for tbt (train/bus/truck) -enum { - AI_TRAIN = 0, - AI_BUS, - AI_TRUCK, -}; - -enum { - AI_ACTION_NONE = 0, - AI_ACTION_BUS_ROUTE, - AI_ACTION_TRUCK_ROUTE, - AI_ACTION_REPAY_LOAN, - AI_ACTION_CHECK_ALL_VEHICLES, -}; - -// Used for from_type/to_type -enum { - AI_NO_TYPE = 0, - AI_CITY, - AI_INDUSTRY, -}; - -// Flags for in the vehicle -enum { - AI_VEHICLEFLAG_SELL = 1, - // Remember, flags must be in power of 2 -}; - -#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry) -#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route -#define AI_STATION_RANGE TileXY(MapMaxX(), MapMaxY()) - -#define AI_PATHFINDER_NO_DIRECTION (byte)-1 - -// Flags used in user_data -#define AI_PATHFINDER_FLAG_BRIDGE 1 -#define AI_PATHFINDER_FLAG_TUNNEL 2 - -typedef void AiNew_StateFunction(Player *p); - -// ai_new.c -void AiNewDoGameLoop(Player *p); - -// ai_pathfinder.c -AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo); -void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo); - -// ai_shared.c -int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c); -int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c); -DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b); -bool AiNew_SetSpecialVehicleFlag(Player *p, Vehicle *v, uint flag); -uint AiNew_GetSpecialVehicleFlag(Player *p, Vehicle *v); - -// ai_build.c -bool AiNew_Build_CompanyHQ(Player *p, TileIndex tile); -int AiNew_Build_Station(Player *p, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag); -int AiNew_Build_Bridge(Player *p, TileIndex tile_a, TileIndex tile_b, byte flag); -int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag); -EngineID AiNew_PickVehicle(Player *p); -int AiNew_Build_Vehicle(Player *p, TileIndex tile, byte flag); -int AiNew_Build_Depot(Player* p, TileIndex tile, DiagDirection direction, byte flag); - -#endif /* AI_TROLLY_H */ diff --git a/aircraft.h b/aircraft.h deleted file mode 100644 --- a/aircraft.h +++ /dev/null @@ -1,26 +0,0 @@ -/* $Id$ */ - -#ifndef AIRCRAFT_H -#define AIRCRAFT_H - -#include "station_map.h" -#include "vehicle.h" - - -static inline bool IsAircraftInHangar(const Vehicle* v) -{ - assert(v->type == VEH_Aircraft); - return v->vehstatus & VS_HIDDEN && IsHangarTile(v->tile); -} - -static inline bool IsAircraftInHangarStopped(const Vehicle* v) -{ - return IsAircraftInHangar(v) && v->vehstatus & VS_STOPPED; -} - -uint16 AircraftDefaultCargoCapacity(CargoID cid, EngineID engine_type); - -void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2); -void HandleAircraftEnterHangar(Vehicle *v); - -#endif /* AIRCRAFT_H */ diff --git a/aircraft_cmd.c b/aircraft_cmd.c deleted file mode 100644 --- a/aircraft_cmd.c +++ /dev/null @@ -1,2120 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "aircraft.h" -#include "debug.h" -#include "functions.h" -#include "station_map.h" -#include "table/strings.h" -#include "map.h" -#include "tile.h" -#include "vehicle.h" -#include "depot.h" -#include "engine.h" -#include "command.h" -#include "station.h" -#include "news.h" -#include "sound.h" -#include "player.h" -#include "airport.h" -#include "vehicle_gui.h" -#include "table/sprites.h" -#include "newgrf_engine.h" -#include "newgrf_callbacks.h" -#include "newgrf_text.h" -#include "newgrf_sound.h" -#include "date.h" - -static bool AirportMove(Vehicle *v, const AirportFTAClass *apc); -static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *apc); -static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc); -static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc); -static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc); -static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *apc); -static void CrashAirplane(Vehicle *v); - -static void AircraftNextAirportPos_and_Order(Vehicle *v); -static byte GetAircraftFlyingAltitude(const Vehicle *v); - -static const SpriteID _aircraft_sprite[] = { - 0x0EB5, 0x0EBD, 0x0EC5, 0x0ECD, - 0x0ED5, 0x0EDD, 0x0E9D, 0x0EA5, - 0x0EAD, 0x0EE5, 0x0F05, 0x0F0D, - 0x0F15, 0x0F1D, 0x0F25, 0x0F2D, - 0x0EED, 0x0EF5, 0x0EFD, 0x0F35, - 0x0E9D, 0x0EA5, 0x0EAD, 0x0EB5, - 0x0EBD, 0x0EC5 -}; - -/* Helicopter rotor animation states */ -enum HelicopterRotorStates { - HRS_ROTOR_STOPPED, - HRS_ROTOR_MOVING_1, - HRS_ROTOR_MOVING_2, - HRS_ROTOR_MOVING_3, -}; - -/* Find the nearest hangar to v - * INVALID_STATION is returned, if the player does not have any suitable - * airports (like helipads only) - */ -static StationID FindNearestHangar(const Vehicle *v) -{ - const Station *st; - uint best = 0; - StationID index = INVALID_STATION; - TileIndex vtile = TileVirtXY(v->x_pos, v->y_pos); - - FOR_ALL_STATIONS(st) { - if (st->owner == v->owner && st->facilities & FACIL_AIRPORT && - GetAirport(st->airport_type)->nof_depots > 0) { - uint distance; - - // don't crash the plane if we know it can't land at the airport - if ((AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) && - (st->airport_type == AT_SMALL || st->airport_type == AT_COMMUTER) && - !_cheats.no_jetcrash.value) - continue; - - // v->tile can't be used here, when aircraft is flying v->tile is set to 0 - distance = DistanceSquare(vtile, st->airport_tile); - if (distance < best || index == INVALID_STATION) { - best = distance; - index = st->index; - } - } - } - return index; -} - -#if 0 -// returns true if vehicle v have an airport in the schedule, that has a hangar -static bool HaveHangarInOrderList(Vehicle *v) -{ - const Order *order; - - FOR_VEHICLE_ORDERS(v, order) { - const Station *st = GetStation(order->station); - if (st->owner == v->owner && st->facilities & FACIL_AIRPORT) { - // If an airport doesn't have a hangar, skip it - if (GetAirport(st->airport_type)->nof_depots != 0) - return true; - } - } - - return false; -} -#endif - -int GetAircraftImage(const Vehicle* v, Direction direction) -{ - int spritenum = v->spritenum; - - if (is_custom_sprite(spritenum)) { - int sprite = GetCustomVehicleSprite(v, direction); - - if (sprite != 0) return sprite; - spritenum = orig_aircraft_vehicle_info[v->engine_type - AIRCRAFT_ENGINES_INDEX].image_index; - } - return direction + _aircraft_sprite[spritenum]; -} - -SpriteID GetRotorImage(const Vehicle *v) -{ - const Vehicle *w; - - assert((v->subtype & 1) == 0); - - w = v->next->next; - if (is_custom_sprite(v->spritenum)) { - SpriteID spritenum = GetCustomRotorSprite(v, false); - if (spritenum != 0) return spritenum; - } - - /* Return standard rotor sprites if there are no custom sprites for this helicopter */ - return SPR_ROTOR_STOPPED + w->u.air.state; -} - -void DrawAircraftEngine(int x, int y, EngineID engine, uint32 image_ormod) -{ - const AircraftVehicleInfo* avi = AircraftVehInfo(engine); - int spritenum = avi->image_index; - int sprite = (6 + _aircraft_sprite[spritenum]); - - if (is_custom_sprite(spritenum)) { - sprite = GetCustomVehicleIcon(engine, DIR_W); - if (sprite == 0) { - spritenum = orig_aircraft_vehicle_info[engine - AIRCRAFT_ENGINES_INDEX].image_index; - sprite = (6 + _aircraft_sprite[spritenum]); - } - } - - DrawSprite(sprite | image_ormod, x, y); - - if (!(avi->subtype & AIR_CTOL)) { - SpriteID rotor_sprite = GetCustomRotorIcon(engine); - if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED; - DrawSprite(rotor_sprite, x, y - 5); - } -} - -static int32 EstimateAircraftCost(EngineID engine_type) -{ - return AircraftVehInfo(engine_type)->base_cost * (_price.aircraft_base>>3)>>5; -} - - -/** - * Calculates cargo capacity based on an aircraft's passenger - * and mail capacities. - * @param cid Which cargo type to calculate a capacity for. - * @param engine Which engine to find a cargo capacity for. - * @return New cargo capacity value. - */ -uint16 AircraftDefaultCargoCapacity(CargoID cid, EngineID engine_type) -{ - const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type); - - assert(cid != CT_INVALID); - - /* An aircraft can carry twice as much goods as normal cargo, - * and four times as many passengers. */ - switch (cid) { - case CT_PASSENGERS: - return avi->passenger_capacity; - case CT_MAIL: - return avi->passenger_capacity + avi->mail_capacity; - case CT_GOODS: - return (avi->passenger_capacity + avi->mail_capacity) / 2; - default: - return (avi->passenger_capacity + avi->mail_capacity) / 4; - } -} - - -/** Build an aircraft. - * @param tile tile of depot where aircraft is built - * @param p1 aircraft type being built (engine) - * @param p2 bit 0 when set, the unitnumber will be 0, otherwise it will be a free number - */ -int32 CmdBuildAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - int32 value; - Vehicle *vl[3], *v, *u, *w; - UnitID unit_num; - const AircraftVehicleInfo *avi; - const AirportFTAClass* ap; - Engine *e; - - if (!IsEngineBuildable(p1, VEH_Aircraft, _current_player)) return_cmd_error(STR_ENGINE_NOT_BUILDABLE); - - value = EstimateAircraftCost(p1); - - // to just query the cost, it is not neccessary to have a valid tile (automation/AI) - if (flags & DC_QUERY_COST) return value; - - if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_player)) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); - - avi = AircraftVehInfo(p1); - - // Prevent building aircraft types at places which can't handle them - ap = GetAirport(GetStationByTile(tile)->airport_type); - if ((avi->subtype & AIR_CTOL ? HELICOPTERS_ONLY : AIRCRAFT_ONLY) == ap->acc_planes) { - return CMD_ERROR; - } - - // allocate 2 or 3 vehicle structs, depending on type - if (!AllocateVehicles(vl, avi->subtype & AIR_CTOL ? 2 : 3) || - IsOrderPoolFull()) { - return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); - } - - unit_num = HASBIT(p2, 0) ? 0 : GetFreeUnitNumber(VEH_Aircraft); - if (unit_num > _patches.max_aircraft) - return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME); - - if (flags & DC_EXEC) { - CargoID cargo; - uint x; - uint y; - - v = vl[0]; - u = vl[1]; - - v->unitnumber = unit_num; - v->type = u->type = VEH_Aircraft; - v->direction = 3; - - v->owner = u->owner = _current_player; - - v->tile = tile; -// u->tile = 0; - - x = TileX(tile) * TILE_SIZE + 5; - y = TileY(tile) * TILE_SIZE + 3; - - v->x_pos = u->x_pos = x; - v->y_pos = u->y_pos = y; - - u->z_pos = GetSlopeZ(x, y); - v->z_pos = u->z_pos + 1; - - v->x_offs = v->y_offs = -1; -// u->delta_x = u->delta_y = 0; - - v->sprite_width = v->sprite_height = 2; - v->z_height = 5; - - u->sprite_width = u->sprite_height = 2; - u->z_height = 1; - - v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; - u->vehstatus = VS_HIDDEN | VS_UNCLICKABLE | VS_SHADOW; - - v->spritenum = avi->image_index; -// v->cargo_count = u->number_of_pieces = 0; - - v->cargo_cap = avi->passenger_capacity; - u->cargo_cap = avi->mail_capacity; - - v->cargo_type = CT_PASSENGERS; - u->cargo_type = CT_MAIL; - - v->cargo_subtype = 0; - - v->string_id = STR_SV_AIRCRAFT_NAME; -// v->next_order_param = v->next_order = 0; - -// v->load_unload_time_rem = 0; -// v->progress = 0; - v->last_station_visited = INVALID_STATION; -// v->destination_coords = 0; - - v->max_speed = avi->max_speed; - v->acceleration = avi->acceleration; - v->engine_type = p1; - - v->subtype = (avi->subtype & AIR_CTOL ? 2 : 0); - v->value = value; - - u->subtype = 4; - - /* Danger, Will Robinson! - * If the aircraft is refittable, but cannot be refitted to - * passengers, we select the cargo type from the refit mask. - * This is a fairly nasty hack to get around the fact that TTD - * has no default cargo type specifier for planes... */ - cargo = FindFirstRefittableCargo(p1); - if (cargo != CT_INVALID && cargo != CT_PASSENGERS) { - uint16 callback = CALLBACK_FAILED; - - v->cargo_type = cargo; - - if (HASBIT(EngInfo(p1)->callbackmask, CBM_REFIT_CAPACITY)) { - callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v); - } - - if (callback == CALLBACK_FAILED) { - /* Callback failed, or not executed; use the default cargo capacity */ - v->cargo_cap = AircraftDefaultCargoCapacity(v->cargo_type, v->engine_type); - } else { - v->cargo_cap = callback; - } - - /* Set the 'second compartent' capacity to none */ - u->cargo_cap = 0; - } - - e = GetEngine(p1); - v->reliability = e->reliability; - v->reliability_spd_dec = e->reliability_spd_dec; - v->max_age = e->lifelength * 366; - - _new_vehicle_id = v->index; - - v->u.air.pos = MAX_ELEMENTS; - - /* When we click on hangar we know the tile it is on. By that we know - * its position in the array of depots the airport has.....we can search - * layout for #th position of depot. Since layout must start with a listing - * of all depots, it is simple */ - { - const Station* st = GetStationByTile(tile); - const AirportFTAClass* apc = GetAirport(st->airport_type); - uint i; - - for (i = 0; i < apc->nof_depots; i++) { - if (st->airport_tile + ToTileIndexDiff(apc->airport_depots[i]) == tile) { - assert(apc->layout[i].heading == HANGAR); - v->u.air.pos = apc->layout[i].position; - break; - } - } - // to ensure v->u.air.pos has been given a value - assert(v->u.air.pos != MAX_ELEMENTS); - } - - v->u.air.state = HANGAR; - v->u.air.previous_pos = v->u.air.pos; - v->u.air.targetairport = GetStationIndex(tile); - v->next = u; - - v->service_interval = _patches.servint_aircraft; - - v->date_of_last_service = _date; - v->build_year = u->build_year = _cur_year; - - v->cur_image = u->cur_image = 0xEA0; - - v->random_bits = VehicleRandomBits(); - u->random_bits = VehicleRandomBits(); - - VehiclePositionChanged(v); - VehiclePositionChanged(u); - - // Aircraft with 3 vehicles (chopper)? - if (v->subtype == 0) { - w = vl[2]; - - u->next = w; - - w->type = VEH_Aircraft; - w->direction = 0; - w->owner = _current_player; - w->x_pos = v->x_pos; - w->y_pos = v->y_pos; - w->z_pos = v->z_pos + 5; - w->x_offs = w->y_offs = -1; - w->sprite_width = w->sprite_height = 2; - w->z_height = 1; - w->vehstatus = VS_HIDDEN | VS_UNCLICKABLE; - w->spritenum = 0xFF; - w->subtype = 6; - w->cur_image = SPR_ROTOR_STOPPED; - w->random_bits = VehicleRandomBits(); - /* Use rotor's air.state to store the rotor animation frame */ - w->u.air.state = HRS_ROTOR_STOPPED; - VehiclePositionChanged(w); - } - GetPlayer(_current_player)->num_engines[p1]++; - - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); - InvalidateWindow(WC_COMPANY, v->owner); - if (IsLocalPlayer()) - InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Aircraft); //updates the replace Aircraft window - } - - return value; -} - - -static void DoDeleteAircraft(Vehicle *v) -{ - DeleteWindowById(WC_VEHICLE_VIEW, v->index); - RebuildVehicleLists(); - InvalidateWindow(WC_COMPANY, v->owner); - DeleteDepotHighlightOfVehicle(v); - DeleteVehicleChain(v); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); -} - -/** Sell an aircraft. - * @param tile unused - * @param p1 vehicle ID to be sold - * @param p2 unused - */ -int32 CmdSellAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *v; - - if (!IsValidVehicleID(p1)) return CMD_ERROR; - - v = GetVehicle(p1); - - if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR; - if (!IsAircraftInHangarStopped(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED); - - SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); - - if (flags & DC_EXEC) { - // Invalidate depot - InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - DoDeleteAircraft(v); - if (IsLocalPlayer()) - InvalidateWindow(WC_REPLACE_VEHICLE, VEH_Aircraft); // updates the replace Aircraft window - } - - return -(int32)v->value; -} - -/** Start/Stop an aircraft. - * @param tile unused - * @param p1 aircraft ID to start/stop - * @param p2 unused - */ -int32 CmdStartStopAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *v; - uint16 callback; - - if (!IsValidVehicleID(p1)) return CMD_ERROR; - - v = GetVehicle(p1); - - if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR; - - // cannot stop airplane when in flight, or when taking off / landing - if (v->u.air.state >= STARTTAKEOFF && v->u.air.state < TERM7) - return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT); - - /* Check if this aircraft can be started/stopped. The callback will fail or - * return 0xFF if it can. */ - callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v); - if (callback != CALLBACK_FAILED && callback != 0xFF) { - StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback); - return_cmd_error(error); - } - - if (flags & DC_EXEC) { - if (IsAircraftInHangarStopped(v)) { - DeleteVehicleNews(p1, STR_A014_AIRCRAFT_IS_WAITING_IN); - } - - v->vehstatus ^= VS_STOPPED; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); - } - - return 0; -} - -/** Send an aircraft to the hangar. - * @param tile unused - * @param p1 vehicle ID to send to the hangar - * @param p2 various bitmasked elements - * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h) - * - p2 bit 8-10 - VLW flag (for mass goto depot) - */ -int32 CmdSendAircraftToHangar(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *v; - - if (p2 & DEPOT_MASS_SEND) { - /* Mass goto depot requested */ - if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR; - return SendAllVehiclesToDepot(VEH_Aircraft, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1); - } - - if (!IsValidVehicleID(p1)) return CMD_ERROR; - - v = GetVehicle(p1); - - if (v->type != VEH_Aircraft || !CheckOwnership(v->owner) || IsAircraftInHangar(v)) return CMD_ERROR; - - if (v->current_order.type == OT_GOTO_DEPOT && !(p2 & DEPOT_LOCATE_HANGAR)) { - if (!!(p2 & DEPOT_SERVICE) == HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT)) { - /* We called with a different DEPOT_SERVICE setting. - * Now we change the setting to apply the new one and let the vehicle head for the same hangar. - * Note: the if is (true for requesting service == true for ordered to stop in hangar) */ - if (flags & DC_EXEC) { - TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - } - return 0; - } - - if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of hangar orders - if (flags & DC_EXEC) { - if (v->current_order.flags & OF_UNLOAD) v->cur_order_index++; - v->current_order.type = OT_DUMMY; - v->current_order.flags = 0; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - } - } else { - bool next_airport_has_hangar = true; - StationID next_airport_index = v->u.air.targetairport; - const Station *st = GetStation(next_airport_index); - /* If the station is not a valid airport or if it has no hangars */ - if (!IsValidStation(st) || st->airport_tile == 0 || GetAirport(st->airport_type)->nof_depots == 0) { - StationID station; - - // the aircraft has to search for a hangar on its own - station = FindNearestHangar(v); - - next_airport_has_hangar = false; - if (station == INVALID_STATION) return CMD_ERROR; - st = GetStation(station); - next_airport_index = station; - - } - - if (flags & DC_EXEC) { - v->current_order.type = OT_GOTO_DEPOT; - v->current_order.flags = OF_NON_STOP; - if (!(p2 & DEPOT_SERVICE)) SETBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); - v->current_order.refit_cargo = CT_INVALID; - v->current_order.dest = next_airport_index; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - if (v->u.air.state == FLYING && !next_airport_has_hangar) { - /* The aircraft is now heading for a different hangar than the next in the orders */ - AircraftNextAirportPos_and_Order(v); - v->u.air.targetairport = next_airport_index; - } - } - } - - return 0; -} - - -/** Refits an aircraft to the specified cargo type. - * @param tile unused - * @param p1 vehicle ID of the aircraft to refit - * @param p2 various bitstuffed elements - * - p2 = (bit 0-7) - the new cargo type to refit to - * - p2 = (bit 8-15) - the new cargo subtype to refit to - */ -int32 CmdRefitAircraft(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - Vehicle *v; - int pass, mail; - int32 cost; - CargoID new_cid = GB(p2, 0, 8); - byte new_subtype = GB(p2, 8, 8); - const AircraftVehicleInfo *avi; - uint16 callback = CALLBACK_FAILED; - - if (!IsValidVehicleID(p1)) return CMD_ERROR; - - v = GetVehicle(p1); - - if (v->type != VEH_Aircraft || !CheckOwnership(v->owner)) return CMD_ERROR; - if (!IsAircraftInHangarStopped(v)) return_cmd_error(STR_A01B_AIRCRAFT_MUST_BE_STOPPED); - - avi = AircraftVehInfo(v->engine_type); - - /* Check cargo */ - if (new_cid > NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN); - - /* Check the refit capacity callback */ - if (HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_REFIT_CAPACITY)) { - /* Back up the existing cargo type */ - CargoID temp_cid = v->cargo_type; - byte temp_subtype = v->cargo_subtype; - v->cargo_type = new_cid; - v->cargo_subtype = new_subtype; - - callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v); - - /* Restore the cargo type */ - v->cargo_type = temp_cid; - v->cargo_subtype = temp_subtype; - } - - if (callback == CALLBACK_FAILED) { - /* If the callback failed, or wasn't executed, use the aircraft's - * default cargo capacity */ - pass = AircraftDefaultCargoCapacity(new_cid, v->engine_type); - } else { - pass = callback; - } - _returned_refit_capacity = pass; - - cost = 0; - if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) { - cost = GetRefitCost(v->engine_type); - } - - if (flags & DC_EXEC) { - Vehicle *u; - v->cargo_cap = pass; - - u = v->next; - mail = (new_cid != CT_PASSENGERS) ? 0 : avi->mail_capacity; - u->cargo_cap = mail; - if (v->cargo_type == new_cid) { - v->cargo_count = min(pass, v->cargo_count); - u->cargo_count = min(mail, u->cargo_count); - } else { - v->cargo_count = 0; - u->cargo_count = 0; - } - v->cargo_type = new_cid; - v->cargo_subtype = new_subtype; - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - RebuildVehicleLists(); - } - - return cost; -} - - -static void CheckIfAircraftNeedsService(Vehicle *v) -{ - const Station* st; - - if (_patches.servint_aircraft == 0) return; - if (!VehicleNeedsService(v)) return; - if (v->vehstatus & VS_STOPPED) return; - - if (v->current_order.type == OT_GOTO_DEPOT && - v->current_order.flags & OF_HALT_IN_DEPOT) - return; - - if (_patches.gotodepot && VehicleHasDepotOrders(v)) return; - - if (IsAircraftInHangar(v)) { - VehicleServiceInDepot(v); - return; - } - - st = GetStation(v->current_order.dest); - // only goto depot if the target airport has terminals (eg. it is airport) - if (IsValidStation(st) && st->airport_tile != 0 && GetAirport(st->airport_type)->terminals != NULL) { -// printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index); -// v->u.air.targetairport = st->index; - v->current_order.type = OT_GOTO_DEPOT; - v->current_order.flags = OF_NON_STOP; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - } else if (v->current_order.type == OT_GOTO_DEPOT) { - v->current_order.type = OT_DUMMY; - v->current_order.flags = 0; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - } -} - -void OnNewDay_Aircraft(Vehicle *v) -{ - int32 cost; - - if (v->subtype > 2) return; - - if ((++v->day_counter & 7) == 0) DecreaseVehicleValue(v); - - CheckOrders(v); - - CheckVehicleBreakdown(v); - AgeVehicle(v); - CheckIfAircraftNeedsService(v); - - if (v->vehstatus & VS_STOPPED) return; - - cost = AircraftVehInfo(v->engine_type)->running_cost * _price.aircraft_running / 364; - - v->profit_this_year -= cost >> 8; - - SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN); - SubtractMoneyFromPlayerFract(v->owner, cost); - - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); -} - -void AircraftYearlyLoop(void) -{ - Vehicle *v; - - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Aircraft && v->subtype <= 2) { - v->profit_last_year = v->profit_this_year; - v->profit_this_year = 0; - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - } - } -} - -static void AgeAircraftCargo(Vehicle *v) -{ - if (_age_cargo_skip_counter != 0) return; - - do { - if (v->cargo_days != 0xFF) v->cargo_days++; - v = v->next; - } while (v != NULL); -} - -static void HelicopterTickHandler(Vehicle *v) -{ - Vehicle *u; - int tick,spd; - SpriteID img; - - u = v->next->next; - - if (u->vehstatus & VS_HIDDEN) return; - - // if true, helicopter rotors do not rotate. This should only be the case if a helicopter is - // loading/unloading at a terminal or stopped - if (v->current_order.type == OT_LOADING || (v->vehstatus & VS_STOPPED)) { - if (u->cur_speed != 0) { - u->cur_speed++; - if (u->cur_speed >= 0x80 && u->u.air.state == HRS_ROTOR_MOVING_3) { - u->cur_speed = 0; - } - } - } else { - if (u->cur_speed == 0) - u->cur_speed = 0x70; - - if (u->cur_speed >= 0x50) - u->cur_speed--; - } - - tick = ++u->tick_counter; - spd = u->cur_speed >> 4; - - if (spd == 0) { - u->u.air.state = HRS_ROTOR_STOPPED; - img = GetRotorImage(v); - if (u->cur_image == img) return; - } else if (tick >= spd) { - u->tick_counter = 0; - u->u.air.state++; - if (u->u.air.state > HRS_ROTOR_MOVING_3) u->u.air.state = HRS_ROTOR_MOVING_1; - img = GetRotorImage(v); - } else { - return; - } - - u->cur_image = img; - - BeginVehicleMove(u); - VehiclePositionChanged(u); - EndVehicleMove(u); -} - -static void SetAircraftPosition(Vehicle *v, int x, int y, int z) -{ - Vehicle *u; - int safe_x; - int safe_y; - - v->x_pos = x; - v->y_pos = y; - v->z_pos = z; - - v->cur_image = GetAircraftImage(v, v->direction); - if (v->subtype == 0) v->next->next->cur_image = GetRotorImage(v); - - BeginVehicleMove(v); - VehiclePositionChanged(v); - EndVehicleMove(v); - - u = v->next; - - safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE); - safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE); - u->x_pos = x; - u->y_pos = y - ((v->z_pos-GetSlopeZ(safe_x, safe_y)) >> 3);; - - safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE); - u->z_pos = GetSlopeZ(safe_x, safe_y); - u->cur_image = v->cur_image; - - BeginVehicleMove(u); - VehiclePositionChanged(u); - EndVehicleMove(u); - - u = u->next; - if (u != NULL) { - u->x_pos = x; - u->y_pos = y; - u->z_pos = z + 5; - - BeginVehicleMove(u); - VehiclePositionChanged(u); - EndVehicleMove(u); - } -} - -/** Handle Aircraft specific tasks when a an Aircraft enters a hangar - * @param *v Vehicle that enters the hangar - */ -void HandleAircraftEnterHangar(Vehicle *v) -{ - Vehicle *u; - - v->subspeed = 0; - v->progress = 0; - - u = v->next; - u->vehstatus |= VS_HIDDEN; - u = u->next; - if (u != NULL) { - u->vehstatus |= VS_HIDDEN; - u->cur_speed = 0; - } - - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); -} - -static void PlayAircraftSound(const Vehicle* v) -{ - if (!PlayVehicleSound(v, VSE_START)) { - SndPlayVehicleFx(AircraftVehInfo(v->engine_type)->sfx, v); - } -} - -static bool UpdateAircraftSpeed(Vehicle *v) -{ - uint spd = v->acceleration * 2; - byte t; - - v->subspeed = (t=v->subspeed) + (byte)spd; - spd = min(v->cur_speed + (spd >> 8) + (v->subspeed < t), v->max_speed); - - // adjust speed for broken vehicles - if (v->vehstatus & VS_AIRCRAFT_BROKEN) spd = min(spd, 27); - - //updates statusbar only if speed have changed to save CPU time - if (spd != v->cur_speed) { - v->cur_speed = spd; - if (_patches.vehicle_speed) - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - } - - if (!(v->direction & 1)) spd = spd * 3 / 4; - - if (spd == 0) return false; - - if ((byte)++spd == 0) return true; - - v->progress = (t = v->progress) - (byte)spd; - - return t < v->progress; -} - -// get Aircraft running altitude -static byte GetAircraftFlyingAltitude(const Vehicle *v) -{ - switch (v->max_speed) { - case 37: return 162; - case 74: return 171; - default: return 180; - } -} - -static bool AircraftController(Vehicle *v) -{ - Station *st; - const AirportMovingData *amd; - Vehicle *u; - byte z,newdir,maxz,curz; - GetNewVehiclePosResult gp; - uint dist; - int x,y; - - st = GetStation(v->u.air.targetairport); - - // prevent going to 0,0 if airport is deleted. - { - TileIndex tile = st->airport_tile; - - if (tile == 0) tile = st->xy; - // xy of destination - x = TileX(tile) * TILE_SIZE; - y = TileY(tile) * TILE_SIZE; - } - - // get airport moving data - amd = GetAirportMovingData(st->airport_type, v->u.air.pos); - - // Helicopter raise - if (amd->flag & AMED_HELI_RAISE) { - u = v->next->next; - - // Make sure the rotors don't rotate too fast - if (u->cur_speed > 32) { - v->cur_speed = 0; - if (--u->cur_speed == 32) SndPlayVehicleFx(SND_18_HELICOPTER, v); - } else { - u->cur_speed = 32; - if (UpdateAircraftSpeed(v)) { - v->tile = 0; - - // Reached altitude? - if (v->z_pos >= 184) { - v->cur_speed = 0; - return true; - } - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1); - } - } - return false; - } - - // Helicopter landing. - if (amd->flag & AMED_HELI_LOWER) { - if (UpdateAircraftSpeed(v)) { - if (st->airport_tile == 0) { - // FIXME - AircraftController -> if station no longer exists, do not land - // helicopter will circle until sign disappears, then go to next order - // * what to do when it is the only order left, right now it just stays in 1 place - v->u.air.state = FLYING; - AircraftNextAirportPos_and_Order(v); - return false; - } - - // Vehicle is now at the airport. - v->tile = st->airport_tile; - - // Find altitude of landing position. - z = GetSlopeZ(x, y) + 1; - if (st->airport_type == AT_OILRIG) z += 54; - if (st->airport_type == AT_HELIPORT) z += 60; - - if (z == v->z_pos) { - u = v->next->next; - - // Increase speed of rotors. When speed is 80, we've landed. - if (u->cur_speed >= 80) return true; - u->cur_speed += 4; - } else if (v->z_pos > z) { - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos-1); - } else { - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos+1); - } - } - return false; - } - - // Get distance from destination pos to current pos. - dist = myabs(x + amd->x - v->x_pos) + myabs(y + amd->y - v->y_pos); - - // Need exact position? - if (!(amd->flag & AMED_EXACTPOS) && dist <= (amd->flag & AMED_SLOWTURN ? 8U : 4U)) - return true; - - // At final pos? - if (dist == 0) { - DirDiff dirdiff; - - if (v->cur_speed > 12) v->cur_speed = 12; - - // Change direction smoothly to final direction. - dirdiff = DirDifference(amd->direction, v->direction); - // if distance is 0, and plane points in right direction, no point in calling - // UpdateAircraftSpeed(). So do it only afterwards - if (dirdiff == DIRDIFF_SAME) { - v->cur_speed = 0; - return true; - } - - if (!UpdateAircraftSpeed(v)) return false; - - v->direction = ChangeDir(v->direction, dirdiff > DIRDIFF_REVERSE ? DIRDIFF_45LEFT : DIRDIFF_45RIGHT); - v->cur_speed >>= 1; - - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); - return false; - } - - if (!(amd->flag & AMED_NOSPDCLAMP) && v->cur_speed > 12) v->cur_speed = 12; - - if (!UpdateAircraftSpeed(v)) return false; - - if (v->load_unload_time_rem != 0) v->load_unload_time_rem--; - - // Turn. Do it slowly if in the air. - newdir = GetDirectionTowards(v, x + amd->x, y + amd->y); - if (newdir != v->direction) { - if (amd->flag & AMED_SLOWTURN) { - if (v->load_unload_time_rem == 0) v->load_unload_time_rem = 8; - v->direction = newdir; - } else { - v->cur_speed >>= 1; - v->direction = newdir; - } - } - - // Move vehicle. - GetNewVehiclePos(v, &gp); - v->tile = gp.new_tile; - - // If vehicle is in the air, use tile coordinate 0. - if (amd->flag & (AMED_TAKEOFF | AMED_SLOWTURN | AMED_LAND)) v->tile = 0; - - // Adjust Z for land or takeoff? - z = v->z_pos; - - if (amd->flag & AMED_TAKEOFF) { - z += 2; - maxz = GetAircraftFlyingAltitude(v); - if (z > maxz) z = maxz; - } - - if (amd->flag & AMED_LAND) { - if (st->airport_tile == 0) { - v->u.air.state = FLYING; - AircraftNextAirportPos_and_Order(v); - // get aircraft back on running altitude - SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v)); - return false; - } - - curz = GetSlopeZ(x, y) + 1; - - if (curz > z) { - z++; - } else { - int t = max(1, dist - 4); - - z -= ((z - curz) + t - 1) / t; - if (z < curz) z = curz; - } - } - - // We've landed. Decrase speed when we're reaching end of runway. - if (amd->flag & AMED_BRAKE) { - curz = GetSlopeZ(x, y) + 1; - - if (z > curz) { - z--; - } else if (z < curz) { - z++; - } - - if (dist < 64 && v->cur_speed > 12) v->cur_speed -= 4; - } - - SetAircraftPosition(v, gp.x, gp.y, z); - return false; -} - - -static void HandleCrashedAircraft(Vehicle *v) -{ - uint32 r; - Station *st; - int z; - - v->u.air.crashed_counter++; - - st = GetStation(v->u.air.targetairport); - - // make aircraft crash down to the ground - if (v->u.air.crashed_counter < 500 && st->airport_tile==0 && ((v->u.air.crashed_counter % 3) == 0) ) { - z = GetSlopeZ(v->x_pos, v->y_pos); - v->z_pos -= 1; - if (v->z_pos == z) { - v->u.air.crashed_counter = 500; - v->z_pos++; - } - } - - if (v->u.air.crashed_counter < 650) { - if (CHANCE16R(1,32,r)) { - static const DirDiff delta[] = { - DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT - }; - - v->direction = ChangeDir(v->direction, delta[GB(r, 16, 2)]); - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); - r = Random(); - CreateEffectVehicleRel(v, - GB(r, 0, 4) + 4, - GB(r, 4, 4) + 4, - GB(r, 8, 4), - EV_EXPLOSION_SMALL); - } - } else if (v->u.air.crashed_counter >= 10000) { - // remove rubble of crashed airplane - - // clear runway-in on all airports, set by crashing plane - // small airports use AIRPORT_BUSY, city airports use RUNWAY_IN_OUT_block, etc. - // but they all share the same number - CLRBITS(st->airport_flags, RUNWAY_IN_block); - CLRBITS(st->airport_flags, RUNWAY_IN_OUT_block); // commuter airport - CLRBITS(st->airport_flags, RUNWAY_IN2_block); // intercontinental - - BeginVehicleMove(v); - EndVehicleMove(v); - - DoDeleteAircraft(v); - } -} - -static void HandleBrokenAircraft(Vehicle *v) -{ - if (v->breakdown_ctr != 1) { - v->breakdown_ctr = 1; - v->vehstatus |= VS_AIRCRAFT_BROKEN; - - if (v->breakdowns_since_last_service != 255) - v->breakdowns_since_last_service++; - InvalidateWindow(WC_VEHICLE_VIEW, v->index); - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - } -} - - -static void HandleAircraftSmoke(Vehicle *v) -{ - static const struct { - int8 x; - int8 y; - } smoke_pos[] = { - { 5, 5 }, - { 6, 0 }, - { 5, -5 }, - { 0, -6 }, - { -5, -5 }, - { -6, 0 }, - { -5, 5 }, - { 0, 6 } - }; - - if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return; - - if (v->cur_speed < 10) { - v->vehstatus &= ~VS_AIRCRAFT_BROKEN; - v->breakdown_ctr = 0; - return; - } - - if ((v->tick_counter & 0x1F) == 0) { - CreateEffectVehicleRel(v, - smoke_pos[v->direction].x, - smoke_pos[v->direction].y, - 2, - EV_SMOKE - ); - } -} - -static void ProcessAircraftOrder(Vehicle *v) -{ - const Order *order; - - switch (v->current_order.type) { - case OT_GOTO_DEPOT: - if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return; - if (v->current_order.flags & OF_SERVICE_IF_NEEDED && - !VehicleNeedsService(v)) { - v->cur_order_index++; - } - break; - - case OT_LOADING: return; - - default: break; - } - - if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; - - order = GetVehicleOrder(v, v->cur_order_index); - - if (order == NULL) { - v->current_order.type = OT_NOTHING; - v->current_order.flags = 0; - return; - } - - if (order->type == OT_DUMMY && !CheckForValidOrders(v)) CrashAirplane(v); - - if (order->type == v->current_order.type && - order->flags == v->current_order.flags && - order->dest == v->current_order.dest) - return; - - v->current_order = *order; - - // orders are changed in flight, ensure going to the right station - if (order->type == OT_GOTO_STATION && v->u.air.state == FLYING) { - AircraftNextAirportPos_and_Order(v); - v->u.air.targetairport = order->dest; - } - - InvalidateVehicleOrder(v); - - InvalidateWindowClasses(WC_AIRCRAFT_LIST); -} - -/** Mark all views dirty for an aircraft. - * @param v vehicle to be redrawn. - */ -static void MarkAircraftDirty(Vehicle *v) -{ - v->cur_image = GetAircraftImage(v, v->direction); - if (v->subtype == 0) { - v->next->next->cur_image = GetRotorImage(v); - } - MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1); -} - -static void HandleAircraftLoading(Vehicle *v, int mode) -{ - if (v->current_order.type == OT_NOTHING) return; - - if (v->current_order.type != OT_DUMMY) { - if (v->current_order.type != OT_LOADING) return; - if (mode != 0) return; - if (--v->load_unload_time_rem != 0) return; - - if (CanFillVehicle(v) && (v->current_order.flags & OF_FULL_LOAD || - (_patches.gradual_loading && !HASBIT(v->load_status, LS_LOADING_FINISHED)))) { - SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC); - if (LoadUnloadVehicle(v, false)) { - InvalidateWindow(WC_AIRCRAFT_LIST, v->owner); - MarkAircraftDirty(v); - } - return; - } - - { - Order b = v->current_order; - v->current_order.type = OT_NOTHING; - v->current_order.flags = 0; - MarkAircraftDirty(v); - if (!(b.flags & OF_NON_STOP)) return; - } - } - v->cur_order_index++; - InvalidateVehicleOrder(v); -} - -static void CrashAirplane(Vehicle *v) -{ - uint16 amt; - Station *st; - StringID newsitem; - - v->vehstatus |= VS_CRASHED; - v->u.air.crashed_counter = 0; - - CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE); - - InvalidateWindow(WC_VEHICLE_VIEW, v->index); - - amt = 2; - if (v->cargo_type == CT_PASSENGERS) amt += v->cargo_count; - SetDParam(0, amt); - - v->cargo_count = 0; - v->next->cargo_count = 0, - st = GetStation(v->u.air.targetairport); - if (st->airport_tile == 0) { - newsitem = STR_PLANE_CRASH_OUT_OF_FUEL; - } else { - SetDParam(1, st->index); - newsitem = STR_A034_PLANE_CRASH_DIE_IN_FIREBALL; - } - - SetDParam(1, st->index); - AddNewsItem(newsitem, - NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0), - v->index, - 0); - - SndPlayVehicleFx(SND_12_EXPLOSION, v); -} - -static void MaybeCrashAirplane(Vehicle *v) -{ - Station *st; - uint16 prob; - uint i; - - st = GetStation(v->u.air.targetairport); - - //FIXME -- MaybeCrashAirplane -> increase crashing chances of very modern airplanes on smaller than AT_METROPOLITAN airports - prob = 0x10000 / 1500; - if (((st->airport_type == AT_SMALL) || (st->airport_type == AT_COMMUTER)) && (AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) { - prob = 0x10000 / 20; - } - - if (GB(Random(), 0, 16) > prob) return; - - // Crash the airplane. Remove all goods stored at the station. - for (i = 0; i != NUM_CARGO; i++) { - st->goods[i].rating = 1; - SB(st->goods[i].waiting_acceptance, 0, 12, 0); - } - - CrashAirplane(v); -} - -// we've landed and just arrived at a terminal -static void AircraftEntersTerminal(Vehicle *v) -{ - Station *st; - Order old_order; - - if (v->current_order.type == OT_GOTO_DEPOT) return; - - st = GetStation(v->u.air.targetairport); - v->last_station_visited = v->u.air.targetairport; - - /* Check if station was ever visited before */ - if (!(st->had_vehicle_of_type & HVOT_AIRCRAFT)) { - uint32 flags; - - st->had_vehicle_of_type |= HVOT_AIRCRAFT; - SetDParam(0, st->index); - // show newsitem of celebrating citizens - flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0); - AddNewsItem( - STR_A033_CITIZENS_CELEBRATE_FIRST, - flags, - v->index, - 0); - } - - old_order = v->current_order; - v->current_order.type = OT_LOADING; - v->current_order.flags = 0; - - if (old_order.type == OT_GOTO_STATION && - v->current_order.dest == v->last_station_visited) { - v->current_order.flags = - (old_order.flags & (OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER)) | OF_NON_STOP; - } - - SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC); - LoadUnloadVehicle(v, true); - MarkAircraftDirty(v); - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); -} - -static void AircraftLand(Vehicle *v) -{ - v->sprite_width = v->sprite_height = 2; -} - -static void AircraftLandAirplane(Vehicle *v) -{ - AircraftLand(v); - if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) { - SndPlayVehicleFx(SND_17_SKID_PLANE, v); - } - MaybeCrashAirplane(v); -} - -// set the right pos when heading to other airports after takeoff -static void AircraftNextAirportPos_and_Order(Vehicle *v) -{ - const Station* st; - const AirportFTAClass *apc; - - if (v->current_order.type == OT_GOTO_STATION || - v->current_order.type == OT_GOTO_DEPOT) - v->u.air.targetairport = v->current_order.dest; - - st = GetStation(v->u.air.targetairport); - apc = GetAirport(st->airport_type); - v->u.air.pos = v->u.air.previous_pos = apc->entry_point; -} - -static void AircraftLeaveHangar(Vehicle *v) -{ - v->cur_speed = 0; - v->subspeed = 0; - v->progress = 0; - v->direction = 3; - v->vehstatus &= ~VS_HIDDEN; - { - Vehicle *u = v->next; - u->vehstatus &= ~VS_HIDDEN; - - // Rotor blades - u = u->next; - if (u != NULL) { - u->vehstatus &= ~VS_HIDDEN; - u->cur_speed = 80; - } - } - - VehicleServiceInDepot(v); - SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos); - InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); - InvalidateWindowClasses(WC_AIRCRAFT_LIST); -} - - -//////////////////////////////////////////////////////////////////////////////// -/////////////////// AIRCRAFT MOVEMENT SCHEME //////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *apc) -{ - AircraftEntersTerminal(v); - v->u.air.state = apc->layout[v->u.air.pos].heading; -} - -static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *apc) -{ - VehicleEnterDepot(v); - v->u.air.state = apc->layout[v->u.air.pos].heading; -} - -// In an Airport Hangar -static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *apc) -{ - // if we just arrived, execute EnterHangar first - if (v->u.air.previous_pos != v->u.air.pos) { - AircraftEventHandler_EnterHangar(v, apc); - return; - } - - // if we were sent to the depot, stay there - if (v->current_order.type == OT_GOTO_DEPOT && (v->vehstatus & VS_STOPPED)) { - v->current_order.type = OT_NOTHING; - v->current_order.flags = 0; - return; - } - - if (v->current_order.type != OT_GOTO_STATION && - v->current_order.type != OT_GOTO_DEPOT) - return; - - // if the block of the next position is busy, stay put - if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return; - - // We are already at the target airport, we need to find a terminal - if (v->current_order.dest == v->u.air.targetairport) { - // FindFreeTerminal: - // 1. Find a free terminal, 2. Occupy it, 3. Set the vehicle's state to that terminal - if (v->subtype != 0) { - if (!AirportFindFreeTerminal(v, apc)) return; // airplane - } else { - if (!AirportFindFreeHelipad(v, apc)) return; // helicopter - } - } else { // Else prepare for launch. - // airplane goto state takeoff, helicopter to helitakeoff - v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF; - } - AircraftLeaveHangar(v); - AirportMove(v, apc); -} - -// At one of the Airport's Terminals -static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *apc) -{ - // if we just arrived, execute EnterTerminal first - if (v->u.air.previous_pos != v->u.air.pos) { - AircraftEventHandler_EnterTerminal(v, apc); - // on an airport with helipads, a helicopter will always land there - // and get serviced at the same time - patch setting - if (_patches.serviceathelipad) { - if (v->subtype == 0 && apc->helipads != NULL) { - // an exerpt of ServiceAircraft, without the invisibility stuff - v->date_of_last_service = _date; - v->breakdowns_since_last_service = 0; - v->reliability = GetEngine(v->engine_type)->reliability; - InvalidateWindow(WC_VEHICLE_DETAILS, v->index); - } - } - return; - } - - if (v->current_order.type == OT_NOTHING) return; - - // if the block of the next position is busy, stay put - if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return; - - // airport-road is free. We either have to go to another airport, or to the hangar - // ---> start moving - - switch (v->current_order.type) { - case OT_GOTO_STATION: // ready to fly to another airport - // airplane goto state takeoff, helicopter to helitakeoff - v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF; - break; - case OT_GOTO_DEPOT: // visit hangar for serivicing, sale, etc. - if (v->current_order.dest == v->u.air.targetairport) { - v->u.air.state = HANGAR; - } else { - v->u.air.state = (v->subtype != 0) ? TAKEOFF : HELITAKEOFF; - } - break; - default: // orders have been deleted (no orders), goto depot and don't bother us - v->current_order.type = OT_NOTHING; - v->current_order.flags = 0; - v->u.air.state = HANGAR; - } - AirportMove(v, apc); -} - -static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *apc) -{ - assert("OK, you shouldn't be here, check your Airport Scheme!" && 0); -} - -static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *apc) { - PlayAircraftSound(v); // play takeoffsound for airplanes - v->u.air.state = STARTTAKEOFF; -} - -static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *apc) -{ - v->sprite_width = v->sprite_height = 24; // ??? no idea what this is - v->u.air.state = ENDTAKEOFF; -} - -static void AircraftEventHandler_EndTakeOff(Vehicle *v, const AirportFTAClass *apc) -{ - v->u.air.state = FLYING; - // get the next position to go to, differs per airport - AircraftNextAirportPos_and_Order(v); -} - -static void AircraftEventHandler_HeliTakeOff(Vehicle *v, const AirportFTAClass *apc) -{ - const Player* p = GetPlayer(v->owner); - v->sprite_width = v->sprite_height = 24; // ??? no idea what this is - v->u.air.state = FLYING; - // get the next position to go to, differs per airport - AircraftNextAirportPos_and_Order(v); - - // check if the aircraft needs to be replaced or renewed and send it to a hangar if needed - // unless it is due for renewal but the engine is no longer available - if (v->owner == _local_player && ( - EngineHasReplacementForPlayer(p, v->engine_type) || - ((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) && - HASBIT(GetEngine(v->engine_type)->player_avail, _local_player)) - )) { - _current_player = _local_player; - DoCommandP(v->tile, v->index, DEPOT_SERVICE | DEPOT_LOCATE_HANGAR, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR); - _current_player = OWNER_NONE; - } -} - -static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *apc) -{ - Station *st; - byte landingtype; - AirportFTA *current; - uint16 tcur_speed, tsubspeed; - - st = GetStation(v->u.air.targetairport); - // flying device is accepted at this station - // small airport --> no helicopters (AIRCRAFT_ONLY) - // all other airports --> all types of flying devices (ALL) - // heliport/oilrig, etc --> no airplanes (HELICOPTERS_ONLY) - // runway busy or not allowed to use this airstation, circle - if (v->subtype != apc->acc_planes && - st->airport_tile != 0 && - (st->owner == OWNER_NONE || st->owner == v->owner)) { - // {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41}, - // if it is an airplane, look for LANDING, for helicopter HELILANDING - // it is possible to choose from multiple landing runways, so loop until a free one is found - landingtype = (v->subtype != 0) ? LANDING : HELILANDING; - current = apc->layout[v->u.air.pos].next; - while (current != NULL) { - if (current->heading == landingtype) { - // save speed before, since if AirportHasBlock is false, it resets them to 0 - // we don't want that for plane in air - // hack for speed thingie - tcur_speed = v->cur_speed; - tsubspeed = v->subspeed; - if (!AirportHasBlock(v, current, apc)) { - v->u.air.state = landingtype; // LANDING / HELILANDING - // it's a bit dirty, but I need to set position to next position, otherwise - // if there are multiple runways, plane won't know which one it took (because - // they all have heading LANDING). And also occupy that block! - v->u.air.pos = current->next_position; - SETBITS(st->airport_flags, apc->layout[v->u.air.pos].block); - return; - } - v->cur_speed = tcur_speed; - v->subspeed = tsubspeed; - } - current = current->next; - } - } - v->u.air.state = FLYING; - v->u.air.pos = apc->layout[v->u.air.pos].next_position; -} - -static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *apc) -{ - const Player* p = GetPlayer(v->owner); - AircraftLandAirplane(v); // maybe crash airplane - v->u.air.state = ENDLANDING; - // check if the aircraft needs to be replaced or renewed and send it to a hangar if needed - if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) { - // only the vehicle owner needs to calculate the rest (locally) - if (EngineHasReplacementForPlayer(p, v->engine_type) || - (p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) { - // send the aircraft to the hangar at next airport - _current_player = _local_player; - DoCommandP(v->tile, v->index, DEPOT_SERVICE, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_SHOW_NO_ERROR); - _current_player = OWNER_NONE; - } - } -} - -static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *apc) -{ - AircraftLand(v); // helicopters don't crash - v->u.air.state = HELIENDLANDING; -} - -static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *apc) -{ - // next block busy, don't do a thing, just wait - if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return; - - // if going to terminal (OT_GOTO_STATION) choose one - // 1. in case all terminals are busy AirportFindFreeTerminal() returns false or - // 2. not going for terminal (but depot, no order), - // --> get out of the way to the hangar. - if (v->current_order.type == OT_GOTO_STATION) { - if (AirportFindFreeTerminal(v, apc)) return; - } - v->u.air.state = HANGAR; - -} - -static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *apc) -{ - // next block busy, don't do a thing, just wait - if (AirportHasBlock(v, &apc->layout[v->u.air.pos], apc)) return; - - // if going to helipad (OT_GOTO_STATION) choose one. If airport doesn't have helipads, choose terminal - // 1. in case all terminals/helipads are busy (AirportFindFreeHelipad() returns false) or - // 2. not going for terminal (but depot, no order), - // --> get out of the way to the hangar IF there are terminals on the airport. - // --> else TAKEOFF - // the reason behind this is that if an airport has a terminal, it also has a hangar. Airplanes - // must go to a hangar. - if (v->current_order.type == OT_GOTO_STATION) { - if (AirportFindFreeHelipad(v, apc)) return; - } - v->u.air.state = (apc->nof_depots != 0) ? HANGAR : HELITAKEOFF; -} - -typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *apc); -static AircraftStateHandler * const _aircraft_state_handlers[] = { - AircraftEventHandler_General, // TO_ALL = 0 - AircraftEventHandler_InHangar, // HANGAR = 1 - AircraftEventHandler_AtTerminal, // TERM1 = 2 - AircraftEventHandler_AtTerminal, // TERM2 = 3 - AircraftEventHandler_AtTerminal, // TERM3 = 4 - AircraftEventHandler_AtTerminal, // TERM4 = 5 - AircraftEventHandler_AtTerminal, // TERM5 = 6 - AircraftEventHandler_AtTerminal, // TERM6 = 7 - AircraftEventHandler_AtTerminal, // HELIPAD1 = 8 - AircraftEventHandler_AtTerminal, // HELIPAD2 = 9 - AircraftEventHandler_TakeOff, // TAKEOFF = 10 - AircraftEventHandler_StartTakeOff, // STARTTAKEOFF = 11 - AircraftEventHandler_EndTakeOff, // ENDTAKEOFF = 12 - AircraftEventHandler_HeliTakeOff, // HELITAKEOFF = 13 - AircraftEventHandler_Flying, // FLYING = 14 - AircraftEventHandler_Landing, // LANDING = 15 - AircraftEventHandler_EndLanding, // ENDLANDING = 16 - AircraftEventHandler_HeliLanding, // HELILANDING = 17 - AircraftEventHandler_HeliEndLanding, // HELIENDLANDING = 18 - AircraftEventHandler_AtTerminal, // TERM7 = 19 - AircraftEventHandler_AtTerminal, // TERM8 = 20 - AircraftEventHandler_AtTerminal, // HELIPAD3 = 21 - AircraftEventHandler_AtTerminal, // HELIPAD4 = 22 -}; - -static void AirportClearBlock(const Vehicle *v, const AirportFTAClass *apc) -{ - // we have left the previous block, and entered the new one. Free the previous block - if (apc->layout[v->u.air.previous_pos].block != apc->layout[v->u.air.pos].block) { - Station *st = GetStation(v->u.air.targetairport); - - CLRBITS(st->airport_flags, apc->layout[v->u.air.previous_pos].block); - } -} - -static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *apc) -{ - // if aircraft is not in position, wait until it is - if (!AircraftController(v)) return; - - AirportClearBlock(v, apc); - AirportMove(v, apc); // move aircraft to next position -} - -// gets pos from vehicle and next orders -static bool AirportMove(Vehicle *v, const AirportFTAClass *apc) -{ - AirportFTA *current; - byte prev_pos; - - // error handling - if (v->u.air.pos >= apc->nofelements) { - DEBUG(misc, 0, "[Ap] position %d is not valid for current airport. Max position is %d", v->u.air.pos, apc->nofelements-1); - assert(v->u.air.pos < apc->nofelements); - } - - current = &apc->layout[v->u.air.pos]; - // we have arrived in an important state (eg terminal, hangar, etc.) - if (current->heading == v->u.air.state) { - prev_pos = v->u.air.pos; // location could be changed in state, so save it before-hand - _aircraft_state_handlers[v->u.air.state](v, apc); - if (v->u.air.state != FLYING) v->u.air.previous_pos = prev_pos; - return true; - } - - v->u.air.previous_pos = v->u.air.pos; // save previous location - - // there is only one choice to move to - if (current->next == NULL) { - if (AirportSetBlocks(v, current, apc)) { - v->u.air.pos = current->next_position; - } // move to next position - return false; - } - - // there are more choices to choose from, choose the one that - // matches our heading - do { - if (v->u.air.state == current->heading || current->heading == TO_ALL) { - if (AirportSetBlocks(v, current, apc)) { - v->u.air.pos = current->next_position; - } // move to next position - return false; - } - current = current->next; - } while (current != NULL); - - DEBUG(misc, 0, "[Ap] cannot move further on Airport! (pos %d state %d)", v->u.air.pos, v->u.air.state); - DEBUG(misc, 0, "[Ap] airport entry point: %d, Vehicle: %d", apc->entry_point, v->index); - assert(0); - return false; -} - -// returns true if the road ahead is busy, eg. you must wait before proceeding -static bool AirportHasBlock(Vehicle *v, const AirportFTA *current_pos, const AirportFTAClass *apc) -{ - const AirportFTA *reference = &apc->layout[v->u.air.pos]; - const AirportFTA *next = &apc->layout[current_pos->next_position]; - - // same block, then of course we can move - if (apc->layout[current_pos->position].block != next->block) { - const Station *st = GetStation(v->u.air.targetairport); - uint32 airport_flags = next->block; - - // check additional possible extra blocks - if (current_pos != reference && current_pos->block != NOTHING_block) { - airport_flags |= current_pos->block; - } - - if (HASBITS(st->airport_flags, airport_flags)) { - v->cur_speed = 0; - v->subspeed = 0; - return true; - } - } - return false; -} - -// returns true on success. Eg, next block was free and we have occupied it -static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *apc) -{ - AirportFTA *next = &apc->layout[current_pos->next_position]; - AirportFTA *reference = &apc->layout[v->u.air.pos]; - - // if the next position is in another block, check it and wait until it is free - if ((apc->layout[current_pos->position].block & next->block) != next->block) { - uint32 airport_flags = next->block; - Station* st = GetStation(v->u.air.targetairport); - //search for all all elements in the list with the same state, and blocks != N - // this means more blocks should be checked/set - AirportFTA *current = current_pos; - if (current == reference) current = current->next; - while (current != NULL) { - if (current->heading == current_pos->heading && current->block != 0) { - airport_flags |= current->block; - break; - } - current = current->next; - }; - - // if the block to be checked is in the next position, then exclude that from - // checking, because it has been set by the airplane before - if (current_pos->block == next->block) airport_flags ^= next->block; - - if (HASBITS(st->airport_flags, airport_flags)) { - v->cur_speed = 0; - v->subspeed = 0; - return false; - } - - if (next->block != NOTHING_block) { - SETBITS(st->airport_flags, airport_flags); // occupy next block - } - } - return true; -} - -static bool FreeTerminal(Vehicle *v, byte i, byte last_terminal) -{ - Station *st = GetStation(v->u.air.targetairport); - for (; i < last_terminal; i++) { - if (!HASBIT(st->airport_flags, _airport_terminal_flag[i])) { - // TERMINAL# HELIPAD# - v->u.air.state = _airport_terminal_state[i]; // start moving to that terminal/helipad - SETBIT(st->airport_flags, _airport_terminal_flag[i]); // occupy terminal/helipad - return true; - } - } - return false; -} - -static uint GetNumTerminals(const AirportFTAClass *apc) -{ - uint num = 0; - uint i; - - for (i = apc->terminals[0]; i > 0; i--) num += apc->terminals[i]; - - return num; -} - -static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *apc) -{ - /* example of more terminalgroups - * {0,HANGAR,NOTHING_block,1}, {0,255,TERM_GROUP1_block,0}, {0,255,TERM_GROUP2_ENTER_block,1}, {0,0,N,1}, - * Heading 255 denotes a group. We see 2 groups here: - * 1. group 0 -- TERM_GROUP1_block (check block) - * 2. group 1 -- TERM_GROUP2_ENTER_block (check block) - * First in line is checked first, group 0. If the block (TERM_GROUP1_block) is free, it - * looks at the corresponding terminals of that group. If no free ones are found, other - * possible groups are checked (in this case group 1, since that is after group 0). If that - * fails, then attempt fails and plane waits - */ - if (apc->terminals[0] > 1) { - Station *st = GetStation(v->u.air.targetairport); - AirportFTA *temp = apc->layout[v->u.air.pos].next; - - while (temp != NULL) { - if (temp->heading == 255) { - if (!HASBITS(st->airport_flags, temp->block)) { - int target_group; - int i; - int group_start = 0; - int group_end; - - //read which group do we want to go to? - //(the first free group) - target_group = temp->next_position + 1; - - //at what terminal does the group start? - //that means, sum up all terminals of - //groups with lower number - for (i = 1; i < target_group; i++) - group_start += apc->terminals[i]; - - group_end = group_start + apc->terminals[target_group]; - if (FreeTerminal(v, group_start, group_end)) return true; - } - } else { - /* once the heading isn't 255, we've exhausted the possible blocks. - * So we cannot move */ - return false; - } - temp = temp->next; - } - } - - // if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max) - return FreeTerminal(v, 0, GetNumTerminals(apc)); -} - -static uint GetNumHelipads(const AirportFTAClass *apc) -{ - uint num = 0; - uint i; - - for (i = apc->helipads[0]; i > 0; i--) num += apc->helipads[i]; - - return num; -} - - -static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *apc) -{ - // if an airport doesn't have helipads, use terminals - if (apc->helipads == NULL) return AirportFindFreeTerminal(v, apc); - - // if there are more helicoptergroups, pick one, just as in AirportFindFreeTerminal() - if (apc->helipads[0] > 1) { - const Station* st = GetStation(v->u.air.targetairport); - const AirportFTA* temp = apc->layout[v->u.air.pos].next; - - while (temp != NULL) { - if (temp->heading == 255) { - if (!HASBITS(st->airport_flags, temp->block)) { - int target_group; - int i; - int group_start = 0; - int group_end; - - //read which group do we want to go to? - //(the first free group) - target_group = temp->next_position + 1; - - //at what terminal does the group start? - //that means, sum up all terminals of - //groups with lower number - for (i = 1; i < target_group; i++) - group_start += apc->helipads[i]; - - group_end = group_start + apc->helipads[target_group]; - if (FreeTerminal(v, group_start, group_end)) return true; - } - } else { - /* once the heading isn't 255, we've exhausted the possible blocks. - * So we cannot move */ - return false; - } - temp = temp->next; - } - } else { - // only 1 helicoptergroup, check all helipads - // The blocks for helipads start after the last terminal (MAX_TERMINALS) - return FreeTerminal(v, MAX_TERMINALS, GetNumHelipads(apc) + MAX_TERMINALS); - } - return false; // it shouldn't get here anytime, but just to be sure -} - -static void AircraftEventHandler(Vehicle *v, int loop) -{ - v->tick_counter++; - - if (v->vehstatus & VS_CRASHED) { - HandleCrashedAircraft(v); - return; - } - - if (v->vehstatus & VS_STOPPED) return; - - /* aircraft is broken down? */ - if (v->breakdown_ctr != 0) { - if (v->breakdown_ctr <= 2) { - HandleBrokenAircraft(v); - } else { - v->breakdown_ctr--; - } - } - - HandleAircraftSmoke(v); - ProcessAircraftOrder(v); - HandleAircraftLoading(v, loop); - - if (v->current_order.type >= OT_LOADING) return; - - // pass the right airport structure to the functions - // DEREF_STATION gets target airport (Station *st), its type is passed to GetAirport - // that returns the correct layout depending on type - AirportGoToNextPosition(v, GetAirport(GetStation(v->u.air.targetairport)->airport_type)); -} - -void Aircraft_Tick(Vehicle *v) -{ - int i; - - if (v->subtype > 2) return; - - if (v->subtype == 0) HelicopterTickHandler(v); - - AgeAircraftCargo(v); - - for (i = 0; i != 6; i++) { - AircraftEventHandler(v, i); - if (v->type != VEH_Aircraft) // In case it was deleted - break; - } -} - -void UpdateOilRig(void) -{ - Station* st; - - FOR_ALL_STATIONS(st) { - if (st->airport_type == 5) st->airport_type = AT_OILRIG; - } -} - -// need to be called to load aircraft from old version -void UpdateOldAircraft(void) -{ - Station *st; - Vehicle *v_oldstyle; - GetNewVehiclePosResult gp; - - // set airport_flags to 0 for all airports just to be sure - FOR_ALL_STATIONS(st) { - st->airport_flags = 0; // reset airport - // type of oilrig has been moved, update it (3-5) - if (st->airport_type == 3) st->airport_type = AT_OILRIG; - } - - FOR_ALL_VEHICLES(v_oldstyle) { - // airplane has another vehicle with subtype 4 (shadow), helicopter also has 3 (rotor) - // skip those - if (v_oldstyle->type == VEH_Aircraft && v_oldstyle->subtype <= 2) { - // airplane in terminal stopped doesn't hurt anyone, so goto next - if (v_oldstyle->vehstatus & VS_STOPPED && v_oldstyle->u.air.state == 0) { - v_oldstyle->u.air.state = HANGAR; - continue; - } - - AircraftLeaveHangar(v_oldstyle); // make airplane visible if it was in a depot for example - v_oldstyle->vehstatus &= ~VS_STOPPED; // make airplane moving - v_oldstyle->u.air.state = FLYING; - AircraftNextAirportPos_and_Order(v_oldstyle); // move it to the entry point of the airport - GetNewVehiclePos(v_oldstyle, &gp); // get the position of the plane (to be used for setting) - v_oldstyle->tile = 0; // aircraft in air is tile=0 - - // correct speed of helicopter-rotors - if (v_oldstyle->subtype == 0) v_oldstyle->next->next->cur_speed = 32; - - // set new position x,y,z - SetAircraftPosition(v_oldstyle, gp.x, gp.y, GetAircraftFlyingAltitude(v_oldstyle)); - } - } -} - -void UpdateAirplanesOnNewStation(Station *st) -{ - GetNewVehiclePosResult gp; - Vehicle *v; - byte takeofftype; - uint16 cnt; - // only 1 station is updated per function call, so it is enough to get entry_point once - const AirportFTAClass *ap = GetAirport(st->airport_type); - FOR_ALL_VEHICLES(v) { - if (v->type == VEH_Aircraft && v->subtype <= 2) { - if (v->u.air.targetairport == st->index) { // if heading to this airport - /* update position of airplane. If plane is not flying, landing, or taking off - *you cannot delete airport, so it doesn't matter - */ - if (v->u.air.state >= FLYING) { // circle around - v->u.air.pos = v->u.air.previous_pos = ap->entry_point; - v->u.air.state = FLYING; - // landing plane needs to be reset to flying height (only if in pause mode upgrade, - // in normal mode, plane is reset in AircraftController. It doesn't hurt for FLYING - GetNewVehiclePos(v, &gp); - // set new position x,y,z - SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlyingAltitude(v)); - } else { - assert(v->u.air.state == ENDTAKEOFF || v->u.air.state == HELITAKEOFF); - takeofftype = (v->subtype == 0) ? HELITAKEOFF : ENDTAKEOFF; - // search in airportdata for that heading - // easiest to do, since this doesn't happen a lot - for (cnt = 0; cnt < ap->nofelements; cnt++) { - if (ap->layout[cnt].heading == takeofftype) { - v->u.air.pos = ap->layout[cnt].position; - break; - } - } - } - } - } - } -} diff --git a/aircraft_gui.c b/aircraft_gui.c deleted file mode 100644 --- a/aircraft_gui.c +++ /dev/null @@ -1,350 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "aircraft.h" -#include "debug.h" -#include "functions.h" -#include "table/sprites.h" -#include "table/strings.h" -#include "map.h" -#include "window.h" -#include "gui.h" -#include "vehicle.h" -#include "gfx.h" -#include "command.h" -#include "engine.h" -#include "viewport.h" -#include "player.h" -#include "depot.h" -#include "vehicle_gui.h" -#include "newgrf_engine.h" - - -void CcCloneAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - if (success) ShowAircraftViewWindow(GetVehicle(_new_vehicle_id)); -} - -static void AircraftDetailsWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Vehicle *v = GetVehicle(w->window_number); - - SetWindowWidgetDisabledState(w, 2, v->owner != _local_player); - - /* Disable service-scroller when interval is set to disabled */ - SetWindowWidgetDisabledState(w, 5, !_patches.servint_aircraft); - SetWindowWidgetDisabledState(w, 6, !_patches.servint_aircraft); - - SetDParam(0, v->string_id); - SetDParam(1, v->unitnumber); - DrawWindowWidgets(w); - - /* Draw running cost */ - { - int year = v->age / 366; - - SetDParam(1, year); - - SetDParam(0, (v->age + 365 < v->max_age) ? STR_AGE : STR_AGE_RED); - SetDParam(2, v->max_age / 366); - SetDParam(3, _price.aircraft_running * AircraftVehInfo(v->engine_type)->running_cost >> 8); - DrawString(2, 15, STR_A00D_AGE_RUNNING_COST_YR, 0); - } - - /* Draw max speed */ - { - SetDParam(0, v->max_speed * 128 / 10); - DrawString(2, 25, STR_A00E_MAX_SPEED, 0); - } - - /* Draw profit */ - { - SetDParam(0, v->profit_this_year); - SetDParam(1, v->profit_last_year); - DrawString(2, 35, STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR, 0); - } - - /* Draw breakdown & reliability */ - { - SetDParam(0, v->reliability * 100 >> 16); - SetDParam(1, v->breakdowns_since_last_service); - DrawString(2, 45, STR_A010_RELIABILITY_BREAKDOWNS, 0); - } - - /* Draw service interval text */ - { - SetDParam(0, v->service_interval); - SetDParam(1, v->date_of_last_service); - DrawString(13, 103, _patches.servint_ispercent?STR_SERVICING_INTERVAL_PERCENT:STR_883C_SERVICING_INTERVAL_DAYS, 0); - } - - DrawAircraftImage(v, 3, 57, INVALID_VEHICLE); - - { - const Vehicle *u; - int y = 57; - - do { - if (v->subtype <= 2) { - SetDParam(0, GetCustomEngineName(v->engine_type)); - SetDParam(1, v->build_year); - SetDParam(2, v->value); - DrawString(60, y, STR_A011_BUILT_VALUE, 0); - y += 10; - - SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_cap); - u = v->next; - SetDParam(2, u->cargo_type); - SetDParam(3, u->cargo_cap); - DrawString(60, y, (u->cargo_cap != 0) ? STR_A019_CAPACITY : STR_A01A_CAPACITY, 0); - y += 14; - } - - if (v->cargo_count != 0) { - - /* Cargo names (fix pluralness) */ - SetDParam(0, v->cargo_type); - SetDParam(1, v->cargo_count); - SetDParam(2, v->cargo_source); - DrawString(60, y, STR_8813_FROM, 0); - - y += 10; - } - } while ( (v=v->next) != NULL); - } - } break; - - case WE_CLICK: { - int mod; - const Vehicle *v; - switch (e->we.click.widget) { - case 2: /* rename */ - v = GetVehicle(w->window_number); - SetDParam(0, v->unitnumber); - ShowQueryString(v->string_id, STR_A030_NAME_AIRCRAFT, 31, 150, w, CS_ALPHANUMERAL); - break; - case 5: /* increase int */ - mod = _ctrl_pressed? 5 : 10; - goto do_change_service_int; - case 6: /* decrease int */ - mod = _ctrl_pressed?- 5 : -10; -do_change_service_int: - v = GetVehicle(w->window_number); - - mod = GetServiceIntervalClamped(mod + v->service_interval); - if (mod == v->service_interval) return; - - DoCommandP(v->tile, v->index, mod, NULL, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING)); - break; - } - } break; - - case WE_ON_EDIT_TEXT: - if (e->we.edittext.str[0] != '\0') { - _cmd_text = e->we.edittext.str; - DoCommandP(0, w->window_number, 0, NULL, - CMD_NAME_VEHICLE | CMD_MSG(STR_A031_CAN_T_NAME_AIRCRAFT)); - } - break; - } -} - - -static const Widget _aircraft_details_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, RESIZE_NONE, 14, 11, 349, 0, 13, STR_A00C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS }, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 350, 389, 0, 13, STR_01AA_NAME, STR_A032_NAME_AIRCRAFT }, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 389, 14, 55, 0x0, STR_NULL }, -{ WWT_PANEL, RESIZE_NONE, 14, 0, 389, 56, 101, 0x0, STR_NULL }, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 102, 107, STR_0188, STR_884D_INCREASE_SERVICING_INTERVAL }, -{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 10, 108, 113, STR_0189, STR_884E_DECREASE_SERVICING_INTERVAL }, -{ WWT_PANEL, RESIZE_NONE, 14, 11, 389, 102, 113, 0x0, STR_NULL }, -{ WIDGETS_END}, -}; - -static const WindowDesc _aircraft_details_desc = { - WDP_AUTO, WDP_AUTO, 390, 114, - WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, - _aircraft_details_widgets, - AircraftDetailsWndProc -}; - - -static void ShowAircraftDetailsWindow(const Vehicle *v) -{ - Window *w; - VehicleID veh = v->index; - - DeleteWindowById(WC_VEHICLE_ORDERS, veh); - DeleteWindowById(WC_VEHICLE_DETAILS, veh); - - w = AllocateWindowDescFront(&_aircraft_details_desc, veh); - w->caption_color = v->owner; -// w->vscroll.cap = 6; -// w->traindetails_d.tab = 0; -} - - -static const Widget _aircraft_view_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, RESIZE_RIGHT, 14, 11, 237, 0, 13, STR_A00A, STR_018C_WINDOW_TITLE_DRAG_THIS }, -{ WWT_STICKYBOX, RESIZE_LR, 14, 238, 249, 0, 13, 0x0, STR_STICKY_BUTTON }, -{ WWT_PANEL, RESIZE_RB, 14, 0, 231, 14, 103, 0x0, STR_NULL }, -{ WWT_INSET, RESIZE_RB, 14, 2, 229, 16, 101, 0x0, STR_NULL }, -{ WWT_PUSHBTN, RESIZE_RTB, 14, 0, 237, 104, 115, 0x0, STR_A027_CURRENT_AIRCRAFT_ACTION }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 14, 31, SPR_CENTRE_VIEW_VEHICLE, STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_SEND_AIRCRAFT_TODEPOT,STR_A02A_SEND_AIRCRAFT_TO_HANGAR }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 50, 67, SPR_REFIT_VEHICLE, STR_A03B_REFIT_AIRCRAFT_TO_CARRY }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 68, 85, SPR_SHOW_ORDERS, STR_A028_SHOW_AIRCRAFT_S_ORDERS }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 86, 103, SPR_SHOW_VEHICLE_DETAILS, STR_A02B_SHOW_AIRCRAFT_DETAILS }, -{ WWT_PUSHIMGBTN, RESIZE_LR, 14, 232, 249, 32, 49, SPR_CLONE_AIRCRAFT, STR_CLONE_AIRCRAFT_INFO }, -{ WWT_PANEL, RESIZE_LRB, 14, 232, 249, 104, 103, 0x0, STR_NULL }, -{ WWT_RESIZEBOX, RESIZE_LRTB, 14, 238, 249, 104, 115, 0x0, STR_NULL }, -{ WIDGETS_END}, -}; - - -static void AircraftViewWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - const Vehicle *v = GetVehicle(w->window_number); - StringID str; - bool is_localplayer = v->owner == _local_player; - - SetWindowWidgetDisabledState(w, 7, !is_localplayer); - SetWindowWidgetDisabledState(w, 8, !IsAircraftInHangarStopped(v) || !is_localplayer); - SetWindowWidgetDisabledState(w, 11, !is_localplayer); - - - /* draw widgets & caption */ - SetDParam(0, v->string_id); - SetDParam(1, v->unitnumber); - DrawWindowWidgets(w); - - if (v->vehstatus & VS_CRASHED) { - str = STR_8863_CRASHED; - } else if (v->vehstatus & VS_STOPPED) { - str = STR_8861_STOPPED; - } else { - switch (v->current_order.type) { - case OT_GOTO_STATION: { - SetDParam(0, v->current_order.dest); - SetDParam(1, v->cur_speed * 128 / 10); - str = STR_HEADING_FOR_STATION + _patches.vehicle_speed; - } break; - - case OT_GOTO_DEPOT: { - /* Aircrafts always go to a station, even if you say depot */ - SetDParam(0, v->current_order.dest); - SetDParam(1, v->cur_speed * 128 / 10); - if (HASBIT(v->current_order.flags, OFB_HALT_IN_DEPOT) && !HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { - str = STR_HEADING_FOR_HANGAR + _patches.vehicle_speed; - } else { - str = STR_HEADING_FOR_HANGAR_SERVICE + _patches.vehicle_speed; - } - } break; - - case OT_LOADING: - str = STR_882F_LOADING_UNLOADING; - break; - - default: - if (v->num_orders == 0) { - str = STR_NO_ORDERS + _patches.vehicle_speed; - SetDParam(0, v->cur_speed * 128 / 10); - } else { - str = STR_EMPTY; - } - break; - } - } - - /* draw the flag plus orders */ - DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, 2, w->widget[5].top + 1); - DrawStringCenteredTruncated(w->widget[5].left + 8, w->widget[5].right, w->widget[5].top + 1, str, 0); - DrawWindowViewport(w); - } break; - - case WE_CLICK: { - const Vehicle *v = GetVehicle(w->window_number); - - switch (e->we.click.widget) { - case 5: /* start stop */ - DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT)); - break; - case 6: /* center main view */ - ScrollMainWindowTo(v->x_pos, v->y_pos); - break; - case 7: /* goto hangar */ - DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO)); - break; - case 8: /* refit */ - ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID); - break; - case 9: /* show orders */ - ShowOrdersWindow(v); - break; - case 10: /* show details */ - ShowAircraftDetailsWindow(v); - break; - case 11: - /* clone vehicle */ - DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0, CcCloneAircraft, CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); - break; - } - } break; - - case WE_RESIZE: - w->viewport->width += e->we.sizing.diff.x; - w->viewport->height += e->we.sizing.diff.y; - w->viewport->virtual_width += e->we.sizing.diff.x; - w->viewport->virtual_height += e->we.sizing.diff.y; - break; - - case WE_DESTROY: - DeleteWindowById(WC_VEHICLE_ORDERS, w->window_number); - DeleteWindowById(WC_VEHICLE_REFIT, w->window_number); - DeleteWindowById(WC_VEHICLE_DETAILS, w->window_number); - break; - - case WE_MOUSELOOP: { - const Vehicle *v = GetVehicle(w->window_number); - bool plane_stopped = IsAircraftInHangarStopped(v); - - /* Widget 7 (send to hangar) must be hidden if the plane is already stopped in hangar. - * Widget 11 (clone) should then be shown, since cloning is allowed only while in hangar and stopped. - * This sytem allows to have two buttons, on top of each other*/ - if (plane_stopped != IsWindowWidgetHidden(w, 7) || plane_stopped == IsWindowWidgetHidden(w, 11)) { - SetWindowWidgetHiddenState(w, 7, plane_stopped); // send to hangar - SetWindowWidgetHiddenState(w, 11, !plane_stopped); // clone - SetWindowDirty(w); - } - } break; - } -} - - -static const WindowDesc _aircraft_view_desc = { - WDP_AUTO, WDP_AUTO, 250, 116, - WC_VEHICLE_VIEW ,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, - _aircraft_view_widgets, - AircraftViewWndProc -}; - - -void ShowAircraftViewWindow(const Vehicle *v) -{ - Window *w = AllocateWindowDescFront(&_aircraft_view_desc, v->index); - - if (w != NULL) { - w->caption_color = v->owner; - AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0); - } -} diff --git a/airport.c b/airport.c deleted file mode 100644 --- a/airport.c +++ /dev/null @@ -1,489 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "debug.h" -#include "map.h" -#include "airport.h" -#include "macros.h" -#include "variables.h" -#include "airport_movement.h" -#include "date.h" - -/* Uncomment this to print out a full report of the airport-structure - * You should either use - * - true: full-report, print out every state and choice with string-names - * OR - * - false: give a summarized report which only shows current and next position */ -//#define DEBUG_AIRPORT false - -static AirportFTAClass *CountryAirport; -static AirportFTAClass *CityAirport; -static AirportFTAClass *Oilrig; -static AirportFTAClass *Heliport; -static AirportFTAClass *MetropolitanAirport; -static AirportFTAClass *InternationalAirport; -static AirportFTAClass *CommuterAirport; -static AirportFTAClass *HeliDepot; -static AirportFTAClass *IntercontinentalAirport; -static AirportFTAClass *HeliStation; - -static void AirportFTAClass_Constructor(AirportFTAClass *apc, - const byte *terminals, const byte *helipads, - const byte entry_point, const byte acc_planes, - const AirportFTAbuildup *apFA, - const TileIndexDiffC *depots, const byte nof_depots, - uint size_x, uint size_y -); -static void AirportFTAClass_Destructor(AirportFTAClass *apc); - -static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA); -static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA); -static byte AirportGetTerminalCount(const byte *terminals, byte *groups); -static byte AirportTestFTA(const AirportFTAClass *apc); - -#ifdef DEBUG_AIRPORT -static void AirportPrintOut(const AirportFTAClass *apc, bool full_report); -#endif /* DEBUG_AIRPORT */ - -void InitializeAirports(void) -{ - // country airport - CountryAirport = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - CountryAirport, - _airport_terminal_country, - NULL, - 16, - ALL, - _airport_fta_country, - _airport_depots_country, - lengthof(_airport_depots_country), - 4, 3 - ); - - // city airport - CityAirport = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - CityAirport, - _airport_terminal_city, - NULL, - 19, - ALL, - _airport_fta_city, - _airport_depots_city, - lengthof(_airport_depots_city), - 6, 6 - ); - - // metropolitan airport - MetropolitanAirport = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - MetropolitanAirport, - _airport_terminal_metropolitan, - NULL, - 20, - ALL, - _airport_fta_metropolitan, - _airport_depots_metropolitan, - lengthof(_airport_depots_metropolitan), - 6, 6 - ); - - // international airport - InternationalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - InternationalAirport, - _airport_terminal_international, - _airport_helipad_international, - 37, - ALL, - _airport_fta_international, - _airport_depots_international, - lengthof(_airport_depots_international), - 7, 7 - ); - - // intercontintental airport - IntercontinentalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - IntercontinentalAirport, - _airport_terminal_intercontinental, - _airport_helipad_intercontinental, - 43, - ALL, - _airport_fta_intercontinental, - _airport_depots_intercontinental, - lengthof(_airport_depots_intercontinental), - 9,11 - ); - - // heliport, oilrig - Heliport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - Heliport, - NULL, - _airport_helipad_heliport_oilrig, - 7, - HELICOPTERS_ONLY, - _airport_fta_heliport_oilrig, - NULL, - 0, - 1, 1 - ); - - Oilrig = Heliport; // exactly the same structure for heliport/oilrig, so share state machine - - // commuter airport - CommuterAirport = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - CommuterAirport, - _airport_terminal_commuter, - _airport_helipad_commuter, - 22, - ALL, - _airport_fta_commuter, - _airport_depots_commuter, - lengthof(_airport_depots_commuter), - 5,4 - ); - - // helidepot airport - HeliDepot = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - HeliDepot, - NULL, - _airport_helipad_helidepot, - 4, - HELICOPTERS_ONLY, - _airport_fta_helidepot, - _airport_depots_helidepot, - lengthof(_airport_depots_helidepot), - 2,2 - ); - - // helistation airport - HeliStation = malloc(sizeof(AirportFTAClass)); - - AirportFTAClass_Constructor( - HeliStation, - NULL, - _airport_helipad_helistation, - 25, - HELICOPTERS_ONLY, - _airport_fta_helistation, - _airport_depots_helistation, - lengthof(_airport_depots_helistation), - 4,2 - ); - -} - -void UnInitializeAirports(void) -{ - AirportFTAClass_Destructor(CountryAirport); - AirportFTAClass_Destructor(CityAirport); - AirportFTAClass_Destructor(Heliport); - AirportFTAClass_Destructor(MetropolitanAirport); - AirportFTAClass_Destructor(InternationalAirport); - AirportFTAClass_Destructor(CommuterAirport); - AirportFTAClass_Destructor(HeliDepot); - AirportFTAClass_Destructor(IntercontinentalAirport); - AirportFTAClass_Destructor(HeliStation); -} - -static void AirportFTAClass_Constructor(AirportFTAClass *apc, - const byte *terminals, const byte *helipads, - const byte entry_point, const byte acc_planes, - const AirportFTAbuildup *apFA, - const TileIndexDiffC *depots, const byte nof_depots, - uint size_x, uint size_y -) -{ - byte nofterminals, nofhelipads; - byte nofterminalgroups, nofhelipadgroups; - - apc->size_x = size_x; - apc->size_y = size_y; - - /* Set up the terminal and helipad count for an airport. - * TODO: If there are more than 10 terminals or 4 helipads, internal variables - * need to be changed, so don't allow that for now */ - nofterminals = AirportGetTerminalCount(terminals, &nofterminalgroups); - if (nofterminals > MAX_TERMINALS) { - DEBUG(misc, 0, "[Ap] only a maximum of %d terminals are supported (requested %d)", MAX_TERMINALS, nofterminals); - assert(nofterminals <= MAX_TERMINALS); - } - apc->terminals = terminals; - - nofhelipads = AirportGetTerminalCount(helipads, &nofhelipadgroups); - if (nofhelipads > MAX_HELIPADS) { - DEBUG(misc, 0, "[Ap] only a maximum of %d helipads are supported (requested %d)", MAX_HELIPADS, nofhelipads); - assert(nofhelipads <= MAX_HELIPADS); - } - apc->helipads = helipads; - - /* Get the number of elements from the source table. We also double check this - * with the entry point which must be within bounds and use this information - * later on to build and validate the state machine */ - apc->nofelements = AirportGetNofElements(apFA); - if (entry_point >= apc->nofelements) { - DEBUG(misc, 0, "[Ap] entry (%d) must be within the airport (maximum %d)", entry_point, apc->nofelements); - assert(entry_point < apc->nofelements); - } - - apc->acc_planes = acc_planes; - apc->entry_point = entry_point; - apc->airport_depots = depots; - apc->nof_depots = nof_depots; - - /* Build the state machine itself */ - AirportBuildAutomata(apc, apFA); - DEBUG(misc, 2, "[Ap] #count %3d; #term %2d (%dgrp); #helipad %2d (%dgrp); entry %3d", - apc->nofelements, nofterminals, nofterminalgroups, nofhelipads, nofhelipadgroups, apc->entry_point); - - /* Test if everything went allright. This is only a rude static test checking - * the symantic correctness. By no means does passing the test mean that the - * airport is working correctly or will not deadlock for example */ - { byte ret = AirportTestFTA(apc); - if (ret != MAX_ELEMENTS) DEBUG(misc, 0, "[Ap] problem with element: %d", ret - 1); - assert(ret == MAX_ELEMENTS); - } - -#ifdef DEBUG_AIRPORT - AirportPrintOut(apc, DEBUG_AIRPORT); -#endif -} - -static void AirportFTAClass_Destructor(AirportFTAClass *apc) -{ - int i; - AirportFTA *current, *next; - - for (i = 0; i < apc->nofelements; i++) { - current = apc->layout[i].next; - while (current != NULL) { - next = current->next; - free(current); - current = next; - }; - } - free(apc->layout); - free(apc); -} - -/** Get the number of elements of a source Airport state automata - * Since it is actually just a big array of AirportFTA types, we only - * know one element from the other by differing 'position' identifiers */ -static uint16 AirportGetNofElements(const AirportFTAbuildup *apFA) -{ - int i; - uint16 nofelements = 0; - int temp = apFA[0].position; - - for (i = 0; i < MAX_ELEMENTS; i++) { - if (temp != apFA[i].position) { - nofelements++; - temp = apFA[i].position; - } - if (apFA[i].position == MAX_ELEMENTS) break; - } - return nofelements; -} - -/* We calculate the terminal/helipod count based on the data passed to us - * This data (terminals) contains an index as a first element as to how many - * groups there are, and then the number of terminals for each group */ -static byte AirportGetTerminalCount(const byte *terminals, byte *groups) -{ - byte i; - byte nof_terminals = 0; - *groups = 0; - - if (terminals != NULL) { - i = terminals[0]; - *groups = i; - while (i-- > 0) { - terminals++; - assert(*terminals != 0); // no empty groups please - nof_terminals += *terminals; - } - } - return nof_terminals; -} - -static void AirportBuildAutomata(AirportFTAClass *apc, const AirportFTAbuildup *apFA) -{ - AirportFTA *current; - AirportFTA *FAutomata = malloc(sizeof(AirportFTA) * apc->nofelements); - uint16 internalcounter = 0; - uint16 i; - - apc->layout = FAutomata; - for (i = 0; i < apc->nofelements; i++) { - current = &apc->layout[i]; - current->position = apFA[internalcounter].position; - current->heading = apFA[internalcounter].heading; - current->block = apFA[internalcounter].block; - current->next_position = apFA[internalcounter].next; - - // outgoing nodes from the same position, create linked list - while (current->position == apFA[internalcounter + 1].position) { - AirportFTA *newNode = malloc(sizeof(AirportFTA)); - - newNode->position = apFA[internalcounter + 1].position; - newNode->heading = apFA[internalcounter + 1].heading; - newNode->block = apFA[internalcounter + 1].block; - newNode->next_position = apFA[internalcounter + 1].next; - // create link - current->next = newNode; - current = current->next; - internalcounter++; - } // while - current->next = NULL; - internalcounter++; - } -} - -static byte AirportTestFTA(const AirportFTAClass *apc) -{ - byte position, i, next_position; - AirportFTA *current, *first; - next_position = 0; - - for (i = 0; i < apc->nofelements; i++) { - position = apc->layout[i].position; - if (position != next_position) return i; - current = first = &apc->layout[i]; - - for (; current != NULL; current = current->next) { - /* A heading must always be valid. The only exceptions are - * - multiple choices as start, identified by a special value of 255 - * - terminal group which is identified by a special value of 255 */ - if (current->heading > MAX_HEADINGS) { - if (current->heading != 255) return i; - if (current == first && current->next == NULL) return i; - if (current != first && current->next_position > apc->terminals[0]) return i; - } - - /* If there is only one choice, it must be at the end */ - if (current->heading == 0 && current->next != NULL) return i; - /* Obviously the elements of the linked list must have the same identifier */ - if (position != current->position) return i; - /* A next position must be within bounds */ - if (current->next_position >= apc->nofelements) return i; - } - next_position++; - } - return MAX_ELEMENTS; -} - -#ifdef DEBUG_AIRPORT -static const char* const _airport_heading_strings[] = { - "TO_ALL", - "HANGAR", - "TERM1", - "TERM2", - "TERM3", - "TERM4", - "TERM5", - "TERM6", - "HELIPAD1", - "HELIPAD2", - "TAKEOFF", - "STARTTAKEOFF", - "ENDTAKEOFF", - "HELITAKEOFF", - "FLYING", - "LANDING", - "ENDLANDING", - "HELILANDING", - "HELIENDLANDING", - "TERM7", - "TERM8", - "HELIPAD3", - "HELIPAD4", - "DUMMY" // extra heading for 255 -}; - -static uint AirportBlockToString(uint32 block) -{ - uint i = 0; - if (block & 0xffff0000) { block >>= 16; i += 16; } - if (block & 0x0000ff00) { block >>= 8; i += 8; } - if (block & 0x000000f0) { block >>= 4; i += 4; } - if (block & 0x0000000c) { block >>= 2; i += 2; } - if (block & 0x00000002) { i += 1; } - return i; -} - -static void AirportPrintOut(const AirportFTAClass *apc, bool full_report) -{ - uint16 i; - - if (!full_report) printf("(P = Current Position; NP = Next Position)\n"); - - for (i = 0; i < apc->nofelements; i++) { - AirportFTA *current = &apc->layout[i]; - - for (; current != NULL; current = current->next) { - if (full_report) { - byte heading = (current->heading == 255) ? MAX_HEADINGS + 1 : current->heading; - printf("\tPos:%2d NPos:%2d Heading:%15s Block:%2d\n", current->position, - current->next_position, _airport_heading_strings[heading], - AirportBlockToString(current->block)); - } else { - printf("P:%2d NP:%2d", current->position, current->next_position); - } - } - printf("\n"); - } -} -#endif - -const AirportFTAClass *GetAirport(const byte airport_type) -{ - //FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code - // needs constant change if more airports are added - switch (airport_type) { - default: NOT_REACHED(); - case AT_SMALL: return CountryAirport; - case AT_LARGE: return CityAirport; - case AT_METROPOLITAN: return MetropolitanAirport; - case AT_HELIPORT: return Heliport; - case AT_OILRIG: return Oilrig; - case AT_INTERNATIONAL: return InternationalAirport; - case AT_COMMUTER: return CommuterAirport; - case AT_HELIDEPOT: return HeliDepot; - case AT_INTERCON: return IntercontinentalAirport; - case AT_HELISTATION: return HeliStation; - } -} - -const AirportMovingData *GetAirportMovingData(byte airport_type, byte position) -{ - assert(airport_type < lengthof(_airport_moving_datas)); - assert(position < GetAirport(airport_type)->nofelements); - return &_airport_moving_datas[airport_type][position]; -} - -uint32 GetValidAirports(void) -{ - uint32 bytemask = _avail_aircraft; /// sets the first 3 bytes, 0 - 2, @see AdjustAvailAircraft() - - if (_cur_year >= 1980) SETBIT(bytemask, 3); // metropolitan airport - if (_cur_year >= 1990) SETBIT(bytemask, 4); // international airport - if (_cur_year >= 1983) SETBIT(bytemask, 5); // commuter airport - if (_cur_year >= 1976) SETBIT(bytemask, 6); // helidepot - if (_cur_year >= 2002) SETBIT(bytemask, 7); // intercontinental airport - if (_cur_year >= 1980) SETBIT(bytemask, 8); // helistation - return bytemask; -} diff --git a/airport.h b/airport.h deleted file mode 100644 --- a/airport.h +++ /dev/null @@ -1,163 +0,0 @@ -/* $Id$ */ - -#ifndef AIRPORT_H -#define AIRPORT_H - -enum {MAX_TERMINALS = 10}; -enum {MAX_HELIPADS = 4}; -enum {MAX_ELEMENTS = 255}; -enum {MAX_HEADINGS = 22}; - -// Airport types -enum { - AT_SMALL = 0, - AT_LARGE = 1, - AT_HELIPORT = 2, - AT_METROPOLITAN = 3, - AT_INTERNATIONAL = 4, - AT_COMMUTER = 5, - AT_HELIDEPOT = 6, - AT_INTERCON = 7, - AT_HELISTATION = 8, - AT_OILRIG = 15 -}; - -// do not change unless you change v->subtype too. This aligns perfectly with its current setting -enum { - AIRCRAFT_ONLY = 0, - ALL = 1, - HELICOPTERS_ONLY = 2, -}; - -enum { - AMED_NOSPDCLAMP = 1 << 0, - AMED_TAKEOFF = 1 << 1, - AMED_SLOWTURN = 1 << 2, - AMED_LAND = 1 << 3, - AMED_EXACTPOS = 1 << 4, - AMED_BRAKE = 1 << 5, - AMED_HELI_RAISE = 1 << 6, - AMED_HELI_LOWER = 1 << 7, -}; - -/* Movement States on Airports (headings target) */ -enum { - TO_ALL = 0, - HANGAR = 1, - TERM1 = 2, - TERM2 = 3, - TERM3 = 4, - TERM4 = 5, - TERM5 = 6, - TERM6 = 7, - HELIPAD1 = 8, - HELIPAD2 = 9, - TAKEOFF = 10, - STARTTAKEOFF = 11, - ENDTAKEOFF = 12, - HELITAKEOFF = 13, - FLYING = 14, - LANDING = 15, - ENDLANDING = 16, - HELILANDING = 17, - HELIENDLANDING = 18, - TERM7 = 19, - TERM8 = 20, - HELIPAD3 = 21, - HELIPAD4 = 22 -}; - -// this maps the terminal to its corresponding state and block flag -// currently set for 10 terms, 4 helipads -static const byte _airport_terminal_state[] = {2, 3, 4, 5, 6, 7, 19, 20, 0, 0, 8, 9, 21, 22}; -static const byte _airport_terminal_flag[] = {0, 1, 2, 3, 4, 5, 22, 23, 0, 0, 6, 7, 24, 25}; - -/* Movement Blocks on Airports */ -// blocks (eg_airport_flags) -enum { - TERM1_block = 1 << 0, - TERM2_block = 1 << 1, - TERM3_block = 1 << 2, - TERM4_block = 1 << 3, - TERM5_block = 1 << 4, - TERM6_block = 1 << 5, - HELIPAD1_block = 1 << 6, - HELIPAD2_block = 1 << 7, - RUNWAY_IN_OUT_block = 1 << 8, - RUNWAY_IN_block = 1 << 8, - AIRPORT_BUSY_block = 1 << 8, - RUNWAY_OUT_block = 1 << 9, - TAXIWAY_BUSY_block = 1 << 10, - OUT_WAY_block = 1 << 11, - IN_WAY_block = 1 << 12, - AIRPORT_ENTRANCE_block = 1 << 13, - TERM_GROUP1_block = 1 << 14, - TERM_GROUP2_block = 1 << 15, - HANGAR2_AREA_block = 1 << 16, - TERM_GROUP2_ENTER1_block = 1 << 17, - TERM_GROUP2_ENTER2_block = 1 << 18, - TERM_GROUP2_EXIT1_block = 1 << 19, - TERM_GROUP2_EXIT2_block = 1 << 20, - PRE_HELIPAD_block = 1 << 21, - -// blocks for new airports - TERM7_block = 1 << 22, - TERM8_block = 1 << 23, - TERM9_block = 1 << 24, - HELIPAD3_block = 1 << 24, - TERM10_block = 1 << 25, - HELIPAD4_block = 1 << 25, - HANGAR1_AREA_block = 1 << 26, - OUT_WAY2_block = 1 << 27, - IN_WAY2_block = 1 << 28, - RUNWAY_IN2_block = 1 << 29, - RUNWAY_OUT2_block = 1 << 10, // note re-uses TAXIWAY_BUSY - HELIPAD_GROUP_block = 1 << 13, // note re-uses AIRPORT_ENTRANCE - OUT_WAY_block2 = 1 << 31, -// end of new blocks - - NOTHING_block = 1 << 30 -}; - -typedef struct AirportMovingData { - int x,y; - byte flag; - byte direction; -} AirportMovingData; - -// Finite sTate mAchine --> FTA -typedef struct AirportFTAClass { - byte nofelements; // number of positions the airport consists of - const byte *terminals; - const byte *helipads; - byte entry_point; // when an airplane arrives at this airport, enter it at position entry_point - byte acc_planes; // accept airplanes or helicopters or both - const TileIndexDiffC *airport_depots; // gives the position of the depots on the airports - byte nof_depots; // number of depots this airport has - struct AirportFTA *layout; // state machine for airport - byte size_x; - byte size_y; -} AirportFTAClass; - -// internal structure used in openttd - Finite sTate mAchine --> FTA -typedef struct AirportFTA { - byte position; // the position that an airplane is at - byte next_position; // next position from this position - uint32 block; // 32 bit blocks (st->airport_flags), should be enough for the most complex airports - byte heading; // heading (current orders), guiding an airplane to its target on an airport - struct AirportFTA *next; // possible extra movement choices from this position -} AirportFTA; - -void InitializeAirports(void); -void UnInitializeAirports(void); -const AirportFTAClass *GetAirport(const byte airport_type); -const AirportMovingData *GetAirportMovingData(byte airport_type, byte position); - -/** Get buildable airport bitmask. - * @return get all buildable airports at this given time, bitmasked. - * Bit 0 means the small airport is buildable, etc. - * @todo set availability of airports by year, instead of airplane - */ -uint32 GetValidAirports(void); - -#endif /* AIRPORT_H */ diff --git a/airport_gui.c b/airport_gui.c deleted file mode 100644 --- a/airport_gui.c +++ /dev/null @@ -1,286 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "table/sprites.h" -#include "table/strings.h" -#include "functions.h" -#include "map.h" -#include "window.h" -#include "gui.h" -#include "viewport.h" -#include "gfx.h" -#include "sound.h" -#include "command.h" -#include "vehicle.h" -#include "station.h" -#include "airport.h" -#include "depot.h" - -static byte _selected_airport_type; - -static void ShowBuildAirportPicker(void); - - -void CcBuildAirport(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - if (success) { - SndPlayTileFx(SND_1F_SPLAT, tile); - ResetObjectToPlace(); - } -} - -static void PlaceAirport(TileIndex tile) -{ - DoCommandP(tile, _selected_airport_type, 0, CcBuildAirport, CMD_BUILD_AIRPORT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_A001_CAN_T_BUILD_AIRPORT_HERE)); -} - -static void PlaceAir_DemolishArea(TileIndex tile) -{ - VpStartPlaceSizing(tile, 4); -} - - -enum { - ATW_AIRPORT = 3, - ATW_DEMOLISH = 4 -}; - - -static void BuildAirClick_Airport(Window *w) -{ - if (HandlePlacePushButton(w, ATW_AIRPORT, SPR_CURSOR_AIRPORT, 1, PlaceAirport)) ShowBuildAirportPicker(); -} - -static void BuildAirClick_Demolish(Window *w) -{ - HandlePlacePushButton(w, ATW_DEMOLISH, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea); -} - -static void BuildAirClick_Landscaping(Window *w) -{ - ShowTerraformToolbar(); -} - -typedef void OnButtonClick(Window *w); -static OnButtonClick * const _build_air_button_proc[] = { - BuildAirClick_Airport, - BuildAirClick_Demolish, - BuildAirClick_Landscaping, -}; - -static void BuildAirToolbWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: - DrawWindowWidgets(w); - break; - - case WE_CLICK: - if (e->we.click.widget - 3 >= 0) - _build_air_button_proc[e->we.click.widget - 3](w); - break; - - case WE_KEYPRESS: { - switch (e->we.keypress.keycode) { - case '1': BuildAirClick_Airport(w); break; - case '2': BuildAirClick_Demolish(w); break; - case 'l': BuildAirClick_Landscaping(w); break; - default: return; - } - } break; - - case WE_PLACE_OBJ: - _place_proc(e->we.place.tile); - break; - - case WE_PLACE_DRAG: - VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.userdata); - break; - - case WE_PLACE_MOUSEUP: - if (e->we.place.pt.x != -1) { - DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA)); - } - break; - - case WE_ABORT_PLACE_OBJ: - RaiseWindowButtons(w); - - w = FindWindowById(WC_BUILD_STATION, 0); - if (w != 0) - WP(w,def_d).close = true; - break; - - case WE_DESTROY: - if (_patches.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0); - break; - } -} - -static const Widget _air_toolbar_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 73, 0, 13, STR_A000_AIRPORTS, STR_018C_WINDOW_TITLE_DRAG_THIS }, -{ WWT_STICKYBOX, RESIZE_NONE, 7, 74, 85, 0, 13, 0x0, STR_STICKY_BUTTON }, -{ WWT_IMGBTN, RESIZE_NONE, 7, 0, 41, 14, 35, SPR_IMG_AIRPORT, STR_A01E_BUILD_AIRPORT }, -{ WWT_IMGBTN, RESIZE_NONE, 7, 42, 63, 14, 35, SPR_IMG_DYNAMITE, STR_018D_DEMOLISH_BUILDINGS_ETC }, -{ WWT_IMGBTN, RESIZE_NONE, 7, 64, 85, 14, 35, SPR_IMG_LANDSCAPING, STR_LANDSCAPING_TOOLBAR_TIP }, -{ WIDGETS_END}, -}; - - -static const WindowDesc _air_toolbar_desc = { - WDP_ALIGN_TBR, 22, 86, 36, - WC_BUILD_TOOLBAR, 0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON, - _air_toolbar_widgets, - BuildAirToolbWndProc -}; - -void ShowBuildAirToolbar(void) -{ - if (!IsValidPlayer(_current_player)) return; - - DeleteWindowById(WC_BUILD_TOOLBAR, 0); - AllocateWindowDescFront(&_air_toolbar_desc, 0); - if (_patches.link_terraform_toolbar) ShowTerraformToolbar(); -} - -static void BuildAirportPickerWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_CREATE: - SetWindowWidgetLoweredState(w, 16, !_station_show_coverage); - SetWindowWidgetLoweredState(w, 17, _station_show_coverage); - LowerWindowWidget(w, _selected_airport_type + 7); - break; - - case WE_PAINT: { - int i; // airport enabling loop - int rad = 4; // default catchment radious - uint32 avail_airports; - const AirportFTAClass *airport; - - if (WP(w,def_d).close) return; - - avail_airports = GetValidAirports(); - - RaiseWindowWidget(w, _selected_airport_type + 7); - if (!HASBIT(avail_airports, 0) && _selected_airport_type == AT_SMALL) _selected_airport_type = AT_LARGE; - if (!HASBIT(avail_airports, 1) && _selected_airport_type == AT_LARGE) _selected_airport_type = AT_SMALL; - LowerWindowWidget(w, _selected_airport_type + 7); - - /* 'Country Airport' starts at widget 7, and if its bit is set, it is - * available, so take its opposite value to set the disabled state. - * There are 9 buildable airports - * XXX TODO : all airports should be held in arrays, with all relevant data. - * This should be part of newgrf-airports, i suppose - */ - for (i = 0; i < 9; i++) SetWindowWidgetDisabledState(w, i + 7, !HASBIT(avail_airports, i)); - - // select default the coverage area to 'Off' (16) - airport = GetAirport(_selected_airport_type); - SetTileSelectSize(airport->size_x, airport->size_y); - - if (_patches.modified_catchment) { - switch (_selected_airport_type) { - case AT_OILRIG: rad = CA_AIR_OILPAD; break; - case AT_HELIPORT: rad = CA_AIR_HELIPORT; break; - case AT_SMALL: rad = CA_AIR_SMALL; break; - case AT_LARGE: rad = CA_AIR_LARGE; break; - case AT_METROPOLITAN: rad = CA_AIR_METRO; break; - case AT_INTERNATIONAL: rad = CA_AIR_INTER; break; - case AT_COMMUTER: rad = CA_AIR_COMMUTER; break; - case AT_HELIDEPOT: rad = CA_AIR_HELIDEPOT; break; - case AT_INTERCON: rad = CA_AIR_INTERCON; break; - case AT_HELISTATION: rad = CA_AIR_HELISTATION; break; - } - } - - if (_station_show_coverage) SetTileSelectBigSize(-rad, -rad, 2 * rad, 2 * rad); - - DrawWindowWidgets(w); - // strings such as 'Size' and 'Coverage Area' - // 'Coverage Area' - DrawStationCoverageAreaText(2, 206, (uint)-1, rad); - break; - } - - case WE_CLICK: { - switch (e->we.click.widget) { - case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: - RaiseWindowWidget(w, _selected_airport_type + 7); - _selected_airport_type = e->we.click.widget - 7; - LowerWindowWidget(w, _selected_airport_type + 7); - SndPlayFx(SND_15_BEEP); - SetWindowDirty(w); - break; - case 16: case 17: - _station_show_coverage = e->we.click.widget - 16; - SetWindowWidgetLoweredState(w, 16, !_station_show_coverage); - SetWindowWidgetLoweredState(w, 17, _station_show_coverage); - SndPlayFx(SND_15_BEEP); - SetWindowDirty(w); - break; - } - } break; - - case WE_MOUSELOOP: { - if (WP(w,def_d).close) { - DeleteWindow(w); - return; - } - - CheckRedrawStationCoverage(w); - } break; - - case WE_DESTROY: - if (!WP(w,def_d).close) ResetObjectToPlace(); - break; - } -} - -static const Widget _build_airport_picker_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 147, 0, 13, STR_3001_AIRPORT_SELECTION, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 14, 52, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 53, 89, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 90, 127, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 128, 177, 0x0, STR_NULL}, -{ WWT_PANEL, RESIZE_NONE, 7, 0, 147, 178, 239, 0x0, STR_NULL}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 27, 38, STR_SMALL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 65, 76, STR_CITY_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 141, 152, STR_HELIPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 77, 88, STR_METRO_AIRPORT , STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 103, 114, STR_INTERNATIONAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 39, 50, STR_COMMUTER_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 165, 176, STR_HELIDEPOT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 115, 126, STR_INTERCONTINENTAL_AIRPORT, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 2, 145, 153, 164, STR_HELISTATION, STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 14, 73, 191, 202, STR_02DB_OFF, STR_3065_DON_T_HIGHLIGHT_COVERAGE}, -{ WWT_TEXTBTN, RESIZE_NONE, 14, 74, 133, 191, 202, STR_02DA_ON, STR_3064_HIGHLIGHT_COVERAGE_AREA}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 14, 27, STR_SMALL_AIRPORTS, STR_NULL}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 52, 65, STR_LARGE_AIRPORTS, STR_NULL}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 90, 103, STR_HUB_AIRPORTS, STR_NULL}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 128, 141, STR_HELIPORTS, STR_NULL}, -{ WWT_LABEL, RESIZE_NONE, 7, 0, 147, 178, 191, STR_3066_COVERAGE_AREA_HIGHLIGHT, STR_NULL}, -{ WIDGETS_END}, -}; - -static const WindowDesc _build_airport_desc = { - WDP_AUTO, WDP_AUTO, 148, 240, - WC_BUILD_STATION, WC_BUILD_TOOLBAR, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_airport_picker_widgets, - BuildAirportPickerWndProc -}; - -static void ShowBuildAirportPicker(void) -{ - AllocateWindowDesc(&_build_airport_desc); -} - -void InitializeAirportGui(void) -{ - _selected_airport_type = AT_SMALL; -} diff --git a/airport_movement.h b/airport_movement.h deleted file mode 100644 --- a/airport_movement.h +++ /dev/null @@ -1,794 +0,0 @@ -/* $Id$ */ - -#ifndef AIRPORT_MOVEMENT_H -#define AIRPORT_MOVEMENT_H - - -// state machine input struct (from external file, etc.) -// Finite sTate mAchine --> FTA -typedef struct AirportFTAbuildup { - byte position; // the position that an airplane is at - byte heading; // the current orders (eg. TAKEOFF, HANGAR, ENDLANDING, etc.) - uint32 block; // the block this position is on on the airport (st->airport_flags) - byte next; // next position from this position -} AirportFTAbuildup; - -/////////////////////////////////////////////////////////////////////// -/////*********Movement Positions on Airports********************/////// -// Country Airfield (small) 4x3 -static const AirportMovingData _airport_moving_data_country[22] = { - { 53, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar - { 53, 27, 0, 0 }, // 01 Taxi to right outside depot - { 32, 23, AMED_EXACTPOS, 7 }, // 02 Terminal 1 - { 10, 23, AMED_EXACTPOS, 7 }, // 03 Terminal 2 - { 43, 37, 0, 0 }, // 04 Going towards terminal 2 - { 24, 37, 0, 0 }, // 05 Going towards terminal 2 - { 53, 37, 0, 0 }, // 06 Going for takeoff - { 61, 40, AMED_EXACTPOS, 1 }, // 07 Taxi to start of runway (takeoff) - { 3, 40, AMED_NOSPDCLAMP, 0 }, // 08 Accelerate to end of runway - { -79, 40, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 09 Take off - { 177, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 10 Fly to landing position in air - { 56, 40, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 11 Going down for land - { 3, 40, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 12 Just landed, brake until end of runway - { 7, 40, 0, 0 }, // 13 Just landed, turn around and taxi 1 square - { 53, 40, 0, 0 }, // 14 Taxi from runway to crossing - { -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 15 Fly around waiting for a landing spot (north-east) - { 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Fly around waiting for a landing spot (north-west) - { 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 17 Fly around waiting for a landing spot (south-west) - { 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Fly around waiting for a landing spot (south) - { 44, 37, AMED_HELI_RAISE, 0 }, // 19 Helicopter takeoff - { 44, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 In position above landing spot helicopter - { 44, 40, AMED_HELI_LOWER, 0 }, // 21 Helicopter landing -}; - -// Commuter Airfield (small) 5x4 -static const AirportMovingData _airport_moving_data_commuter[37] = { - { 69, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar - { 72, 22, 0, 0 }, // 01 Taxi to right outside depot - { 8, 22, AMED_EXACTPOS, 5 }, // 01 Taxi to right outside depot - { 24, 36, AMED_EXACTPOS, 3 }, // 03 Terminal 1 - { 40, 36, AMED_EXACTPOS, 3 }, // 04 Terminal 2 - { 56, 36, AMED_EXACTPOS, 3 }, // 05 Terminal 3 - { 40, 8, AMED_EXACTPOS, 1 }, // 06 Helipad 1 - { 56, 8, AMED_EXACTPOS, 1 }, // 07 Helipad 2 - { 24, 22, 0, 5 }, // 08 Taxiing - { 40, 22, 0, 5 }, // 09 Taxiing - { 56, 22, 0, 5 }, // 10 Taxiing - { 72, 40, 0, 3 }, // 11 Airport OUTWAY - { 72, 54, AMED_EXACTPOS, 1 }, // 12 Accelerate to end of runway - { 7, 54, AMED_NOSPDCLAMP, 0 }, // 13 Release control of runway, for smoother movement - { 5, 54, AMED_NOSPDCLAMP, 0 }, // 14 End of runway - { -79, 54, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 15 Take off - { 145, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Fly to landing position in air - { 73, 54, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 17 Going down for land - { 3, 54, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 18 Just landed, brake until end of runway - { 12, 54, 0, 7 }, // 19 Just landed, turn around and taxi - { 8, 32, 0, 7 }, // 20 Taxi from runway to crossing - { -31, 149, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (north-east) - { 1, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 22 Fly around waiting for a landing spot (north-west) - { 193, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 Fly around waiting for a landing spot (south-west) - { 225, 81, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 24 Fly around waiting for a landing spot (south) - // Helicopter - { 80, 0, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Bufferspace before helipad - { 80, 0, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 26 Bufferspace before helipad - { 32, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 27 Get in position for Helipad1 - { 48, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 28 Get in position for Helipad2 - { 32, 8, AMED_HELI_LOWER, 0 }, // 29 Land at Helipad1 - { 48, 8, AMED_HELI_LOWER, 0 }, // 30 Land at Helipad2 - { 32, 8, AMED_HELI_RAISE, 0 }, // 31 Takeoff Helipad1 - { 48, 8, AMED_HELI_RAISE, 0 }, // 32 Takeoff Helipad2 - { 64, 22, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 33 Go to position for Hangarentrance in air - { 64, 22, AMED_HELI_LOWER, 0 }, // 34 Land in front of hangar - { 40, 8, AMED_EXACTPOS, 0 }, // pre-helitakeoff helipad 1 - { 56, 8, AMED_EXACTPOS, 0 }, // pre-helitakeoff helipad 2 -}; - -// City Airport (large) 6x6 -static const AirportMovingData _airport_moving_data_town[25] = { - { 85, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar - { 85, 27, 0, 0 }, // 01 Taxi to right outside depot - { 26, 41, AMED_EXACTPOS, 5 }, // 02 Terminal 1 - { 56, 20, AMED_EXACTPOS, 3 }, // 03 Terminal 2 - { 38, 8, AMED_EXACTPOS, 5 }, // 04 Terminal 3 - { 65, 6, 0, 0 }, // 05 Taxi to right in infront of terminal 2/3 - { 80, 27, 0, 0 }, // 06 Taxiway terminals 2-3 - { 44, 63, 0, 0 }, // 07 Taxi to Airport center - { 58, 71, 0, 0 }, // 08 Towards takeoff - { 72, 85, 0, 0 }, // 09 Taxi to runway (takeoff) - { 89, 85, AMED_EXACTPOS, 1 }, // 10 Taxi to start of runway (takeoff) - { 3, 85, AMED_NOSPDCLAMP, 0 }, // 11 Accelerate to end of runway - { -79, 85, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 12 Take off - { 177, 85, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 13 Fly to landing position in air - { 89, 85, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 14 Going down for land - { 3, 85, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 15 Just landed, brake until end of runway - { 20, 87, 0, 0 }, // 16 Just landed, turn around and taxi 1 square - { 36, 71, 0, 0 }, // 17 Taxi from runway to crossing - { -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Fly around waiting for a landing spot (north-east) - { 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 19 Fly around waiting for a landing spot (north-west) - { 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 Fly around waiting for a landing spot (south-west) - { 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (south) - { 44, 63, AMED_HELI_RAISE, 0 }, // 22 Helicopter takeoff - { 28, 74, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 In position above landing spot helicopter - { 28, 74, AMED_HELI_LOWER, 0 }, // 24 Helicopter landing -}; - -// Metropolitan Airport (metropolitan) - 2 runways -static const AirportMovingData _airport_moving_data_metropolitan[27] = { - { 85, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar - { 85, 27, 0, 0 }, // 01 Taxi to right outside depot - { 26, 41, AMED_EXACTPOS, 5 }, // 02 Terminal 1 - { 56, 20, AMED_EXACTPOS, 3 }, // 03 Terminal 2 - { 38, 8, AMED_EXACTPOS, 5 }, // 04 Terminal 3 - { 65, 6, 0, 0 }, // 05 Taxi to right in infront of terminal 2/3 - { 70, 33, 0, 0 }, // 06 Taxiway terminals 2-3 - { 44, 58, 0, 0 }, // 07 Taxi to Airport center - { 72, 58, 0, 0 }, // 08 Towards takeoff - { 72, 69, 0, 0 }, // 09 Taxi to runway (takeoff) - { 89, 69, AMED_EXACTPOS, 1 }, // 10 Taxi to start of runway (takeoff) - { 3, 69, AMED_NOSPDCLAMP, 0 }, // 11 Accelerate to end of runway - { -79, 69, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 12 Take off - { 177, 85, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 13 Fly to landing position in air - { 89, 85, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 14 Going down for land - { 3, 85, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 15 Just landed, brake until end of runway - { 21, 85, 0, 0 }, // 16 Just landed, turn around and taxi 1 square - { 21, 69, 0, 0 }, // 17 On Runway-out taxiing to In-Way - { 21, 54, AMED_EXACTPOS, 5 }, // 18 Taxi from runway to crossing - { -31, 193, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 19 Fly around waiting for a landing spot (north-east) - { 1, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 20 Fly around waiting for a landing spot (north-west) - { 257, 1, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 21 Fly around waiting for a landing spot (south-west) - { 273, 49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 22 Fly around waiting for a landing spot (south) - { 44, 58, 0, 0 }, // 23 Helicopter takeoff spot on ground (to clear airport sooner) - { 44, 63, AMED_HELI_RAISE, 0 }, // 24 Helicopter takeoff - { 15, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Get in position above landing spot helicopter - { 15, 54, AMED_HELI_LOWER, 0 }, // 26 Helicopter landing -}; - -// International Airport (international) - 2 runways, 6 terminals, dedicated helipod -static const AirportMovingData _airport_moving_data_international[51] = { - { 7, 55, AMED_EXACTPOS, 3 }, // 00 In Hangar 1 - { 100, 21, AMED_EXACTPOS, 3 }, // 01 In Hangar 2 - { 7, 70, 0, 0 }, // 02 Taxi to right outside depot - { 100, 36, 0, 0 }, // 03 Taxi to right outside depot - { 38, 70, AMED_EXACTPOS, 5 }, // 04 Terminal 1 - { 38, 54, AMED_EXACTPOS, 5 }, // 05 Terminal 2 - { 38, 38, AMED_EXACTPOS, 5 }, // 06 Terminal 3 - { 70, 70, AMED_EXACTPOS, 1 }, // 07 Terminal 4 - { 70, 54, AMED_EXACTPOS, 1 }, // 08 Terminal 5 - { 70, 38, AMED_EXACTPOS, 1 }, // 09 Terminal 6 - { 104, 71, AMED_EXACTPOS, 1 }, // 10 Helipad 1 - { 104, 55, AMED_EXACTPOS, 1 }, // 11 Helipad 2 - { 22, 87, 0, 0 }, // 12 Towards Terminals 4/5/6, Helipad 1/2 - { 60, 87, 0, 0 }, // 13 Towards Terminals 4/5/6, Helipad 1/2 - { 66, 87, 0, 0 }, // 14 Towards Terminals 4/5/6, Helipad 1/2 - { 86, 87, AMED_EXACTPOS, 7 }, // 15 Towards Terminals 4/5/6, Helipad 1/2 - { 86, 70, 0, 0 }, // 16 In Front of Terminal 4 / Helipad 1 - { 86, 54, 0, 0 }, // 17 In Front of Terminal 5 / Helipad 2 - { 86, 38, 0, 0 }, // 18 In Front of Terminal 6 - { 86, 22, 0, 0 }, // 19 Towards Terminals Takeoff (Taxiway) - { 66, 22, 0, 0 }, // 20 Towards Terminals Takeoff (Taxiway) - { 60, 22, 0, 0 }, // 21 Towards Terminals Takeoff (Taxiway) - { 38, 22, 0, 0 }, // 22 Towards Terminals Takeoff (Taxiway) - { 22, 70, 0, 0 }, // 23 In Front of Terminal 1 - { 22, 58, 0, 0 }, // 24 In Front of Terminal 2 - { 22, 38, 0, 0 }, // 25 In Front of Terminal 3 - { 22, 22, AMED_EXACTPOS, 7 }, // 26 Going for Takeoff - { 22, 6, 0, 0 }, // 27 On Runway-out, prepare for takeoff - { 3, 6, AMED_EXACTPOS, 5 }, // 28 Accelerate to end of runway - { 60, 6, AMED_NOSPDCLAMP, 0 }, // 29 Release control of runway, for smoother movement - { 105, 6, AMED_NOSPDCLAMP, 0 }, // 30 End of runway - { 190, 6, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 31 Take off - { 193, 104, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 32 Fly to landing position in air - { 105, 104, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 33 Going down for land - { 3, 104, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 34 Just landed, brake until end of runway - { 12, 104, 0, 0 }, // 35 Just landed, turn around and taxi 1 square - { 7, 84, 0, 0 }, // 36 Taxi from runway to crossing - { -31, 209, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 37 Fly around waiting for a landing spot (north-east) - { 1, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 38 Fly around waiting for a landing spot (north-west) - { 273, 6, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 39 Fly around waiting for a landing spot (south-west) - { 305, 81, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 40 Fly around waiting for a landing spot (south) - // Helicopter - { 128, 80, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 41 Bufferspace before helipad - { 128, 80, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 42 Bufferspace before helipad - { 96, 71, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 43 Get in position for Helipad1 - { 96, 55, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 44 Get in position for Helipad2 - { 96, 71, AMED_HELI_LOWER, 0 }, // 45 Land at Helipad1 - { 96, 55, AMED_HELI_LOWER, 0 }, // 46 Land at Helipad2 - { 104, 71, AMED_HELI_RAISE, 0 }, // 47 Takeoff Helipad1 - { 104, 55, AMED_HELI_RAISE, 0 }, // 48 Takeoff Helipad2 - { 104, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 49 Go to position for Hangarentrance in air - { 104, 32, AMED_HELI_LOWER, 0} // 50 Land in HANGAR2_AREA to go to hangar -}; - -// Intercontinental Airport - 4 runways, 8 terminals, 2 dedicated helipads -static const AirportMovingData _airport_moving_data_intercontinental[77] = { - { 7, 87, AMED_EXACTPOS, 3 }, // 00 In Hangar 1 - { 135, 72, AMED_EXACTPOS, 3 }, // 01 In Hangar 2 - { 7, 104, 0, 0 }, // 02 Taxi to right outside depot 1 - { 135, 88, 0, 0 }, // 03 Taxi to right outside depot 2 - { 56, 120, AMED_EXACTPOS, 6 }, // 04 Terminal 1 - { 56, 104, AMED_EXACTPOS, 5 }, // 05 Terminal 2 - { 56, 88, AMED_EXACTPOS, 5 }, // 06 Terminal 3 - { 56, 72, AMED_EXACTPOS, 5 }, // 07 Terminal 4 - { 88, 120, AMED_EXACTPOS, 0 }, // 08 Terminal 5 - { 88, 104, AMED_EXACTPOS, 1 }, // 09 Terminal 6 - { 88, 88, AMED_EXACTPOS, 1 }, // 10 Terminal 7 - { 88, 72, AMED_EXACTPOS, 1 }, // 11 Terminal 8 - { 88, 56, AMED_EXACTPOS, 3 }, // 12 Helipad 1 - { 72, 56, AMED_EXACTPOS, 1 }, // 13 Helipad 2 - { 40, 136, 0, 0 }, // 14 Term group 2 enter 1 a - { 56, 136, 0, 0 }, // 15 Term group 2 enter 1 b - { 88, 136, 0, 0 }, // 16 Term group 2 enter 2 a - { 104, 136, 0, 0 }, // 17 Term group 2 enter 2 b - { 104, 120, 0, 0 }, // 18 Term group 2 - opp term 5 - { 104, 104, 0, 0 }, // 19 Term group 2 - opp term 6 & exit2 - { 104, 88, 0, 0 }, // 20 Term group 2 - opp term 7 & hangar area 2 - { 104, 72, 0, 0 }, // 21 Term group 2 - opp term 8 - { 104, 56, 0, 0 }, // 22 Taxi Term group 2 exit a - { 104, 40, 0, 0 }, // 23 Taxi Term group 2 exit b - { 56, 40, 0, 0 }, // 24 Term group 2 exit 2a - { 40, 40, 0, 0 }, // 25 Term group 2 exit 2b - { 40, 120, 0, 0 }, // 26 Term group 1 - opp term 1 - { 40, 104, 0, 0 }, // 27 Term group 1 - opp term 2 & hangar area 1 - { 40, 88, 0, 0 }, // 28 Term group 1 - opp term 3 - { 40, 72, 0, 0 }, // 29 Term group 1 - opp term 4 - { 18, 72, 0, 7 }, // 30 Outway 1 - { 8, 40, 0, 7 }, // 31 Airport OUTWAY - { 8, 24, AMED_EXACTPOS, 5 }, // 32 Accelerate to end of runway - { 119, 24, AMED_NOSPDCLAMP, 0 }, // 33 Release control of runway, for smoother movement - { 117, 24, AMED_NOSPDCLAMP, 0 }, // 34 End of runway - { 197, 24, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 35 Take off - { 254, 84, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 36 Flying to landing position in air - { 117, 168, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 37 Going down for land - { 3, 168, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 38 Just landed, brake until end of runway - { 8, 168, 0, 0 }, // 39 Just landed, turn around and taxi - { 8, 144, 0, 7 }, // 40 Taxi from runway - { 8, 128, 0, 7 }, // 41 Taxi from runway - { 8, 120, AMED_EXACTPOS, 5 }, // 42 Airport entrance - { 56, 344, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 43 Fly around waiting for a landing spot (north-east) - { -200, 88, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 44 Fly around waiting for a landing spot (north-west) - { 56, -168, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 45 Fly around waiting for a landing spot (south-west) - { 312, 88, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 46 Fly around waiting for a landing spot (south) - // Helicopter - { 96, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 47 Bufferspace before helipad - { 96, 40, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 48 Bufferspace before helipad - { 82, 54, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 49 Get in position for Helipad1 - { 64, 56, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 50 Get in position for Helipad2 - { 81, 55, AMED_HELI_LOWER, 0 }, // 51 Land at Helipad1 - { 64, 56, AMED_HELI_LOWER, 0 }, // 52 Land at Helipad2 - { 80, 56, AMED_HELI_RAISE, 0 }, // 53 Takeoff Helipad1 - { 64, 56, AMED_HELI_RAISE, 0 }, // 54 Takeoff Helipad2 - { 136, 96, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 55 Go to position for Hangarentrance in air - { 136, 96, AMED_HELI_LOWER, 0 }, // 56 Land in front of hangar2 - { 126, 104, 0, 3 }, // 57 Outway 2 - { 136, 136, 0, 1 }, // 58 Airport OUTWAY 2 - { 136, 152, AMED_EXACTPOS, 5 }, // 59 Accelerate to end of runway2 - { 16, 152, AMED_NOSPDCLAMP, 0 }, // 60 Release control of runway2, for smoother movement - { 20, 152, AMED_NOSPDCLAMP, 0 }, // 61 End of runway2 - { -56, 152, AMED_NOSPDCLAMP | AMED_TAKEOFF, 0 }, // 62 Take off2 - { 24, 8, AMED_NOSPDCLAMP | AMED_LAND, 0 }, // 63 Going down for land2 - { 136, 8, AMED_NOSPDCLAMP | AMED_BRAKE, 0 }, // 64 Just landed, brake until end of runway2in - { 136, 8, 0, 0 }, // 65 Just landed, turn around and taxi - { 136, 24, 0, 3 }, // 66 Taxi from runway 2in - { 136, 40, 0, 3 }, // 67 Taxi from runway 2in - { 136, 56, AMED_EXACTPOS, 1 }, // 68 Airport entrance2 - { -56, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 69 Fly to landing position in air2 - { 88, 40, 0, 0 }, // 70 Taxi Term group 2 exit - opp heli1 - { 72, 40, 0, 0 }, // 71 Taxi Term group 2 exit - opp heli2 - { 88, 57, AMED_EXACTPOS, 3 }, // 72 pre-helitakeoff helipad 1 - { 71, 56, AMED_EXACTPOS, 1 }, // 73 pre-helitakeoff helipad 2 - { 8, 120, AMED_HELI_RAISE, 0 }, // 74 Helitakeoff outside depot 1 - { 136, 104, AMED_HELI_RAISE, 0 }, // 75 Helitakeoff outside depot 2 - { 197, 168, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0} // 76 Fly to landing position in air1 -}; - - -// Heliport (heliport) -static const AirportMovingData _airport_moving_data_heliport[9] = { - { 5, 9, AMED_EXACTPOS, 1 }, // 0 - At heliport terminal - { 2, 9, AMED_HELI_RAISE, 0 }, // 1 - Take off (play sound) - { -3, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 - In position above landing spot helicopter - { -3, 9, AMED_HELI_LOWER, 0 }, // 3 - Land - { 2, 9, 0, 0 }, // 4 - Goto terminal on ground - { -31, 59, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - Circle #1 (north-east) - { -31, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - Circle #2 (north-west) - { 49, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 7 - Circle #3 (south-west) - { 70, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 8 - Circle #4 (south) -}; - -// HeliDepot 2x2 (heliport) -static const AirportMovingData _airport_moving_data_helidepot[18] = { - { 24, 4, AMED_EXACTPOS, 1 }, // 0 - At depot - { 24, 28, 0, 0 }, // 1 Taxi to right outside depot - { 5, 38, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 Flying - { -15, -15, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 3 - Circle #1 (north-east) - { -15, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 4 - Circle #2 (north-west) - { 49, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - Circle #3 (south-west) - { 49, -15, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - Circle #4 (south-east) - { 8, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 7 - PreHelipad - { 8, 32, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 8 - Helipad - { 8, 16, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 9 - Land - { 8, 16, AMED_HELI_LOWER, 7 }, // 10 - Land - { 8, 24, AMED_HELI_RAISE, 0 }, // 11 - Take off (play sound) - { 32, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 7 }, // 12 Air to above hangar area - { 32, 24, AMED_HELI_LOWER, 7 }, // 13 Taxi to right outside depot - { 8, 24, AMED_EXACTPOS, 7 }, // 14 - on helipad1 - { 24, 28, AMED_HELI_RAISE, 0 }, // 15 Takeoff right outside depot - { 8, 24, AMED_HELI_RAISE, 5 }, // 16 - Take off (play sound) - { 8, 24, AMED_SLOWTURN | AMED_EXACTPOS, 2 }, // 17 - turn on helipad1 for takeoff -}; - -// HeliDepot 2x2 (heliport) -static const AirportMovingData _airport_moving_data_helistation[33] = { - { 8, 3, AMED_EXACTPOS, 3 }, // 00 In Hangar2 - { 8, 22, 0, 0 }, // 01 outside hangar 2 - { 116, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 02 Fly to landing position in air - { 14, 22, AMED_HELI_RAISE, 0 }, // 03 Helitakeoff outside hangar1(play sound) - { 24, 22, 0, 0 }, // 04 taxiing - { 40, 22, 0, 0 }, // 05 taxiing - { 40, 8, AMED_EXACTPOS, 1 }, // 06 Helipad 1 - { 56, 8, AMED_EXACTPOS, 1 }, // 07 Helipad 2 - { 56, 24, AMED_EXACTPOS, 1 }, // 08 Helipad 3 - { 40, 8, AMED_EXACTPOS, 0 }, // 09 pre-helitakeoff helipad 1 - { 56, 8, AMED_EXACTPOS, 0 }, // 10 pre-helitakeoff helipad 2 - { 56, 24, AMED_EXACTPOS, 0 }, // 11 pre-helitakeoff helipad 3 - { 32, 8, AMED_HELI_RAISE, 0 }, // 12 Takeoff Helipad1 - { 48, 8, AMED_HELI_RAISE, 0 }, // 13 Takeoff Helipad2 - { 48, 24, AMED_HELI_RAISE, 0 }, // 14 Takeoff Helipad3 - { 84, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 15 Bufferspace before helipad - { 68, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 16 Bufferspace before helipad - { 32, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 17 Get in position for Helipad1 - { 48, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 18 Get in position for Helipad2 - { 48, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 1 }, // 19 Get in position for Helipad3 - { 40, 8, AMED_HELI_LOWER, 0 }, // 20 Land at Helipad1 - { 48, 8, AMED_HELI_LOWER, 0 }, // 21 Land at Helipad2 - { 48, 24, AMED_HELI_LOWER, 0 }, // 22 Land at Helipad3 - { 0, 22, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 23 Go to position for Hangarentrance in air - { 0, 22, AMED_HELI_LOWER, 0 }, // 24 Land in front of hangar - { 148, -8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 25 Fly around waiting for a landing spot (south-east) - { 148, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 26 Fly around waiting for a landing spot (south-west) - { 132, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 27 Fly around waiting for a landing spot (south-west) - { 100, 24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 28 Fly around waiting for a landing spot (north-east) - { 84, 8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 29 Fly around waiting for a landing spot (south-east) - { 84, -8, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 30 Fly around waiting for a landing spot (south-west) - { 100, -24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 31 Fly around waiting for a landing spot (north-west) - { 132, -24, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 32 Fly around waiting for a landing spot (north-east) -}; - -// Oilrig -static const AirportMovingData _airport_moving_data_oilrig[9] = { - { 31, 9, AMED_EXACTPOS, 1 }, // 0 - At oilrig terminal - { 28, 9, AMED_HELI_RAISE, 0 }, // 1 - Take off (play sound) - { 23, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 2 - In position above landing spot helicopter - { 23, 9, AMED_HELI_LOWER, 0 }, // 3 - Land - { 28, 9, 0, 0 }, // 4 - Goto terminal on ground - { -31, 69, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 5 - circle #1 (north-east) - { -31, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 6 - circle #2 (north-west) - { 69, -49, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 7 - circle #3 (south-west) - { 70, 9, AMED_NOSPDCLAMP | AMED_SLOWTURN, 0 }, // 8 - circle #4 (south) -}; - -/////////////////////////////////////////////////////////////////////// -/////**********Movement Machine on Airports*********************/////// -/* First element of terminals array tells us how many depots there are (to know size of array) - * this may be changed later when airports are moved to external file */ -static const TileIndexDiffC _airport_depots_country[] = {{3, 0}}; -static const byte _airport_terminal_country[] = {1, 2}; -static const AirportFTAbuildup _airport_fta_country[] = { - { 0, HANGAR, NOTHING_block, 1 }, - { 1, 255, AIRPORT_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM1, TERM1_block, 2 }, { 1, TERM2, 0, 4 }, { 1, HELITAKEOFF, 0, 19 }, { 1, 0, 0, 6 }, - { 2, TERM1, TERM1_block, 1 }, - { 3, TERM2, TERM2_block, 5 }, - { 4, 255, AIRPORT_BUSY_block, 0 }, { 4, TERM2, 0, 5 }, { 4, HANGAR, 0, 1 }, { 4, TAKEOFF, 0, 6 }, { 4, HELITAKEOFF, 0, 1 }, - { 5, 255, AIRPORT_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, 0, 0, 4 }, - { 6, 0, AIRPORT_BUSY_block, 7 }, - // takeoff - { 7, TAKEOFF, AIRPORT_BUSY_block, 8 }, - { 8, STARTTAKEOFF, NOTHING_block, 9 }, - { 9, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 10, FLYING, NOTHING_block, 15 }, { 10, LANDING, 0, 11 }, { 10, HELILANDING, 0, 20 }, - { 11, LANDING, AIRPORT_BUSY_block, 12 }, - { 12, 0, AIRPORT_BUSY_block, 13 }, - { 13, ENDLANDING, AIRPORT_BUSY_block, 14 }, { 13, TERM2, 0, 5 }, { 13, 0, 0, 14 }, - { 14, 0, AIRPORT_BUSY_block, 1 }, - // In air - { 15, 0, NOTHING_block, 16 }, - { 16, 0, NOTHING_block, 17 }, - { 17, 0, NOTHING_block, 18 }, - { 18, 0, NOTHING_block, 10 }, - { 19, HELITAKEOFF, NOTHING_block, 0 }, - { 20, HELILANDING, AIRPORT_BUSY_block, 21 }, - { 21, HELIENDLANDING, AIRPORT_BUSY_block, 1 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -static const TileIndexDiffC _airport_depots_commuter[] = { { 4, 0 } }; -static const byte _airport_terminal_commuter[] = { 1, 3 }; -static const byte _airport_helipad_commuter[] = { 1, 2 }; -static const AirportFTAbuildup _airport_fta_commuter[] = { - { 0, HANGAR, NOTHING_block, 1 }, { 0, HELITAKEOFF, HELIPAD2_block, 1 }, { 0, 0, 0, 1 }, - { 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TAKEOFF, 0, 11 }, { 1, TERM1, TAXIWAY_BUSY_block, 10 }, { 1, TERM2, TAXIWAY_BUSY_block, 10 }, { 1, TERM3, TAXIWAY_BUSY_block, 10 }, { 1, HELIPAD1, TAXIWAY_BUSY_block, 10 }, { 1, HELIPAD2, TAXIWAY_BUSY_block, 10 }, { 1, HELITAKEOFF, TAXIWAY_BUSY_block, 10 }, { 1, 0, 0, 0 }, - { 2, 255, AIRPORT_ENTRANCE_block, 2 }, { 2, HANGAR, 0, 8 }, { 2, TERM1, 0, 8 }, { 2, TERM2, 0, 8 }, { 2, TERM3, 0, 8 }, { 2, HELIPAD1, 0, 8 }, { 2, HELIPAD2, 0, 8 }, { 2, HELITAKEOFF, 0, 8 }, { 2, 0, 0, 2 }, - { 3, TERM1, TERM1_block, 8 }, { 3, HANGAR, 0, 8 }, { 3, TAKEOFF, 0, 8 }, { 3, 0, 0, 3 }, - { 4, TERM2, TERM2_block, 9 }, { 4, HANGAR, 0, 9 }, { 4, TAKEOFF, 0, 9 }, { 4, 0, 0, 4 }, - { 5, TERM3, TERM3_block, 10 }, { 5, HANGAR, 0, 10 }, { 5, TAKEOFF, 0, 10 }, { 5, 0, 0, 5 }, - { 6, HELIPAD1, HELIPAD1_block, 6 }, { 6, HANGAR, TAXIWAY_BUSY_block, 9 }, { 6, HELITAKEOFF, 0, 35 }, - { 7, HELIPAD2, HELIPAD2_block, 7 }, { 7, HANGAR, TAXIWAY_BUSY_block, 10 }, { 7, HELITAKEOFF, 0, 36 }, - { 8, 255, TAXIWAY_BUSY_block, 8 }, { 8, TAKEOFF, TAXIWAY_BUSY_block, 9 }, { 8, HANGAR, TAXIWAY_BUSY_block, 9 }, { 8, TERM1, TERM1_block, 3 }, { 8, 0, TAXIWAY_BUSY_block, 9 }, - { 9, 255, TAXIWAY_BUSY_block, 9 }, { 9, TAKEOFF, TAXIWAY_BUSY_block, 10 }, { 9, HANGAR, TAXIWAY_BUSY_block, 10 }, { 9, TERM2, TERM2_block, 4 }, { 9, HELIPAD1, HELIPAD1_block, 6 }, { 9, HELITAKEOFF, HELIPAD1_block, 6 }, { 9, TERM1, TAXIWAY_BUSY_block, 8 }, { 9, 0, TAXIWAY_BUSY_block, 10 }, - { 10, 255, TAXIWAY_BUSY_block, 10 }, { 10, TERM3, TERM3_block, 5 }, { 10, HELIPAD1, 0, 9 }, { 10, HELIPAD2, HELIPAD2_block, 7 }, { 10, HELITAKEOFF, HELIPAD2_block, 7 }, { 10, TAKEOFF, TAXIWAY_BUSY_block, 1 }, { 10, HANGAR, TAXIWAY_BUSY_block, 1 }, { 10, 0, TAXIWAY_BUSY_block, 9 }, - { 11, 0, OUT_WAY_block, 12 }, - // takeoff - { 12, TAKEOFF, RUNWAY_IN_OUT_block, 13 }, - { 13, 0, RUNWAY_IN_OUT_block, 14 }, - { 14, STARTTAKEOFF, RUNWAY_IN_OUT_block, 15 }, - { 15, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 16, FLYING, NOTHING_block, 21 }, { 16, LANDING, IN_WAY_block, 17 }, { 16, HELILANDING, 0, 25 }, - { 17, LANDING, RUNWAY_IN_OUT_block, 18 }, - { 18, 0, RUNWAY_IN_OUT_block, 19 }, - { 19, 0, RUNWAY_IN_OUT_block, 20 }, - { 20, ENDLANDING, IN_WAY_block, 2 }, - // In Air - { 21, 0, NOTHING_block, 22 }, - { 22, 0, NOTHING_block, 23 }, - { 23, 0, NOTHING_block, 24 }, - { 24, 0, NOTHING_block, 16 }, - // Helicopter -- stay in air in special place as a buffer to choose from helipads - { 25, HELILANDING, PRE_HELIPAD_block, 26 }, - { 26, HELIENDLANDING, PRE_HELIPAD_block, 26 }, { 26, HELIPAD1, 0, 27 }, { 26, HELIPAD2, 0, 28 }, { 26, HANGAR, 0, 33 }, - { 27, 0, NOTHING_block, 29 }, //helipad1 approach - { 28, 0, NOTHING_block, 30 }, - // landing - { 29, 255, NOTHING_block, 0 }, { 29, HELIPAD1, HELIPAD1_block, 6 }, - { 30, 255, NOTHING_block, 0 }, { 30, HELIPAD2, HELIPAD2_block, 7 }, - // Helicopter -- takeoff - { 31, HELITAKEOFF, NOTHING_block, 0 }, - { 32, HELITAKEOFF, NOTHING_block, 0 }, - { 33, 0, TAXIWAY_BUSY_block, 34 }, // need to go to hangar when waiting in air - { 34, 0, TAXIWAY_BUSY_block, 1 }, - { 35, 0, HELIPAD1_block, 31 }, - { 36, 0, HELIPAD2_block, 32 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -static const TileIndexDiffC _airport_depots_city[] = { { 5, 0 } }; -static const byte _airport_terminal_city[] = { 1, 3 }; -static const AirportFTAbuildup _airport_fta_city[] = { - { 0, HANGAR, NOTHING_block, 1 }, { 0, TAKEOFF, OUT_WAY_block, 1 }, { 0, 0, 0, 1 }, - { 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM2, 0, 6 }, { 1, TERM3, 0, 6 }, { 1, 0, 0, 7 }, // for all else, go to 7 - { 2, TERM1, TERM1_block, 7 }, { 2, TAKEOFF, OUT_WAY_block, 7 }, { 2, 0, 0, 7 }, - { 3, TERM2, TERM2_block, 5 }, { 3, TAKEOFF, OUT_WAY_block, 5 }, { 3, 0, 0, 5 }, - { 4, TERM3, TERM3_block, 5 }, { 4, TAKEOFF, OUT_WAY_block, 5 }, { 4, 0, 0, 5 }, - { 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, TERM3, TERM3_block, 4 }, { 5, 0, 0, 6 }, - { 6, 255, TAXIWAY_BUSY_block, 0 }, { 6, TERM2, 0, 5 }, { 6, TERM3, 0, 5 }, { 6, HANGAR, 0, 1 }, { 6, 0, 0, 7 }, - { 7, 255, TAXIWAY_BUSY_block, 0 }, { 7, TERM1, TERM1_block, 2 }, { 7, TAKEOFF, OUT_WAY_block, 8 }, { 7, HELITAKEOFF, 0, 22 }, { 7, HANGAR, 0, 1 }, { 7, 0, 0, 6 }, - { 8, 0, OUT_WAY_block, 9 }, - { 9, 0, RUNWAY_IN_OUT_block, 10 }, - // takeoff - { 10, TAKEOFF, RUNWAY_IN_OUT_block, 11 }, - { 11, STARTTAKEOFF, NOTHING_block, 12 }, - { 12, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 13, FLYING, NOTHING_block, 18 }, { 13, LANDING, 0, 14 }, { 13, HELILANDING, 0, 23 }, - { 14, LANDING, RUNWAY_IN_OUT_block, 15 }, - { 15, 0, RUNWAY_IN_OUT_block, 16 }, - { 16, 0, RUNWAY_IN_OUT_block, 17 }, - { 17, ENDLANDING, IN_WAY_block, 7 }, - // In Air - { 18, 0, NOTHING_block, 19 }, - { 19, 0, NOTHING_block, 20 }, - { 20, 0, NOTHING_block, 21 }, - { 21, 0, NOTHING_block, 13 }, - // helicopter - { 22, HELITAKEOFF, NOTHING_block, 0 }, - { 23, HELILANDING, IN_WAY_block, 24 }, - { 24, HELIENDLANDING, IN_WAY_block, 17 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -static const TileIndexDiffC _airport_depots_metropolitan[] = { { 5, 0 } }; -static const byte _airport_terminal_metropolitan[] = { 1, 3 }; -static const AirportFTAbuildup _airport_fta_metropolitan[] = { - { 0, HANGAR, NOTHING_block, 1 }, - { 1, 255, TAXIWAY_BUSY_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, TERM2, 0, 6 }, { 1, TERM3, 0, 6 }, { 1, 0, 0, 7 }, // for all else, go to 7 - { 2, TERM1, TERM1_block, 7 }, - { 3, TERM2, TERM2_block, 5 }, - { 4, TERM3, TERM3_block, 5 }, - { 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, TERM2, TERM2_block, 3 }, { 5, TERM3, TERM3_block, 4 }, { 5, 0, 0, 6 }, - { 6, 255, TAXIWAY_BUSY_block, 0 }, { 6, TERM2, 0, 5 }, { 6, TERM3, 0, 5 }, { 6, HANGAR, 0, 1 }, { 6, 0, 0, 7 }, - { 7, 255, TAXIWAY_BUSY_block, 0 }, { 7, TERM1, TERM1_block, 2 }, { 7, TAKEOFF, 0, 8 }, { 7, HELITAKEOFF, 0, 23 }, { 7, HANGAR, 0, 1 }, { 7, 0, 0, 6 }, - { 8, 0, OUT_WAY_block, 9 }, - { 9, 0, RUNWAY_OUT_block, 10 }, - // takeoff - { 10, TAKEOFF, RUNWAY_OUT_block, 11 }, - { 11, STARTTAKEOFF, NOTHING_block, 12 }, - { 12, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 13, FLYING, NOTHING_block, 19 }, { 13, LANDING, 0, 14 }, { 13, HELILANDING, 0, 25 }, - { 14, LANDING, RUNWAY_IN_block, 15 }, - { 15, 0, RUNWAY_IN_block, 16 }, - { 16, 255, RUNWAY_IN_block, 0 }, { 16, ENDLANDING, IN_WAY_block, 17 }, - { 17, 255, RUNWAY_OUT_block, 0 }, { 17, ENDLANDING, IN_WAY_block, 18 }, - { 18, ENDLANDING, IN_WAY_block, 7 }, - // In Air - { 19, 0, NOTHING_block, 20 }, - { 20, 0, NOTHING_block, 21 }, - { 21, 0, NOTHING_block, 22 }, - { 22, 0, NOTHING_block, 13 }, - // helicopter - { 23, 0, NOTHING_block, 24 }, - { 24, HELITAKEOFF, NOTHING_block, 0 }, - { 25, HELILANDING, IN_WAY_block, 26 }, - { 26, HELIENDLANDING, IN_WAY_block, 18 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -static const TileIndexDiffC _airport_depots_international[] = { { 0, 3 }, { 6, 1 } }; -static const byte _airport_terminal_international[] = { 2, 3, 3 }; -static const byte _airport_helipad_international[] = { 1, 2 }; -static const AirportFTAbuildup _airport_fta_international[] = { - { 0, HANGAR, NOTHING_block, 2 }, { 0, 255, TERM_GROUP1_block, 0 }, { 0, 255, TERM_GROUP2_ENTER1_block, 1 }, { 0, HELITAKEOFF, HELIPAD1_block, 2 }, { 0, 0, 0, 2 }, - { 1, HANGAR, NOTHING_block, 3 }, { 1, 255, HANGAR2_AREA_block, 1 }, { 1, HELITAKEOFF, HELIPAD2_block, 3 }, { 1, 0, 0, 3 }, - { 2, 255, AIRPORT_ENTRANCE_block, 0 }, { 2, HANGAR, 0, 0 }, { 2, TERM4, 0, 12 }, { 2, TERM5, 0, 12 }, { 2, TERM6, 0, 12 }, { 2, HELIPAD1, 0, 12 }, { 2, HELIPAD2, 0, 12 }, { 2, HELITAKEOFF, 0, 12 }, { 2, 0, 0, 23 }, - { 3, 255, HANGAR2_AREA_block, 0 }, { 3, HANGAR, 0, 1 }, { 3, 0, 0, 18 }, - { 4, TERM1, TERM1_block, 23 }, { 4, HANGAR, AIRPORT_ENTRANCE_block, 23 }, { 4, 0, 0, 23 }, - { 5, TERM2, TERM2_block, 24 }, { 5, HANGAR, AIRPORT_ENTRANCE_block, 24 }, { 5, 0, 0, 24 }, - { 6, TERM3, TERM3_block, 25 }, { 6, HANGAR, AIRPORT_ENTRANCE_block, 25 }, { 6, 0, 0, 25 }, - { 7, TERM4, TERM4_block, 16 }, { 7, HANGAR, HANGAR2_AREA_block, 16 }, { 7, 0, 0, 16 }, - { 8, TERM5, TERM5_block, 17 }, { 8, HANGAR, HANGAR2_AREA_block, 17 }, { 8, 0, 0, 17 }, - { 9, TERM6, TERM6_block, 18 }, { 9, HANGAR, HANGAR2_AREA_block, 18 }, { 9, 0, 0, 18 }, - { 10, HELIPAD1, HELIPAD1_block, 10 }, { 10, HANGAR, HANGAR2_AREA_block, 16 }, { 10, HELITAKEOFF, 0, 47 }, - { 11, HELIPAD2, HELIPAD2_block, 11 }, { 11, HANGAR, HANGAR2_AREA_block, 17 }, { 11, HELITAKEOFF, 0, 48 }, - { 12, 0, TERM_GROUP2_ENTER1_block, 13 }, - { 13, 0, TERM_GROUP2_ENTER1_block, 14 }, - { 14, 0, TERM_GROUP2_ENTER2_block, 15 }, - { 15, 0, TERM_GROUP2_ENTER2_block, 16 }, - { 16, 255, TERM_GROUP2_block, 0 }, { 16, TERM4, TERM4_block, 7 }, { 16, HELIPAD1, HELIPAD1_block, 10 }, { 16, HELITAKEOFF, HELIPAD1_block, 10 }, { 16, 0, 0, 17 }, - { 17, 255, TERM_GROUP2_block, 0 }, { 17, TERM5, TERM5_block, 8 }, { 17, TERM4, 0, 16 }, { 17, HELIPAD1, 0, 16 }, { 17, HELIPAD2, HELIPAD2_block, 11 }, { 17, HELITAKEOFF, HELIPAD2_block, 11 }, { 17, 0, 0, 18 }, - { 18, 255, TERM_GROUP2_block, 0 }, { 18, TERM6, TERM6_block, 9 }, { 18, TAKEOFF, 0, 19 }, { 18, HANGAR, HANGAR2_AREA_block, 3 }, { 18, 0, 0, 17 }, - { 19, 0, TERM_GROUP2_EXIT1_block, 20 }, - { 20, 0, TERM_GROUP2_EXIT1_block, 21 }, - { 21, 0, TERM_GROUP2_EXIT2_block, 22 }, - { 22, 0, TERM_GROUP2_EXIT2_block, 26 }, - { 23, 255, TERM_GROUP1_block, 0 }, { 23, TERM1, TERM1_block, 4 }, { 23, HANGAR, AIRPORT_ENTRANCE_block, 2 }, { 23, 0, 0, 24 }, - { 24, 255, TERM_GROUP1_block, 0 }, { 24, TERM2, TERM2_block, 5 }, { 24, TERM1, 0, 23 }, { 24, HANGAR, 0, 23 }, { 24, 0, 0, 25 }, - { 25, 255, TERM_GROUP1_block, 0 }, { 25, TERM3, TERM3_block, 6 }, { 25, TAKEOFF, 0, 26 }, { 25, 0, 0, 24 }, - { 26, 255, TAXIWAY_BUSY_block, 0 }, { 26, TAKEOFF, 0, 27 }, { 26, 0, 0, 25 }, - { 27, 0, OUT_WAY_block, 28 }, - // takeoff - { 28, TAKEOFF, OUT_WAY_block, 29 }, - { 29, 0, RUNWAY_OUT_block, 30 }, - { 30, STARTTAKEOFF, NOTHING_block, 31 }, - { 31, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 32, FLYING, NOTHING_block, 37 }, { 32, LANDING, 0, 33 }, { 32, HELILANDING, 0, 41 }, - { 33, LANDING, RUNWAY_IN_block, 34 }, - { 34, 0, RUNWAY_IN_block, 35 }, - { 35, 0, RUNWAY_IN_block, 36 }, - { 36, ENDLANDING, IN_WAY_block, 36 }, { 36, 255, TERM_GROUP1_block, 0 }, { 36, 255, TERM_GROUP2_ENTER1_block, 1 }, { 36, TERM4, 0, 12 }, { 36, TERM5, 0, 12 }, { 36, TERM6, 0, 12 }, { 36, 0, 0, 2 }, - // In Air - { 37, 0, NOTHING_block, 38 }, - { 38, 0, NOTHING_block, 39 }, - { 39, 0, NOTHING_block, 40 }, - { 40, 0, NOTHING_block, 32 }, - // Helicopter -- stay in air in special place as a buffer to choose from helipads - { 41, HELILANDING, PRE_HELIPAD_block, 42 }, - { 42, HELIENDLANDING, PRE_HELIPAD_block, 42 }, { 42, HELIPAD1, 0, 43 }, { 42, HELIPAD2, 0, 44 }, { 42, HANGAR, 0, 49 }, - { 43, 0, NOTHING_block, 45 }, - { 44, 0, NOTHING_block, 46 }, - // landing - { 45, 255, NOTHING_block, 0 }, { 45, HELIPAD1, HELIPAD1_block, 10 }, - { 46, 255, NOTHING_block, 0 }, { 46, HELIPAD2, HELIPAD2_block, 11 }, - // Helicopter -- takeoff - { 47, HELITAKEOFF, NOTHING_block, 0 }, - { 48, HELITAKEOFF, NOTHING_block, 0 }, - { 49, 0, HANGAR2_AREA_block, 50 }, // need to go to hangar when waiting in air - { 50, 0, HANGAR2_AREA_block, 3 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -// intercontinental -static const TileIndexDiffC _airport_depots_intercontinental[] = { { 0, 5 }, { 8, 4 } }; -static const byte _airport_terminal_intercontinental[] = { 2, 4, 4 }; -static const byte _airport_helipad_intercontinental[] = { 1, 2 }; -static const AirportFTAbuildup _airport_fta_intercontinental[] = { - { 0, HANGAR, NOTHING_block, 2 }, { 0, 255, HANGAR1_AREA_block | TERM_GROUP1_block, 0 }, { 0, 255, HANGAR1_AREA_block | TERM_GROUP1_block, 1 }, { 0, TAKEOFF, HANGAR1_AREA_block | TERM_GROUP1_block, 2 }, { 0, 0, 0, 2 }, - { 1, HANGAR, NOTHING_block, 3 }, { 1, 255, HANGAR2_AREA_block, 1 }, { 1, 255, HANGAR2_AREA_block, 0 }, { 1, 0, 0, 3 }, - { 2, 255, HANGAR1_AREA_block, 0 }, { 2, 255, TERM_GROUP1_block, 0 }, { 2, 255, TERM_GROUP1_block, 1 }, { 2, HANGAR, 0, 0 }, { 2, TAKEOFF, TERM_GROUP1_block, 27 }, { 2, TERM5, 0, 26 }, { 2, TERM6, 0, 26 }, { 2, TERM7, 0, 26 }, { 2, TERM8, 0, 26 }, { 2, HELIPAD1, 0, 26 }, { 2, HELIPAD2, 0, 26 }, { 2, HELITAKEOFF, 0, 74 }, { 2, 0, 0, 27 }, - { 3, 255, HANGAR2_AREA_block, 0 }, { 3, HANGAR, 0, 1 }, { 3, HELITAKEOFF, 0, 75 }, { 3, 0, 0, 20 }, - { 4, TERM1, TERM1_block, 26 }, { 4, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 26 }, { 4, 0, 0, 26 }, - { 5, TERM2, TERM2_block, 27 }, { 5, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 27 }, { 5, 0, 0, 27 }, - { 6, TERM3, TERM3_block, 28 }, { 6, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 28 }, { 6, 0, 0, 28 }, - { 7, TERM4, TERM4_block, 29 }, { 7, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 29 }, { 7, 0, 0, 29 }, - { 8, TERM5, TERM5_block, 18 }, { 8, HANGAR, HANGAR2_AREA_block, 18 }, { 8, 0, 0, 18 }, - { 9, TERM6, TERM6_block, 19 }, { 9, HANGAR, HANGAR2_AREA_block, 19 }, { 9, 0, 0, 19 }, - { 10, TERM7, TERM7_block, 20 }, { 10, HANGAR, HANGAR2_AREA_block, 20 }, { 10, 0, 0, 20 }, - { 11, TERM8, TERM8_block, 21 }, { 11, HANGAR, HANGAR2_AREA_block, 21 }, { 11, 0, 0, 21 }, - { 12, HELIPAD1, HELIPAD1_block, 12 }, { 12, HANGAR, 0, 70 }, { 12, HELITAKEOFF, 0, 72 }, - { 13, HELIPAD2, HELIPAD2_block, 13 }, { 13, HANGAR, 0, 71 }, { 13, HELITAKEOFF, 0, 73 }, - { 14, 0, TERM_GROUP2_ENTER1_block, 15 }, - { 15, 0, TERM_GROUP2_ENTER1_block, 16 }, - { 16, 0, TERM_GROUP2_ENTER2_block, 17 }, - { 17, 0, TERM_GROUP2_ENTER2_block, 18 }, - { 18, 255, TERM_GROUP2_block, 0 }, { 18, TERM5, TERM5_block, 8 }, { 18, TAKEOFF, 0, 19 }, { 18, HELITAKEOFF, HELIPAD1_block, 19 }, { 18, 0, TERM_GROUP2_EXIT1_block, 19 }, - { 19, 255, TERM_GROUP2_block, 0 }, { 19, TERM6, TERM6_block, 9 }, { 19, TERM5, 0, 18 }, { 19, TAKEOFF, 0, 57 }, { 19, HELITAKEOFF, HELIPAD1_block, 20 }, { 19, 0, TERM_GROUP2_EXIT1_block, 20 }, // add exit to runway out 2 - { 20, 255, TERM_GROUP2_block, 0 }, { 20, TERM7, TERM7_block, 10 }, { 20, TERM5, 0, 19 }, { 20, TERM6, 0, 19 }, { 20, HANGAR, HANGAR2_AREA_block, 3 }, { 20, TAKEOFF, 0, 19 }, { 20, 0, TERM_GROUP2_EXIT1_block, 21 }, - { 21, 255, TERM_GROUP2_block, 0 }, { 21, TERM8, TERM8_block, 11 }, { 21, HANGAR, HANGAR2_AREA_block, 20 }, { 21, TERM5, 0, 20 }, { 21, TERM6, 0, 20 }, { 21, TERM7, 0, 20 }, { 21, TAKEOFF, 0, 20 }, { 21, 0, TERM_GROUP2_EXIT1_block, 22 }, - { 22, 255, TERM_GROUP2_block, 0 }, { 22, HANGAR, 0, 21 }, { 22, TERM5, 0, 21 }, { 22, TERM6, 0, 21 }, { 22, TERM7, 0, 21 }, { 22, TERM8, 0, 21 }, { 22, TAKEOFF, 0, 21 }, { 22, 0, 0, 23 }, - { 23, 0, TERM_GROUP2_EXIT1_block, 70 }, - { 24, 0, TERM_GROUP2_EXIT2_block, 25 }, - { 25, 255, TERM_GROUP2_EXIT2_block, 0 }, { 25, HANGAR, HANGAR1_AREA_block | TERM_GROUP1_block, 29 }, { 25, 0, 0, 29 }, - { 26, 255, TERM_GROUP1_block, 0 }, { 26, TERM1, TERM1_block, 4 }, { 26, HANGAR, HANGAR1_AREA_block, 27 }, { 26, TERM5, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM6, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM7, TERM_GROUP2_ENTER1_block, 14 }, { 26, TERM8, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELIPAD1, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELIPAD2, TERM_GROUP2_ENTER1_block, 14 }, { 26, HELITAKEOFF, TERM_GROUP2_ENTER1_block, 14 }, { 26, 0, 0, 27 }, - { 27, 255, TERM_GROUP1_block, 0 }, { 27, TERM2, TERM2_block, 5 }, { 27, HANGAR, HANGAR1_AREA_block, 2 }, { 27, TERM1, 0, 26 }, { 27, TERM5, 0, 26 }, { 27, TERM6, 0, 26 }, { 27, TERM7, 0, 26 }, { 27, TERM8, 0, 26 }, { 27, HELIPAD1, 0, 14 }, { 27, HELIPAD2, 0, 14 }, { 27, 0, 0, 28 }, - { 28, 255, TERM_GROUP1_block, 0 }, { 28, TERM3, TERM3_block, 6 }, { 28, HANGAR, HANGAR1_AREA_block, 27 }, { 28, TERM1, 0, 27 }, { 28, TERM2, 0, 27 }, { 28, TERM4, 0, 29 }, { 28, TERM5, 0, 14 }, { 28, TERM6, 0, 14 }, { 28, TERM7, 0, 14 }, { 28, TERM8, 0, 14 }, { 28, HELIPAD1, 0, 14 }, { 28, HELIPAD2, 0, 14 }, { 28, 0, 0, 29 }, - { 29, 255, TERM_GROUP1_block, 0 }, { 29, TERM4, TERM4_block, 7 }, { 29, HANGAR, HANGAR1_AREA_block, 27 }, { 29, TAKEOFF, 0, 30 }, { 29, 0, 0, 28 }, - { 30, 0, OUT_WAY_block2, 31 }, - { 31, 0, OUT_WAY_block, 32 }, - // takeoff - { 32, TAKEOFF, RUNWAY_OUT_block, 33 }, - { 33, 0, RUNWAY_OUT_block, 34 }, - { 34, STARTTAKEOFF, NOTHING_block, 35 }, - { 35, ENDTAKEOFF, NOTHING_block, 0 }, - // landing - { 36, 0, 0, 0 }, - { 37, LANDING, RUNWAY_IN_block, 38 }, - { 38, 0, RUNWAY_IN_block, 39 }, - { 39, 0, RUNWAY_IN_block, 40 }, - { 40, ENDLANDING, RUNWAY_IN_block, 41 }, - { 41, 0, IN_WAY_block, 42 }, - { 42, 255, IN_WAY_block, 0 }, { 42, 255, TERM_GROUP1_block, 0 }, { 42, 255, TERM_GROUP1_block, 1 }, { 42, HANGAR, 0, 2 }, { 42, 0, 0, 26 }, - // In Air - { 43, 0, 0, 44 }, - { 44, FLYING, 0, 45 }, { 44, HELILANDING, 0, 47 }, { 44, LANDING, 0, 69 }, { 44, 0, 0, 45 }, - { 45, 0, 0, 46 }, - { 46, FLYING, 0, 43 }, { 46, LANDING, 0, 76 }, { 46, 0, 0, 43 }, - // Helicopter -- stay in air in special place as a buffer to choose from helipads - { 47, HELILANDING, PRE_HELIPAD_block, 48 }, - { 48, HELIENDLANDING, PRE_HELIPAD_block, 48 }, { 48, HELIPAD1, 0, 49 }, { 48, HELIPAD2, 0, 50 }, { 48, HANGAR, 0, 55 }, - { 49, 0, NOTHING_block, 51 }, - { 50, 0, NOTHING_block, 52 }, - // landing - { 51, 255, NOTHING_block, 0 }, { 51, HELIPAD1, HELIPAD1_block, 12 }, { 51, HANGAR, 0, 55 }, { 51, 0, 0, 12 }, - { 52, 255, NOTHING_block, 0 }, { 52, HELIPAD2, HELIPAD2_block, 13 }, { 52, HANGAR, 0, 55 }, { 52, 0, 0, 13 }, - // Helicopter -- takeoff - { 53, HELITAKEOFF, NOTHING_block, 0 }, - { 54, HELITAKEOFF, NOTHING_block, 0 }, - { 55, 0, HANGAR2_AREA_block, 56 }, // need to go to hangar when waiting in air - { 56, 0, HANGAR2_AREA_block, 3 }, - // runway 2 out support - { 57, 255, OUT_WAY2_block, 0 }, { 57, TAKEOFF, 0, 58 }, { 57, 0, 0, 58 }, - { 58, 0, OUT_WAY2_block, 59 }, - { 59, TAKEOFF, RUNWAY_OUT2_block, 60 }, // takeoff - { 60, 0, RUNWAY_OUT2_block, 61 }, - { 61, STARTTAKEOFF, NOTHING_block, 62 }, - { 62, ENDTAKEOFF, NOTHING_block, 0 }, - // runway 2 in support - { 63, LANDING, RUNWAY_IN2_block, 64 }, - { 64, 0, RUNWAY_IN2_block, 65 }, - { 65, 0, RUNWAY_IN2_block, 66 }, - { 66, ENDLANDING, RUNWAY_IN2_block, 0 }, { 66, 255, 0, 1 }, { 66, 255, 0, 0 }, { 66, 0, 0, 67 }, - { 67, 0, IN_WAY2_block, 68 }, - { 68, 255, IN_WAY2_block, 0 }, { 68, 255, TERM_GROUP2_block, 1 }, { 68, 255, TERM_GROUP1_block, 0 }, { 68, HANGAR, HANGAR2_AREA_block, 22 }, { 68, 0, 0, 22 }, - { 69, 255, RUNWAY_IN2_block, 0 }, { 69, 0, RUNWAY_IN2_block, 63 }, - { 70, 255, TERM_GROUP2_EXIT1_block, 0 }, { 70, HELIPAD1, HELIPAD1_block, 12 }, { 70, HELITAKEOFF, HELIPAD1_block, 12 }, { 70, 0, 0, 71 }, - { 71, 255, TERM_GROUP2_EXIT1_block, 0 }, { 71, HELIPAD2, HELIPAD2_block, 13 }, { 71, HELITAKEOFF, HELIPAD1_block, 12 }, { 71, 0, 0, 24 }, - { 72, 0, HELIPAD1_block, 53 }, - { 73, 0, HELIPAD2_block, 54 }, - { 74, HELITAKEOFF, NOTHING_block, 0 }, - { 75, HELITAKEOFF, NOTHING_block, 0 }, - { 76, 255, RUNWAY_IN_block, 0 }, { 76, 0, RUNWAY_IN_block, 37 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - - -// heliports, oilrigs don't have depots -static const byte _airport_helipad_heliport_oilrig[] = { 1, 1 }; -static const AirportFTAbuildup _airport_fta_heliport_oilrig[] = { - { 0, HELIPAD1, HELIPAD1_block, 1 }, - { 1, HELITAKEOFF, NOTHING_block, 0 }, // takeoff - { 2, 255, AIRPORT_BUSY_block, 0 }, { 2, HELILANDING, 0, 3 }, { 2, HELITAKEOFF, 0, 1 }, - { 3, HELILANDING, AIRPORT_BUSY_block, 4 }, - { 4, HELIENDLANDING, AIRPORT_BUSY_block, 4 }, { 4, HELIPAD1, HELIPAD1_block, 0 }, { 4, HELITAKEOFF, 0, 2 }, - // In Air - { 5, 0, NOTHING_block, 6 }, - { 6, 0, NOTHING_block, 7 }, - { 7, 0, NOTHING_block, 8 }, - { 8, FLYING, NOTHING_block, 5 }, { 8, HELILANDING, HELIPAD1_block, 2 }, // landing - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -// helidepots -static const TileIndexDiffC _airport_depots_helidepot[] = { { 1, 0 } }; -static const byte _airport_helipad_helidepot[] = { 1, 1 }; -static const AirportFTAbuildup _airport_fta_helidepot[] = { - { 0, HANGAR, NOTHING_block, 1 }, - { 1, 255, HANGAR2_AREA_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, HELIPAD1, HELIPAD1_block, 14 }, { 1, HELITAKEOFF, 0, 15 }, { 1, 0, 0, 0 }, - { 2, FLYING, NOTHING_block, 3 }, { 2, HELILANDING, PRE_HELIPAD_block, 7 }, { 2, HANGAR, 0, 12 }, { 2, HELITAKEOFF, NOTHING_block, 16 }, - // In Air - { 3, 0, NOTHING_block, 4 }, - { 4, 0, NOTHING_block, 5 }, - { 5, 0, NOTHING_block, 6 }, - { 6, 0, NOTHING_block, 2 }, - // Helicopter -- stay in air in special place as a buffer to choose from helipads - { 7, HELILANDING, PRE_HELIPAD_block, 8 }, - { 8, HELIENDLANDING, PRE_HELIPAD_block, 8 }, { 8, HELIPAD1, 0, 9 }, { 8, HANGAR, 0, 12 }, { 8, 0, 0, 2 }, - { 9, 0, NOTHING_block, 10 }, - // landing - { 10, 255, NOTHING_block, 10 }, { 10, HELIPAD1, HELIPAD1_block, 14 }, { 10, HANGAR, 0, 1 }, { 10, 0, 0, 14 }, - // Helicopter -- takeoff - { 11, HELITAKEOFF, NOTHING_block, 0 }, - { 12, 0, HANGAR2_AREA_block, 13 }, // need to go to hangar when waiting in air - { 13, 0, HANGAR2_AREA_block, 1 }, - { 14, HELIPAD1, HELIPAD1_block, 14 }, { 14, HANGAR, 0, 1 }, { 14, HELITAKEOFF, 0, 17 }, - { 15, HELITAKEOFF, NOTHING_block, 0 }, // takeoff outside depot - { 16, HELITAKEOFF, 0, 14 }, - { 17, 0, NOTHING_block, 11 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - -// helistation -static const TileIndexDiffC _airport_depots_helistation[] = { { 0, 0 } }; -static const byte _airport_helipad_helistation[] = { 1, 3 }; -static const AirportFTAbuildup _airport_fta_helistation[] = { - { 0, HANGAR, NOTHING_block, 8 }, { 0, HELIPAD1, 0, 1 }, { 0, HELIPAD2, 0, 1 }, { 0, HELIPAD3, 0, 1 }, { 0, HELITAKEOFF, 0, 1 }, { 0, 0, 0, 0 }, - { 1, 255, HANGAR2_AREA_block, 0 }, { 1, HANGAR, 0, 0 }, { 1, HELITAKEOFF, 0, 3 }, { 1, 0, 0, 4 }, - // landing - { 2, FLYING, NOTHING_block, 28 }, { 2, HELILANDING, 0, 15 }, { 2, 0, 0, 28 }, - // helicopter side - { 3, HELITAKEOFF, NOTHING_block, 0 }, // helitakeoff outside hangar2 - { 4, 255, TAXIWAY_BUSY_block, 0 }, { 4, HANGAR, HANGAR2_AREA_block, 1 }, { 4, HELITAKEOFF, 0, 1 }, { 4, 0, 0, 5 }, - { 5, 255, TAXIWAY_BUSY_block, 0 }, { 5, HELIPAD1, HELIPAD1_block, 6 }, { 5, HELIPAD2, HELIPAD2_block, 7 }, { 5, HELIPAD3, HELIPAD3_block, 8 }, { 5, 0, 0, 4 }, - { 6, HELIPAD1, HELIPAD1_block, 5 }, { 6, HANGAR, HANGAR2_AREA_block, 5 }, { 6, HELITAKEOFF, 0, 9 }, { 6, 0, 0, 6 }, - { 7, HELIPAD2, HELIPAD2_block, 5 }, { 7, HANGAR, HANGAR2_AREA_block, 5 }, { 7, HELITAKEOFF, 0, 10 }, { 7, 0, 0, 7 }, - { 8, HELIPAD3, HELIPAD3_block, 5 }, { 8, HANGAR, HANGAR2_AREA_block, 5 }, { 8, HELITAKEOFF, 0, 11 }, { 8, 0, 0, 8 }, - { 9, 0, HELIPAD1_block, 12 }, - { 10, 0, HELIPAD2_block, 13 }, - { 11, 0, HELIPAD3_block, 14 }, - { 12, HELITAKEOFF, NOTHING_block, 0 }, - { 13, HELITAKEOFF, NOTHING_block, 0 }, - { 14, HELITAKEOFF, NOTHING_block, 0 }, - // heli - in flight moves - { 15, HELILANDING, PRE_HELIPAD_block, 16 }, - { 16, HELIENDLANDING, PRE_HELIPAD_block, 16 }, { 16, HELIPAD1, 0, 17 }, { 16, HELIPAD2, 0, 18 }, { 16, HELIPAD3, 0, 19 }, { 16, HANGAR, 0, 23 }, - { 17, 0, NOTHING_block, 20 }, - { 18, 0, NOTHING_block, 21 }, - { 19, 0, NOTHING_block, 22 }, - // heli landing - { 20, 255, NOTHING_block, 0 }, { 20, HELIPAD1, HELIPAD1_block, 6 }, { 20, HANGAR, 0, 23 }, { 20, 0, 0, 6 }, - { 21, 255, NOTHING_block, 0 }, { 21, HELIPAD2, HELIPAD2_block, 7 }, { 21, HANGAR, 0, 23 }, { 21, 0, 0, 7 }, - { 22, 255, NOTHING_block, 0 }, { 22, HELIPAD3, HELIPAD3_block, 8 }, { 22, HANGAR, 0, 23 }, { 22, 0, 0, 8 }, - { 23, 0, HANGAR2_AREA_block, 24 }, // need to go to helihangar when waiting in air - { 24, 0, HANGAR2_AREA_block, 1 }, - { 25, 0, NOTHING_block, 26 }, - { 26, 0, NOTHING_block, 27 }, - { 27, 0, NOTHING_block, 2 }, - { 28, 0, NOTHING_block, 29 }, - { 29, 0, NOTHING_block, 30 }, - { 30, 0, NOTHING_block, 31 }, - { 31, 0, NOTHING_block, 32 }, - { 32, 0, NOTHING_block, 25 }, - { MAX_ELEMENTS, 0, 0, 0 } // end marker. DO NOT REMOVE -}; - - -static const AirportMovingData * const _airport_moving_datas[] = { - _airport_moving_data_country, // Country Airfield (small) 4x3 - _airport_moving_data_town, // City Airport (large) 6x6 - _airport_moving_data_heliport, // Heliport - _airport_moving_data_metropolitan, // Metropolitain Airport (large) - 2 runways - _airport_moving_data_international, // International Airport (xlarge) - 2 runways - _airport_moving_data_commuter, // Commuter Airfield (small) 5x4 - _airport_moving_data_helidepot, // Helidepot - _airport_moving_data_intercontinental, // Intercontinental Airport (xxlarge) - 4 runways - _airport_moving_data_helistation, // Helistation - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - _airport_moving_data_oilrig // Oilrig -}; - -#endif /* AIRPORT_MOVEMENT_H */ diff --git a/aystar.c b/aystar.c deleted file mode 100644 --- a/aystar.c +++ /dev/null @@ -1,296 +0,0 @@ -/* $Id$ */ - -/* - * This file has the core function for AyStar - * AyStar is a fast pathfinding routine and is used for things like - * AI_pathfinding and Train_pathfinding. - * For more information about AyStar (A* Algorithm), you can look at - * http://en.wikipedia.org/wiki/A-star_search_algorithm - */ - -/* - * Friendly reminder: - * Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory - * And when not free'd, it can cause system-crashes. - * Also remember that when you stop an algorithm before it is finished, your - * should call clear() yourself! - */ - -#include "stdafx.h" -#include "openttd.h" -#include "aystar.h" - -int _aystar_stats_open_size; -int _aystar_stats_closed_size; - -// This looks in the Hash if a node exists in ClosedList -// If so, it returns the PathNode, else NULL -static PathNode* AyStarMain_ClosedList_IsInList(AyStar *aystar, const AyStarNode *node) -{ - return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction); -} - -// This adds a node to the ClosedList -// It makes a copy of the data -static void AyStarMain_ClosedList_Add(AyStar *aystar, const PathNode *node) -{ - // Add a node to the ClosedList - PathNode *new_node = malloc(sizeof(*new_node)); - *new_node = *node; - Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node); -} - -// Checks if a node is in the OpenList -// If so, it returns the OpenListNode, else NULL -static OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, const AyStarNode *node) -{ - return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction); -} - -// Gets the best node from OpenList -// returns the best node, or NULL of none is found -// Also it deletes the node from the OpenList -static OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar) -{ - // Return the item the Queue returns.. the best next OpenList item. - OpenListNode *res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue); - if (res != NULL) { - Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction); - } - - return res; -} - -// Adds a node to the OpenList -// It makes a copy of node, and puts the pointer of parent in the struct -static void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, const AyStarNode *node, int f, int g) -{ - // Add a new Node to the OpenList - OpenListNode *new_node = malloc(sizeof(*new_node)); - new_node->g = g; - new_node->path.parent = parent; - new_node->path.node = *node; - Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node); - - // Add it to the queue - aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f); -} - -/* - * Checks one tile and calculate his f-value - * return values: - * AYSTAR_DONE : indicates we are done - */ -int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent) -{ - int new_f, new_g, new_h; - PathNode *closedlist_parent; - OpenListNode *check; - - // Check the new node against the ClosedList - if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE; - - // Calculate the G-value for this node - new_g = aystar->CalculateG(aystar, current, parent); - // If the value was INVALID_NODE, we don't do anything with this node - if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE; - - // There should not be given any other error-code.. - assert(new_g >= 0); - // Add the parent g-value to the new g-value - new_g += parent->g; - if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE; - - // Calculate the h-value - new_h = aystar->CalculateH(aystar, current, parent); - // There should not be given any error-code.. - assert(new_h >= 0); - - // The f-value if g + h - new_f = new_g + new_h; - - // Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList) - closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node); - - // Check if this item is already in the OpenList - check = AyStarMain_OpenList_IsInList(aystar, current); - if (check != NULL) { - uint i; - // Yes, check if this g value is lower.. - if (new_g > check->g) return AYSTAR_DONE; - aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0); - // It is lower, so change it to this item - check->g = new_g; - check->path.parent = closedlist_parent; - /* Copy user data, will probably have changed */ - for (i = 0; i < lengthof(current->user_data); i++) { - check->path.node.user_data[i] = current->user_data[i]; - } - // Readd him in the OpenListQueue - aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f); - } else { - // A new node, add him to the OpenList - AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g); - } - - return AYSTAR_DONE; -} - -/* - * This function is the core of AyStar. It handles one item and checks - * his neighbour items. If they are valid, they are added to be checked too. - * return values: - * AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path - * has been found. - * AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been - * reached. - * AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found. - * AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try. - */ -int AyStarMain_Loop(AyStar *aystar) -{ - int i, r; - - // Get the best node from OpenList - OpenListNode *current = AyStarMain_OpenList_Pop(aystar); - // If empty, drop an error - if (current == NULL) return AYSTAR_EMPTY_OPENLIST; - - // Check for end node and if found, return that code - if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) { - if (aystar->FoundEndNode != NULL) - aystar->FoundEndNode(aystar, current); - free(current); - return AYSTAR_FOUND_END_NODE; - } - - // Add the node to the ClosedList - AyStarMain_ClosedList_Add(aystar, ¤t->path); - - // Load the neighbours - aystar->GetNeighbours(aystar, current); - - // Go through all neighbours - for (i = 0; i < aystar->num_neighbours; i++) { - // Check and add them to the OpenList if needed - r = aystar->checktile(aystar, &aystar->neighbours[i], current); - } - - // Free the node - free(current); - - if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes) { - /* We've expanded enough nodes */ - return AYSTAR_LIMIT_REACHED; - } else { - // Return that we are still busy - return AYSTAR_STILL_BUSY; - } -} - -/* - * This function frees the memory it allocated - */ -void AyStarMain_Free(AyStar *aystar) -{ - aystar->OpenListQueue.free(&aystar->OpenListQueue, false); - /* 2nd argument above is false, below is true, to free the values only - * once */ - delete_Hash(&aystar->OpenListHash, true); - delete_Hash(&aystar->ClosedListHash, true); -#ifdef AYSTAR_DEBUG - printf("[AyStar] Memory free'd\n"); -#endif -} - -/* - * This function make the memory go back to zero - * This function should be called when you are using the same instance again. - */ -void AyStarMain_Clear(AyStar *aystar) -{ - // Clean the Queue, but not the elements within. That will be done by - // the hash. - aystar->OpenListQueue.clear(&aystar->OpenListQueue, false); - // Clean the hashes - clear_Hash(&aystar->OpenListHash, true); - clear_Hash(&aystar->ClosedListHash, true); - -#ifdef AYSTAR_DEBUG - printf("[AyStar] Cleared AyStar\n"); -#endif -} - -/* - * This is the function you call to run AyStar. - * return values: - * AYSTAR_FOUND_END_NODE : indicates we found an end node. - * AYSTAR_NO_PATH : indicates that there was no path found. - * AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try. - * When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY) - * aystar->clear() is called. Note that when you stop the algorithm halfway, - * you should still call clear() yourself! - */ -int AyStarMain_Main(AyStar *aystar) { - int r, i = 0; - // Loop through the OpenList - // Quit if result is no AYSTAR_STILL_BUSY or is more than loops_per_tick - while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { } -#ifdef AYSTAR_DEBUG - switch (r) { - case AYSTAR_FOUND_END_NODE: printf("[AyStar] Found path!\n"); break; - case AYSTAR_EMPTY_OPENLIST: printf("[AyStar] OpenList run dry, no path found\n"); break; - case AYSTAR_LIMIT_REACHED: printf("[AyStar] Exceeded search_nodes, no path found\n"); break; - default: break; - } -#endif - if (r != AYSTAR_STILL_BUSY) { - /* We're done, clean up */ - _aystar_stats_open_size = aystar->OpenListHash.size; - _aystar_stats_closed_size = aystar->ClosedListHash.size; - aystar->clear(aystar); - } - - switch (r) { - case AYSTAR_FOUND_END_NODE: return AYSTAR_FOUND_END_NODE; - case AYSTAR_EMPTY_OPENLIST: - case AYSTAR_LIMIT_REACHED: return AYSTAR_NO_PATH; - default: return AYSTAR_STILL_BUSY; - } -} - -/* - * Adds a node from where to start an algorithm. Multiple nodes can be added - * if wanted. You should make sure that clear() is called before adding nodes - * if the AyStar has been used before (though the normal main loop calls - * clear() automatically when the algorithm finishes - * g is the cost for starting with this node. - */ -void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g) -{ -#ifdef AYSTAR_DEBUG - printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n", - TileX(start_node->tile), TileY(start_node->tile), start_node->direction); -#endif - AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, g); -} - -void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets) -{ - // Allocated the Hash for the OpenList and ClosedList - init_Hash(&aystar->OpenListHash, hash, num_buckets); - init_Hash(&aystar->ClosedListHash, hash, num_buckets); - - // Set up our sorting queue - // BinaryHeap allocates a block of 1024 nodes - // When thatone gets full it reserves an otherone, till this number - // That is why it can stay this high - init_BinaryHeap(&aystar->OpenListQueue, 102400); - - aystar->addstart = AyStarMain_AddStartNode; - aystar->main = AyStarMain_Main; - aystar->loop = AyStarMain_Loop; - aystar->free = AyStarMain_Free; - aystar->clear = AyStarMain_Clear; - aystar->checktile = AyStarMain_CheckTile; -} diff --git a/aystar.h b/aystar.h deleted file mode 100644 --- a/aystar.h +++ /dev/null @@ -1,179 +0,0 @@ -/* $Id$ */ - -/* - * This file has the header for AyStar - * AyStar is a fast pathfinding routine and is used for things like - * AI_pathfinding and Train_pathfinding. - * For more information about AyStar (A* Algorithm), you can look at - * http://en.wikipedia.org/wiki/A-star_search_algorithm - */ - -#ifndef AYSTAR_H -#define AYSTAR_H - -#include "queue.h" - -//#define AYSTAR_DEBUG -enum { - AYSTAR_FOUND_END_NODE, - AYSTAR_EMPTY_OPENLIST, - AYSTAR_STILL_BUSY, - AYSTAR_NO_PATH, - AYSTAR_LIMIT_REACHED, - AYSTAR_DONE -}; - -enum{ - AYSTAR_INVALID_NODE = -1, -}; - -typedef struct AyStarNode AyStarNode; -struct AyStarNode { - TileIndex tile; - uint direction; - uint user_data[2]; -}; - -// The resulting path has nodes looking like this. -typedef struct PathNode PathNode; -struct PathNode { - AyStarNode node; - // The parent of this item - PathNode *parent; -}; - -// For internal use only -// We do not save the h-value, because it is only needed to calculate the f-value. -// h-value should _always_ be the distance left to the end-tile. -typedef struct OpenListNode OpenListNode; -struct OpenListNode { - int g; - PathNode path; -}; - -typedef struct AyStar AyStar; -/* - * This function is called to check if the end-tile is found - * return values can be: - * AYSTAR_FOUND_END_NODE : indicates this is the end tile - * AYSTAR_DONE : indicates this is not the end tile (or direction was wrong) - */ -/* - * The 2nd parameter should be OpenListNode, and NOT AyStarNode. AyStarNode is - * part of OpenListNode and so it could be accessed without any problems. - * The good part about OpenListNode is, and how AIs use it, that you can - * access the parent of the current node, and so check if you, for example - * don't try to enter the file tile with a 90-degree curve. So please, leave - * this an OpenListNode, it works just fine -- TrueLight - */ -typedef int32 AyStar_EndNodeCheck(AyStar *aystar, OpenListNode *current); - -/* - * This function is called to calculate the G-value for AyStar Algorithm. - * return values can be: - * AYSTAR_INVALID_NODE : indicates an item is not valid (e.g.: unwalkable) - * Any value >= 0 : the g-value for this tile - */ -typedef int32 AyStar_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent); - -/* - * This function is called to calculate the H-value for AyStar Algorithm. - * Mostly, this must result the distance (Manhattan way) between the - * current point and the end point - * return values can be: - * Any value >= 0 : the h-value for this tile - */ -typedef int32 AyStar_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent); - -/* - * This function request the tiles around the current tile and put them in tiles_around - * tiles_around is never resetted, so if you are not using directions, just leave it alone. - * Warning: never add more tiles_around than memory allocated for it. - */ -typedef void AyStar_GetNeighbours(AyStar *aystar, OpenListNode *current); - -/* - * If the End Node is found, this function is called. - * It can do, for example, calculate the route and put that in an array - */ -typedef void AyStar_FoundEndNode(AyStar *aystar, OpenListNode *current); - -// For internal use, see aystar.c -typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g); -typedef int AyStar_Main(AyStar *aystar); -typedef int AyStar_Loop(AyStar *aystar); -typedef int AyStar_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -typedef void AyStar_Free(AyStar *aystar); -typedef void AyStar_Clear(AyStar *aystar); - -struct AyStar { -/* These fields should be filled before initting the AyStar, but not changed - * afterwards (except for user_data and user_path)! (free and init again to change them) */ - - /* These should point to the application specific routines that do the - * actual work */ - AyStar_CalculateG *CalculateG; - AyStar_CalculateH *CalculateH; - AyStar_GetNeighbours *GetNeighbours; - AyStar_EndNodeCheck *EndNodeCheck; - AyStar_FoundEndNode *FoundEndNode; - - /* These are completely untouched by AyStar, they can be accesed by - * the application specific routines to input and output data. - * user_path should typically contain data about the resulting path - * afterwards, user_target should typically contain information about - * what where looking for, and user_data can contain just about - * everything */ - void *user_path; - void *user_target; - uint user_data[10]; - - /* How many loops are there called before AyStarMain_Main gives - * control back to the caller. 0 = until done */ - byte loops_per_tick; - /* If the g-value goes over this number, it stops searching - * 0 = infinite */ - uint max_path_cost; - /* The maximum amount of nodes that will be expanded, 0 = infinite */ - uint max_search_nodes; - - /* These should be filled with the neighbours of a tile by - * GetNeighbours */ - AyStarNode neighbours[12]; - byte num_neighbours; - - /* These will contain the methods for manipulating the AyStar. Only - * main() should be called externally */ - AyStar_AddStartNode *addstart; - AyStar_Main *main; - AyStar_Loop *loop; - AyStar_Free *free; - AyStar_Clear *clear; - AyStar_CheckTile *checktile; - - /* These will contain the open and closed lists */ - - /* The actual closed list */ - Hash ClosedListHash; - /* The open queue */ - Queue OpenListQueue; - /* An extra hash to speed up the process of looking up an element in - * the open list */ - Hash OpenListHash; -}; - - -void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node, uint g); -int AyStarMain_Main(AyStar *aystar); -int AyStarMain_Loop(AyStar *aystar); -int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent); -void AyStarMain_Free(AyStar *aystar); -void AyStarMain_Clear(AyStar *aystar); - -/* Initialize an AyStar. You should fill all appropriate fields before - * callling init_AyStar (see the declaration of AyStar for which fields are - * internal */ -void init_AyStar(AyStar *aystar, Hash_HashProc hash, uint num_buckets); - - -#endif /* AYSTAR_H */ diff --git a/bin/data/2ccmap.grf b/bin/data/2ccmap.grf new file mode 100644 index 0000000000000000000000000000000000000000..4e833c0e885423f7542f4635e75182babb137e30 GIT binary patch literal 66566 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 Q2#R9&!t!h_hwFL$8)qrr`2YX_ diff --git a/bin/data/autorail.grf b/bin/data/autorail.grf new file mode 100644 index 0000000000000000000000000000000000000000..cd6fd293c2ac14699d930b41049242407bab7158 GIT binary patch literal 9747 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 MMe~JcJC^zPzX!=Yn*aa+ diff --git a/bin/data/canalsw.grf b/bin/data/canalsw.grf new file mode 100644 index 0000000000000000000000000000000000000000..045eae15e8b7c42ae5cf5f9bccf165e298f64814 GIT binary patch literal 41639 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 diff --git a/bin/data/trkfoundw.grf b/bin/data/trkfoundw.grf new file mode 100644 index 0000000000000000000000000000000000000000..5fbfa36e98d44fedded8bc1b5ebc5e356d007084 GIT binary patch literal 91558 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 SdRl!mJ&%=@?n)(R{`mjl{9Gph diff --git a/bin/scenario/README b/bin/scenario/README new file mode 100644 --- /dev/null +++ b/bin/scenario/README @@ -0,0 +1,4 @@ +You can place you scenarios in this dir. + +This file is really here to make the Makefile happy, it should be modified to +properly handle scenarios (or maybe even not at all...). diff --git a/bin/scripts/autoexec.scr.example b/bin/scripts/autoexec.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/autoexec.scr.example @@ -0,0 +1,4 @@ +# send chat messages from the console with +# ] s i love this chat +# +alias s "say %!" \ No newline at end of file diff --git a/bin/scripts/on_client.scr.example b/bin/scripts/on_client.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/on_client.scr.example @@ -0,0 +1,2 @@ +echo "Setting default network client settings..." +name = "myname" diff --git a/bin/scripts/on_dedicated.scr.example b/bin/scripts/on_dedicated.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/on_dedicated.scr.example @@ -0,0 +1,4 @@ +echo "Setting dedicated network server settings..." +# empty the server password +server_pw = "*" +server_name = "My example dedicated gameserver" diff --git a/bin/scripts/on_server.scr.example b/bin/scripts/on_server.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/on_server.scr.example @@ -0,0 +1,3 @@ +echo "Setting default network server settings..." +net_sync_freq = 100 +net_frame_freq = 0 diff --git a/bin/scripts/pre_dedicated.scr.example b/bin/scripts/pre_dedicated.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/pre_dedicated.scr.example @@ -0,0 +1,3 @@ +# set default server port, and have the dedicated server listen on all interfaces +server_ip = all +server_port = 3979 diff --git a/bin/scripts/pre_server.scr.example b/bin/scripts/pre_server.scr.example new file mode 100644 --- /dev/null +++ b/bin/scripts/pre_server.scr.example @@ -0,0 +1,2 @@ +# set the server port to the default value +server_port = 3979 diff --git a/bin/scripts/readme.txt b/bin/scripts/readme.txt new file mode 100644 --- /dev/null +++ b/bin/scripts/readme.txt @@ -0,0 +1,21 @@ +Scripting +--------- + +OpenTTD supports scripts. + +local scripts: + - 'autoexec.scr' is executed on gamestart [all - use this for custom aliases per ex.] + ++network scripts: + should be used to set client optimization settings: + - 'on_client.scr' is executed when you join a server [all clients] + + should be used to set the servers port/ip and/or server optimization settings/patches: + - 'pre_server.scr' is executed before the servers tcp stack is started [in-game only] + - 'pre_dedicated.scr' is executed before the servers tcp stack is started [dedicated only] + + should be used to set the servers name, password and so on: + - 'on_server.scr' is executed after starting a server [dedicated and in-game] + - 'on_dedicated.scr' is additionally executed after starting a server [dedicated only] + +For examples how a script can look, check the .example examples. diff --git a/bmp.c b/bmp.c deleted file mode 100644 --- a/bmp.c +++ /dev/null @@ -1,378 +0,0 @@ -/* $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 = (int)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); -} diff --git a/bmp.h b/bmp.h deleted file mode 100644 --- a/bmp.h +++ /dev/null @@ -1,36 +0,0 @@ -/* $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 */ diff --git a/bridge.h b/bridge.h deleted file mode 100644 --- a/bridge.h +++ /dev/null @@ -1,32 +0,0 @@ -/* $Id$ */ - -/** @file bridge.h Header file for bridges */ - -#ifndef BRIDGE_H -#define BRIDGE_H - -enum { - MAX_BRIDGES = 13 -}; - -/** Struct containing information about a single bridge type - */ -typedef struct Bridge { - Year avail_year; ///< the year in which the bridge becomes available - byte min_length; ///< the minimum length of the bridge (not counting start and end tile) - byte max_length; ///< the maximum length of the bridge (not counting start and end tile) - uint16 price; ///< the relative price of the bridge - uint16 speed; ///< maximum travel speed - PalSpriteID sprite; ///< the sprite which is used in the GUI (possibly with a recolor sprite) - StringID material; ///< the string that contains the bridge description - PalSpriteID **sprite_table; ///< table of sprites for drawing the bridge - byte flags; ///< bit 0 set: disable drawing of far pillars. -} Bridge; - -extern const Bridge orig_bridge[MAX_BRIDGES]; -extern Bridge _bridge[MAX_BRIDGES]; - -uint GetBridgeFoundation(Slope tileh, Axis axis); -uint SetSpeedLimitOnBridge(Vehicle *); - -#endif /* BRIDGE_H */ diff --git a/bridge_gui.c b/bridge_gui.c deleted file mode 100644 --- a/bridge_gui.c +++ /dev/null @@ -1,167 +0,0 @@ -/* $Id$ */ - -/** @file bridge_gui.c Graphical user interface for bridge construction*/ - -#include "stdafx.h" -#include "openttd.h" -#include "table/strings.h" -#include "functions.h" -#include "map.h" -#include "window.h" -#include "gui.h" -#include "viewport.h" -#include "gfx.h" -#include "command.h" -#include "sound.h" -#include "variables.h" -#include "bridge.h" - -static struct BridgeData { - uint count; - TileIndex start_tile; - TileIndex end_tile; - byte type; - byte indexes[MAX_BRIDGES]; - int32 costs[MAX_BRIDGES]; -} _bridgedata; - -void CcBuildBridge(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - if (success) SndPlayTileFx(SND_27_BLACKSMITH_ANVIL, tile); -} - -static void BuildBridge(Window *w, int i) -{ - DeleteWindow(w); - DoCommandP(_bridgedata.end_tile, _bridgedata.start_tile, - _bridgedata.indexes[i] | (_bridgedata.type << 8), CcBuildBridge, - CMD_BUILD_BRIDGE | CMD_AUTO | CMD_MSG(STR_5015_CAN_T_BUILD_BRIDGE_HERE)); -} - -static void BuildBridgeWndProc(Window *w, WindowEvent *e) -{ - switch (e->event) { - case WE_PAINT: { - uint i; - - DrawWindowWidgets(w); - - for (i = 0; i < 4 && i + w->vscroll.pos < _bridgedata.count; i++) { - const Bridge *b = &_bridge[_bridgedata.indexes[i + w->vscroll.pos]]; - - SetDParam(2, _bridgedata.costs[i + w->vscroll.pos]); - SetDParam(1, b->speed); - SetDParam(0, b->material); - DrawSprite(b->sprite, 3, 15 + i * 22); - - DrawString(44, 15 + i * 22 , STR_500D, 0); - } - } break; - - case WE_KEYPRESS: { - uint i = e->we.keypress.keycode - '1'; - if (i < 9 && i < _bridgedata.count) { - e->we.keypress.cont = false; - BuildBridge(w, i); - } - - break; - } - - case WE_CLICK: - if (e->we.click.widget == 2) { - uint ind = ((int)e->we.click.pt.y - 14) / 22; - if (ind < 4 && (ind += w->vscroll.pos) < _bridgedata.count) - BuildBridge(w, ind); - } - break; - } -} - -static const Widget _build_bridge_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_100D_SELECT_RAIL_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK}, -{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WIDGETS_END}, -}; - -static const WindowDesc _build_bridge_desc = { - WDP_AUTO, WDP_AUTO, 200, 102, - WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_bridge_widgets, - BuildBridgeWndProc -}; - - -static const Widget _build_road_bridge_widgets[] = { -{ WWT_CLOSEBOX, RESIZE_NONE, 7, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, -{ WWT_CAPTION, RESIZE_NONE, 7, 11, 199, 0, 13, STR_1803_SELECT_ROAD_BRIDGE, STR_018C_WINDOW_TITLE_DRAG_THIS}, -{ WWT_MATRIX, RESIZE_NONE, 7, 0, 187, 14, 101, 0x401, STR_101F_BRIDGE_SELECTION_CLICK}, -{ WWT_SCROLLBAR, RESIZE_NONE, 7, 188, 199, 14, 101, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, -{ WIDGETS_END}, -}; - -static const WindowDesc _build_road_bridge_desc = { - WDP_AUTO, WDP_AUTO, 200, 102, - WC_BUILD_BRIDGE, WC_BUILD_TOOLBAR, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET, - _build_road_bridge_widgets, - BuildBridgeWndProc -}; - - -void ShowBuildBridgeWindow(TileIndex start, TileIndex end, byte bridge_type) -{ - uint j = 0; - int32 ret; - StringID errmsg; - - DeleteWindowById(WC_BUILD_BRIDGE, 0); - - _bridgedata.type = bridge_type; - _bridgedata.start_tile = start; - _bridgedata.end_tile = end; - - errmsg = INVALID_STRING_ID; - - // only query bridge building possibility once, result is the same for all bridges! - // returns CMD_ERROR on failure, and price on success - ret = DoCommand(end, start, (bridge_type << 8), DC_AUTO | DC_QUERY_COST, CMD_BUILD_BRIDGE); - - if (CmdFailed(ret)) { - errmsg = _error_message; - } else { - // check which bridges can be built - int bridge_len; // length of the middle parts of the bridge - int tot_bridgedata_len; // total length of bridge - - // get absolute bridge length - bridge_len = GetBridgeLength(start, end); - tot_bridgedata_len = bridge_len + 2; - - tot_bridgedata_len = CalcBridgeLenCostFactor(tot_bridgedata_len); - - for (bridge_type = 0; bridge_type != MAX_BRIDGES; bridge_type++) { // loop for all bridgetypes - if (CheckBridge_Stuff(bridge_type, bridge_len)) { - const Bridge *b = &_bridge[bridge_type]; - // bridge is accepted, add to list - // add to terraforming & bulldozing costs the cost of the bridge itself (not computed with DC_QUERY_COST) - _bridgedata.costs[j] = ret + (((int64)tot_bridgedata_len * _price.build_bridge * b->price) >> 8); - _bridgedata.indexes[j] = bridge_type; - j++; - } - } - } - - _bridgedata.count = j; - - if (j != 0) { - Window *w = AllocateWindowDesc((_bridgedata.type & 0x80) ? &_build_road_bridge_desc : &_build_bridge_desc); - w->vscroll.cap = 4; - w->vscroll.count = (byte)j; - } else { - ShowErrorMessage(errmsg, STR_5015_CAN_T_BUILD_BRIDGE_HERE, TileX(end) * TILE_SIZE, TileY(end) * TILE_SIZE); - } -} diff --git a/bridge_map.c b/bridge_map.c deleted file mode 100644 --- a/bridge_map.c +++ /dev/null @@ -1,51 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "bridge_map.h" -#include "variables.h" - - -TileIndex GetBridgeEnd(TileIndex tile, DiagDirection dir) -{ - TileIndexDiff delta = TileOffsByDiagDir(dir); - - dir = ReverseDiagDir(dir); - do { - tile += delta; - } while (!IsBridgeTile(tile) || GetBridgeRampDirection(tile) != dir); - - return tile; -} - - -TileIndex GetNorthernBridgeEnd(TileIndex t) -{ - return GetBridgeEnd(t, ReverseDiagDir(AxisToDiagDir(GetBridgeAxis(t)))); -} - - -TileIndex GetSouthernBridgeEnd(TileIndex t) -{ - return GetBridgeEnd(t, AxisToDiagDir(GetBridgeAxis(t))); -} - - -TileIndex GetOtherBridgeEnd(TileIndex tile) -{ - assert(IsBridgeTile(tile)); - return GetBridgeEnd(tile, GetBridgeRampDirection(tile)); -} - -uint GetBridgeHeight(TileIndex t) -{ - uint h; - uint tileh = GetTileSlope(t, &h); - uint f = GetBridgeFoundation(tileh, DiagDirToAxis(GetBridgeRampDirection(t))); - - // one height level extra if the ramp is on a flat foundation - return - h + TILE_HEIGHT + - (IS_INT_INSIDE(f, 1, 15) ? TILE_HEIGHT : 0) + - (IsSteepSlope(tileh) ? TILE_HEIGHT : 0); -} diff --git a/bridge_map.h b/bridge_map.h deleted file mode 100644 --- a/bridge_map.h +++ /dev/null @@ -1,163 +0,0 @@ -/* $Id$ */ - -#ifndef BRIDGE_MAP_H -#define BRIDGE_MAP_H - -#include "direction.h" -#include "macros.h" -#include "map.h" -#include "rail.h" -#include "road_map.h" -#include "tile.h" - - -void DrawBridgeMiddle(const TileInfo* ti); // XXX - - -static inline bool IsBridge(TileIndex t) -{ - assert(IsTileType(t, MP_TUNNELBRIDGE)); - return HASBIT(_m[t].m5, 7); -} - -static inline bool IsBridgeTile(TileIndex t) -{ - return IsTileType(t, MP_TUNNELBRIDGE) && IsBridge(t); -} - - -static inline bool MayHaveBridgeAbove(TileIndex t) -{ - return - IsTileType(t, MP_CLEAR) || - IsTileType(t, MP_RAILWAY) || - IsTileType(t, MP_STREET) || - IsTileType(t, MP_WATER) || - IsTileType(t, MP_TUNNELBRIDGE) || - IsTileType(t, MP_UNMOVABLE); -} - - -static inline bool IsBridgeAbove(TileIndex t) -{ - assert(MayHaveBridgeAbove(t)); - return GB(_m[t].extra, 6, 2) != 0; -} - - -/** - * Determines the type of bridge on a tile - * @param tile The tile to analyze - * @return The bridge type - */ -static inline uint GetBridgeType(TileIndex t) -{ - assert(IsBridgeTile(t)); - return GB(_m[t].m2, 4, 4); -} - - -/** - * Get the direction pointing onto the bridge - */ -static inline DiagDirection GetBridgeRampDirection(TileIndex t) -{ - assert(IsBridgeTile(t)); - return (DiagDirection)GB(_m[t].m5, 0, 2); -} - - -static inline Axis GetBridgeAxis(TileIndex t) -{ - assert(IsBridgeAbove(t)); - return (Axis)(GB(_m[t].extra, 6, 2) - 1); -} - - -static inline TransportType GetBridgeTransportType(TileIndex t) -{ - assert(IsBridgeTile(t)); - return (TransportType)GB(_m[t].m5, 2, 2); -} - - -static inline bool HasBridgeSnowOrDesert(TileIndex t) -{ - assert(IsBridgeTile(t)); - return HASBIT(_m[t].m4, 7); -} - - -static inline void SetBridgeSnowOrDesert(TileIndex t, bool snow_or_desert) -{ - assert(IsBridgeTile(t)); - SB(_m[t].m4, 7, 1, snow_or_desert); -} - -/** - * Finds the end of a bridge in the specified direction starting at a middle tile - */ -TileIndex GetBridgeEnd(TileIndex, DiagDirection); - -/** - * Finds the northern end of a bridge starting at a middle tile - */ -TileIndex GetNorthernBridgeEnd(TileIndex t); - -/** - * Finds the southern end of a bridge starting at a middle tile - */ -TileIndex GetSouthernBridgeEnd(TileIndex t); - - -/** - * Starting at one bridge end finds the other bridge end - */ -TileIndex GetOtherBridgeEnd(TileIndex); - -uint GetBridgeHeight(TileIndex tile); -uint GetBridgeFoundation(Slope tileh, Axis axis); - -static inline void ClearSingleBridgeMiddle(TileIndex t, Axis a) -{ - assert(MayHaveBridgeAbove(t)); - CLRBIT(_m[t].extra, 6 + a); -} - - -static inline void ClearBridgeMiddle(TileIndex t) -{ - ClearSingleBridgeMiddle(t, AXIS_X); - ClearSingleBridgeMiddle(t, AXIS_Y); -} - -static inline void SetBridgeMiddle(TileIndex t, Axis a) -{ - assert(MayHaveBridgeAbove(t)); - SETBIT(_m[t].extra, 6 + a); -} - - -static inline void MakeBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, TransportType tt) -{ - SetTileType(t, MP_TUNNELBRIDGE); - SetTileOwner(t, o); - _m[t].m2 = bridgetype << 4; - _m[t].m4 = 0; - _m[t].m5 = 1 << 7 | tt << 2 | d; -} - -static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d) -{ - MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD); - _m[t].m3 = 0; -} - -static inline void MakeRailBridgeRamp(TileIndex t, Owner o, uint bridgetype, DiagDirection d, RailType r) -{ - MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_RAIL); - _m[t].m3 = r; -} - - -#endif /* BRIDGE_MAP_H */ diff --git a/build_vehicle_gui.c b/build_vehicle_gui.c deleted file mode 100644 --- a/build_vehicle_gui.c +++ /dev/null @@ -1,492 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "aircraft.h" -#include "debug.h" -#include "functions.h" -#include "table/sprites.h" -#include "table/strings.h" -#include "window.h" -#include "gui.h" -#include "vehicle.h" -#include "gfx.h" -#include "station.h" -#include "command.h" -#include "engine.h" -#include "player.h" -#include "depot.h" -#include "airport.h" -#include "vehicle_gui.h" -#include "newgrf_engine.h" -#include "date.h" -#include "strings.h" - - -enum BuildVehicleWidgets { - BUILD_VEHICLE_WIDGET_CLOSEBOX = 0, - BUILD_VEHICLE_WIDGET_CAPTION, - BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, - BUILD_VEHICLE_WIDGET_SORT_TEXT, - BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, - BUILD_VEHICLE_WIDGET_LIST, - BUILD_VEHICLE_WIDGET_SCROLLBAR, - BUILD_VEHICLE_WIDGET_PANEL, - BUILD_VEHICLE_WIDGET_BUILD, - BUILD_VEHICLE_WIDGET_RENAME, - BUILD_VEHICLE_WIDGET_RESIZE, -}; - -static const Widget _build_vehicle_widgets[] = { - { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW }, - { WWT_CAPTION, RESIZE_NONE, 14, 11, 239, 0, 13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS }, - { WWT_PUSHTXTBTN, RESIZE_NONE, 14, 0, 80, 14, 25, STR_SORT_BY, STR_SORT_ORDER_TIP}, - { WWT_PANEL, RESIZE_NONE, 14, 81, 227, 14, 25, 0x0, STR_SORT_CRITERIA_TIP}, - { WWT_TEXTBTN, RESIZE_NONE, 14, 228, 239, 14, 25, STR_0225, STR_SORT_CRITERIA_TIP}, - { WWT_MATRIX, RESIZE_BOTTOM, 14, 0, 227, 26, 121, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST }, - { WWT_SCROLLBAR, RESIZE_BOTTOM, 14, 228, 239, 26, 121, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST }, - { WWT_PANEL, RESIZE_TB, 14, 0, 239, 122, 213, 0x0, STR_NULL }, - - { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 114, 214, 225, STR_A006_BUILD_AIRCRAFT, STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT }, - { WWT_PUSHTXTBTN, RESIZE_TB, 14, 115, 227, 214, 225, STR_A037_RENAME, STR_A038_RENAME_AIRCRAFT_TYPE }, - { WWT_RESIZEBOX, RESIZE_TB, 14, 228, 239, 214, 225, 0x0, STR_RESIZE_BUTTON }, - { WIDGETS_END}, -}; - -static bool _internal_sort_order; // descending/ascending -static byte _last_sort_criteria = 0; -static bool _last_sort_order = false; - -static int CDECL EngineNumberSorter(const void *a, const void *b) -{ - const EngineID va = *(const EngineID*)a; - const EngineID vb = *(const EngineID*)b; - int r = va - vb; - - return _internal_sort_order ? -r : r; -} - -static int CDECL EngineIntroDateSorter(const void *a, const void *b) -{ - const int va = GetEngine(*(const EngineID*)a)->intro_date; - const int vb = GetEngine(*(const EngineID*)b)->intro_date; - const int r = va - vb; - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -static int CDECL EngineNameSorter(const void *a, const void *b) -{ - static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE }; - static char last_name[2][64] = { "\0", "\0" }; - - const EngineID va = *(const EngineID*)a; - const EngineID vb = *(const EngineID*)b; - int r; - - if (va != last_engine[0]) { - last_engine[0] = va; - GetString(last_name[0], GetCustomEngineName(va), lastof(last_name[0])); - } - - if (vb != last_engine[1]) { - last_engine[1] = vb; - GetString(last_name[1], GetCustomEngineName(vb), lastof(last_name[1])); - } - - r = strcmp(last_name[0], last_name[1]); // sort by name - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -static int CDECL EngineReliabilitySorter(const void *a, const void *b) -{ - const int va = GetEngine(*(const EngineID*)a)->reliability; - const int vb = GetEngine(*(const EngineID*)b)->reliability; - const int r = va - vb; - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -/* Aircraft sorting functions */ - -static int CDECL AircraftEngineCostSorter(const void *a, const void *b) -{ - const int va = AircraftVehInfo(*(const EngineID*)a)->base_cost; - const int vb = AircraftVehInfo(*(const EngineID*)b)->base_cost; - int r = va - vb; - - return _internal_sort_order ? -r : r; -} - -static int CDECL AircraftEngineSpeedSorter(const void *a, const void *b) -{ - const int va = AircraftVehInfo(*(const EngineID*)a)->max_speed; - const int vb = AircraftVehInfo(*(const EngineID*)b)->max_speed; - const int r = va - vb; - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -static int CDECL AircraftEngineRunningCostSorter(const void *a, const void *b) -{ - const int va = AircraftVehInfo(*(const EngineID*)a)->running_cost; - const int vb = AircraftVehInfo(*(const EngineID*)b)->running_cost; - const int r = va - vb; - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -static int CDECL AircraftEngineCargoSorter(const void *a, const void *b) -{ - const int va = AircraftVehInfo(*(const EngineID*)a)->passenger_capacity; - const int vb = AircraftVehInfo(*(const EngineID*)b)->passenger_capacity; - const int r = va - vb; - - if (r == 0) { - /* Use EngineID to sort instead since we want consistent sorting */ - return EngineNumberSorter(a, b); - } - return _internal_sort_order ? -r : r; -} - -static EngList_SortTypeFunction * const _aircraft_sorter[] = { - &EngineNumberSorter, - &AircraftEngineCostSorter, - &AircraftEngineSpeedSorter, - &EngineIntroDateSorter, - &EngineNameSorter, - &AircraftEngineRunningCostSorter, - &EngineReliabilitySorter, - &AircraftEngineCargoSorter, -}; - -static const StringID _aircraft_sort_listing[] = { - STR_ENGINE_SORT_ENGINE_ID, - STR_ENGINE_SORT_COST, - STR_SORT_BY_MAX_SPEED, - STR_ENGINE_SORT_INTRO_DATE, - STR_SORT_BY_DROPDOWN_NAME, - STR_ENGINE_SORT_RUNNING_COST, - STR_SORT_BY_RELIABILITY, - STR_ENGINE_SORT_CARGO_CAPACITY, - INVALID_STRING_ID -}; - - -/** -* Draw the purchase info details of an aircraft at a given location. - * @param x,y location where to draw the info - * @param engine_number the engine of which to draw the info of - */ -void DrawAircraftPurchaseInfo(int x, int y, uint w, EngineID engine_number) -{ - const AircraftVehicleInfo *avi = AircraftVehInfo(engine_number); - const Engine *e = GetEngine(engine_number); - CargoID cargo; - YearMonthDay ymd; - ConvertDateToYMD(e->intro_date, &ymd); - - /* Purchase cost - Max speed */ - SetDParam(0, avi->base_cost * (_price.aircraft_base>>3)>>5); - SetDParam(1, avi->max_speed * 128 / 10); - DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, 0); - y += 10; - - /* Cargo capacity */ - cargo = FindFirstRefittableCargo(engine_number); - if (cargo == CT_INVALID || cargo == CT_PASSENGERS) { - SetDParam(0, avi->passenger_capacity); - SetDParam(1, avi->mail_capacity); - DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, 0); - } else { - /* Note, if the default capacity is selected by the refit capacity - * callback, then the capacity shown is likely to be incorrect. */ - SetDParam(0, cargo); - SetDParam(1, AircraftDefaultCargoCapacity(cargo, engine_number)); - SetDParam(2, STR_9842_REFITTABLE); - DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, 0); - } - y += 10; - - /* Running cost */ - SetDParam(0, avi->running_cost * _price.aircraft_running >> 8); - DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, 0); - y += 10; - - /* Design date - Life length */ - SetDParam(0, ymd.year); - SetDParam(1, e->lifelength); - DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, 0); - y += 10; - - /* Reliability */ - SetDParam(0, e->reliability * 100 >> 16); - DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, 0); - y += 10; - - /* Additional text from NewGRF */ - y += ShowAdditionalText(x, y, w, engine_number); - y += ShowRefitOptionsList(x, y, w, engine_number); -} - -void DrawAircraftImage(const Vehicle *v, int x, int y, VehicleID selection) -{ - PalSpriteID pal = (v->vehstatus & VS_CRASHED) ? PALETTE_CRASH : GetVehiclePalette(v); - DrawSprite(GetAircraftImage(v, DIR_W) | pal, x + 25, y + 10); - if (v->subtype == 0) { - SpriteID rotor_sprite = GetCustomRotorSprite(v, true); - if (rotor_sprite == 0) rotor_sprite = SPR_ROTOR_STOPPED; - DrawSprite(rotor_sprite, x + 25, y + 5); - } - if (v->index == selection) { - DrawFrameRect(x - 1, y - 1, x + 58, y + 21, 0xF, FR_BORDERONLY); - } -} - -void CcBuildAircraft(bool success, TileIndex tile, uint32 p1, uint32 p2) -{ - if (success) { - const Vehicle *v = GetVehicle(_new_vehicle_id); - - if (v->tile == _backup_orders_tile) { - _backup_orders_tile = 0; - RestoreVehicleOrders(v, _backup_orders_data); - } - ShowAircraftViewWindow(v); - } -} - -static void GenerateBuildAircraftList(Window *w) -{ - EngineID eid, sel_id; - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - EngList_RemoveAll(&bv->eng_list); - - /* Make list of all available planes. - * Also check to see if the previously selected plane is still available, - * and if not, reset selection to INVALID_ENGINE. This could be the case - * when planes become obsolete and are removed */ - sel_id = INVALID_ENGINE; - for (eid = AIRCRAFT_ENGINES_INDEX; eid < AIRCRAFT_ENGINES_INDEX + NUM_AIRCRAFT_ENGINES; eid++) { - if (IsEngineBuildable(eid, VEH_Aircraft, _local_player)) { - const AircraftVehicleInfo *avi = AircraftVehInfo(eid); - switch (bv->filter.acc_planes) { - case HELICOPTERS_ONLY: - if (avi->subtype != 0) continue; // if not helicopter - break; - - case AIRCRAFT_ONLY: - if (avi->subtype == 0) continue; // if helicopter - break; - - case ALL: break; - } - EngList_Add(&bv->eng_list, eid); - - if (eid == bv->sel_engine) sel_id = eid; - } - } - - bv->sel_engine = sel_id; -} - -static void GenerateBuildList(Window *w) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - switch (bv->vehicle_type) { - case VEH_Aircraft: - GenerateBuildAircraftList(w); - _internal_sort_order = bv->descending_sort_order; - EngList_Sort(&bv->eng_list, _aircraft_sorter[bv->sort_criteria]); - break; - - default: NOT_REACHED(); - } -} - -static void DrawBuildAircraftWindow(Window *w) -{ - const buildvehicle_d *bv = &WP(w, buildvehicle_d); - - SetWindowWidgetDisabledState(w, BUILD_VEHICLE_WIDGET_BUILD, w->window_number == 0); - - SetVScrollCount(w, EngList_Count(&bv->eng_list)); - DrawWindowWidgets(w); - - { - int x = 2; - int y = 27; - EngineID selected_id = bv->sel_engine; - EngineID eid = w->vscroll.pos; - uint16 max = min(w->vscroll.pos + w->vscroll.cap, EngList_Count(&bv->eng_list)); - - for (; eid < max; eid++) { - const EngineID engine = bv->eng_list[eid]; - - DrawString(x + 62, y + 7, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10); - DrawAircraftEngine(x + 29, y + 10, engine, GetEnginePalette(engine, _local_player)); - y += 24; - } - - if (selected_id != INVALID_ENGINE) { - const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL]; - DrawAircraftPurchaseInfo(x, wi->top + 1, wi->right - wi->left - 2, selected_id); - } - } - DrawString(85, 15, _aircraft_sort_listing[bv->sort_criteria], 0x10); - DoDrawString(bv->descending_sort_order ? DOWNARROW : UPARROW, 69, 15, 0x10); -} - -static void BuildAircraftClickEvent(Window *w, WindowEvent *e) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - switch (e->we.click.widget) { - case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: - bv->descending_sort_order ^= true; - _last_sort_order = bv->descending_sort_order; - GenerateBuildList(w); - SetWindowDirty(w); - break; - - case BUILD_VEHICLE_WIDGET_LIST: { - uint i = (e->we.click.pt.y - 26) / 24 + w->vscroll.pos; - uint num_items = EngList_Count(&bv->eng_list); - bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE; - SetWindowDirty(w); - break; - } - - case BUILD_VEHICLE_WIDGET_SORT_TEXT: case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:/* Select sorting criteria dropdown menu */ - ShowDropDownMenu(w, _aircraft_sort_listing, bv->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0); - return; - - case BUILD_VEHICLE_WIDGET_BUILD: { - EngineID sel_eng = bv->sel_engine; - if (sel_eng != INVALID_ENGINE) { - DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)); - } - break; - } - - case BUILD_VEHICLE_WIDGET_RENAME: { - EngineID sel_eng = bv->sel_engine; - if (sel_eng != INVALID_ENGINE) { - bv->rename_engine = sel_eng; - ShowQueryString(GetCustomEngineName(sel_eng), STR_A039_RENAME_AIRCRAFT_TYPE, 31, 160, w, CS_ALPHANUMERAL); - } - break; - } - } -} - -static void NewAircraftWndProc(Window *w, WindowEvent *e) -{ - buildvehicle_d *bv = &WP(w, buildvehicle_d); - - switch (e->event) { - case WE_INVALIDATE_DATA: - GenerateBuildList(w); - break; - - case WE_DESTROY: - EngList_Destroy(&bv->eng_list); - break; - - case WE_PAINT: - DrawBuildAircraftWindow(w); - break; - - case WE_CLICK: - BuildAircraftClickEvent(w, e); - break; - - case WE_ON_EDIT_TEXT: { - if (e->we.edittext.str[0] != '\0') { - _cmd_text = e->we.edittext.str; - DoCommandP(0, bv->rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE)); - } - break; - } - - case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */ - if (bv->sort_criteria != e->we.dropdown.index) { - bv->sort_criteria = _last_sort_criteria = e->we.dropdown.index; - GenerateBuildList(w); - } - SetWindowDirty(w); - break; - - case WE_RESIZE: - w->vscroll.cap += e->we.sizing.diff.y / 24; - w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1; - break; - } -} - -static const WindowDesc _build_vehicle_desc = { - WDP_AUTO, WDP_AUTO, 240, 226, - WC_BUILD_VEHICLE,0, - WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE, - _build_vehicle_widgets, - NewAircraftWndProc -}; - -void ShowBuildVehicleWindow(TileIndex tile, byte type) -{ - buildvehicle_d *bv; - Window *w; - - DeleteWindowById(WC_BUILD_VEHICLE, tile); - w = AllocateWindowDescFront(&_build_vehicle_desc, tile); - if (w == NULL) return; - - w->caption_color = (tile != 0) ? GetTileOwner(tile) : _local_player; - w->resize.step_height = GetVehicleListHeight(type); - w->vscroll.cap = 4; - w->widget[BUILD_VEHICLE_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1; - - bv = &WP(w, buildvehicle_d); - EngList_Create(&bv->eng_list); - bv->sel_engine = INVALID_ENGINE; - bv->sort_criteria = _last_sort_criteria; - bv->descending_sort_order = _last_sort_order; - - bv->vehicle_type = type; - - switch (type) { - case VEH_Aircraft: { - byte acc_planes = (tile == 0) ? ALL : GetAirport(GetStationByTile(tile)->airport_type)->acc_planes; - bv->filter.acc_planes = acc_planes; - break; - } - default: NOT_REACHED(); - } - - GenerateBuildList(w); - /* Select the first plane in the list as default when opening the window */ - if (EngList_Count(&bv->eng_list) > 0) bv->sel_engine = bv->eng_list[0]; -} diff --git a/callback_table.c b/callback_table.c deleted file mode 100644 --- a/callback_table.c +++ /dev/null @@ -1,91 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "callback_table.h" -#include "functions.h" - -// If you add a callback for DoCommandP, also add the callback in here -// see below for the full list! -// If you don't do it, it won't work across the network!! - -/* aircraft_gui.c */ -CommandCallback CcBuildAircraft; -CommandCallback CcCloneAircraft; - -/* airport_gui.c */ -CommandCallback CcBuildAirport; - -/* bridge_gui.c */ -CommandCallback CcBuildBridge; - -/* dock_gui.c */ -CommandCallback CcBuildDocks; -CommandCallback CcBuildCanal; - -/* depot_gui.c */ -CommandCallback CcCloneVehicle; - -/* main_gui.c */ -CommandCallback CcPlaySound10; -CommandCallback CcPlaceSign; -CommandCallback CcTerraform; -CommandCallback CcBuildTown; - -/* rail_gui.c */ -CommandCallback CcPlaySound1E; -CommandCallback CcRailDepot; -CommandCallback CcStation; -CommandCallback CcBuildRailTunnel; - -/* road_gui.c */ -CommandCallback CcPlaySound1D; -CommandCallback CcBuildRoadTunnel; -CommandCallback CcRoadDepot; - -/* roadveh_gui.c */ -CommandCallback CcBuildRoadVeh; -CommandCallback CcCloneRoadVeh; - -/* ship_gui.c */ -CommandCallback CcBuildShip; -CommandCallback CcCloneShip; - -/* train_gui.c */ -CommandCallback CcBuildWagon; -CommandCallback CcBuildLoco; -CommandCallback CcCloneTrain; - -CommandCallback CcAI; - -CommandCallback *_callback_table[] = { - /* 0x00 */ NULL, - /* 0x01 */ CcBuildAircraft, - /* 0x02 */ CcBuildAirport, - /* 0x03 */ CcBuildBridge, - /* 0x04 */ CcBuildCanal, - /* 0x05 */ CcBuildDocks, - /* 0x06 */ CcBuildLoco, - /* 0x07 */ CcBuildRoadVeh, - /* 0x08 */ CcBuildShip, - /* 0x09 */ CcBuildTown, - /* 0x0A */ CcBuildRoadTunnel, - /* 0x0B */ CcBuildRailTunnel, - /* 0x0C */ CcBuildWagon, - /* 0x0D */ CcRoadDepot, - /* 0x0E */ CcRailDepot, - /* 0x0F */ CcPlaceSign, - /* 0x10 */ CcPlaySound10, - /* 0x11 */ CcPlaySound1D, - /* 0x12 */ CcPlaySound1E, - /* 0x13 */ CcStation, - /* 0x14 */ CcTerraform, - /* 0x15 */ CcCloneAircraft, - /* 0x16 */ CcCloneRoadVeh, - /* 0x17 */ CcCloneShip, - /* 0x18 */ CcCloneTrain, - /* 0x19 */ CcAI, - /* 0x1A */ CcCloneVehicle -}; - -const int _callback_table_count = lengthof(_callback_table); diff --git a/callback_table.h b/callback_table.h deleted file mode 100644 --- a/callback_table.h +++ /dev/null @@ -1,11 +0,0 @@ -/* $Id$ */ - -#ifndef CALLBACK_TABLE_H -#define CALLBACK_TABLE_H - -#include "command.h" - -extern CommandCallback *_callback_table[]; -extern const int _callback_table_count; - -#endif /* CALLBACK_TABLE_H */ diff --git a/clear_cmd.c b/clear_cmd.c deleted file mode 100644 --- a/clear_cmd.c +++ /dev/null @@ -1,803 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "clear_map.h" -#include "rail_map.h" -#include "table/strings.h" -#include "functions.h" -#include "map.h" -#include "player.h" -#include "tile.h" -#include "viewport.h" -#include "command.h" -#include "tunnel_map.h" -#include "bridge_map.h" -#include "variables.h" -#include "table/sprites.h" -#include "unmovable_map.h" -#include "genworld.h" -#include "industry.h" - -typedef struct TerraformerHeightMod { - TileIndex tile; - byte height; -} TerraformerHeightMod; - -typedef struct TerraformerState { - int height[4]; - uint32 flags; - - int direction; - int modheight_count; - int tile_table_count; - - int32 cost; - - TileIndex *tile_table; - TerraformerHeightMod *modheight; - -} TerraformerState; - -static int TerraformAllowTileProcess(TerraformerState *ts, TileIndex tile) -{ - TileIndex *t; - int count; - - if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return -1; - - t = ts->tile_table; - for (count = ts->tile_table_count; count != 0; count--, t++) { - if (*t == tile) return 0; - } - - return 1; -} - -static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile) -{ - TerraformerHeightMod *mod = ts->modheight; - int count; - - for (count = ts->modheight_count; count != 0; count--, mod++) { - if (mod->tile == tile) return mod->height; - } - - return TileHeight(tile); -} - -static void TerraformAddDirtyTile(TerraformerState *ts, TileIndex tile) -{ - int count; - TileIndex *t; - - count = ts->tile_table_count; - - if (count >= 625) return; - - for (t = ts->tile_table; count != 0; count--,t++) { - if (*t == tile) return; - } - - ts->tile_table[ts->tile_table_count++] = tile; -} - -static void TerraformAddDirtyTileAround(TerraformerState *ts, TileIndex tile) -{ - TerraformAddDirtyTile(ts, tile + TileDiffXY( 0, -1)); - TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, -1)); - TerraformAddDirtyTile(ts, tile + TileDiffXY(-1, 0)); - TerraformAddDirtyTile(ts, tile); -} - -static int TerraformProc(TerraformerState *ts, TileIndex tile, int mode) -{ - int r; - int32 ret; - - assert(tile < MapSize()); - - r = TerraformAllowTileProcess(ts, tile); - if (r <= 0) return r; - - if (IsTileType(tile, MP_RAILWAY)) { - static const TrackBits safe_track[] = { TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER, TRACK_BIT_RIGHT }; - static const Slope unsafe_slope[] = { SLOPE_S, SLOPE_W, SLOPE_N, SLOPE_E }; - - Slope tileh; - uint z; - - // Nothing could be built at the steep slope - this avoids a bug - // when you have a single diagonal track in one corner on a - // basement and then you raise/lower the other corner. - tileh = GetTileSlope(tile, &z); - if (tileh == unsafe_slope[mode] || - tileh == ComplementSlope(unsafe_slope[mode])) { - _terraform_err_tile = tile; - _error_message = STR_1008_MUST_REMOVE_RAILROAD_TRACK; - return -1; - } - - // If we have a single diagonal track there, the other side of - // tile can be terraformed. - if (IsPlainRailTile(tile) && GetTrackBits(tile) == safe_track[mode]) { - /* If terraforming downwards prevent damaging a potential tunnel below. - * This check is only necessary for flat tiles, because if the tile is - * non-flat, then the corner opposing the rail is raised. Only this corner - * can be lowered and this is a safe action - */ - if (tileh == SLOPE_FLAT && - ts->direction == -1 && - IsTunnelInWay(tile, z - TILE_HEIGHT)) { - _terraform_err_tile = tile; - _error_message = STR_1002_EXCAVATION_WOULD_DAMAGE; - return -1; - } - return 0; - } - } - - ret = DoCommand(tile, 0,0, ts->flags & ~DC_EXEC, CMD_LANDSCAPE_CLEAR); - - if (CmdFailed(ret)) { - _terraform_err_tile = tile; - return -1; - } - - ts->cost += ret; - - if (ts->tile_table_count >= 625) return -1; - ts->tile_table[ts->tile_table_count++] = tile; - - return 0; -} - -static bool TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) -{ - int nh; - TerraformerHeightMod *mod; - int count; - - assert(tile < MapSize()); - - if (height < 0) { - _error_message = STR_1003_ALREADY_AT_SEA_LEVEL; - return false; - } - - _error_message = STR_1004_TOO_HIGH; - - if (height > 15) return false; - - nh = TerraformGetHeightOfTile(ts, tile); - if (nh < 0 || height == nh) return false; - - if (TerraformProc(ts, tile, 0) < 0) return false; - if (TerraformProc(ts, tile + TileDiffXY( 0, -1), 1) < 0) return false; - if (TerraformProc(ts, tile + TileDiffXY(-1, -1), 2) < 0) return false; - if (TerraformProc(ts, tile + TileDiffXY(-1, 0), 3) < 0) return false; - - mod = ts->modheight; - count = ts->modheight_count; - - for (;;) { - if (count == 0) { - if (ts->modheight_count >= 576) return false; - ts->modheight_count++; - break; - } - if (mod->tile == tile) break; - mod++; - count--; - } - - mod->tile = tile; - mod->height = (byte)height; - - ts->cost += _price.terraform; - - { - int direction = ts->direction, r; - const TileIndexDiffC *ttm; - - static const TileIndexDiffC _terraform_tilepos[] = { - { 1, 0}, - {-2, 0}, - { 1, 1}, - { 0, -2} - }; - - for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) { - tile += ToTileIndexDiff(*ttm); - - r = TerraformGetHeightOfTile(ts, tile); - if (r != height && r-direction != height && r+direction != height) { - if (!TerraformTileHeight(ts, tile, r+direction)) - return false; - } - } - } - - return true; -} - -/** Terraform land - * @param tile tile to terraform - * @param p1 corners to terraform. - * @param p2 direction; eg up or down - */ -int32 CmdTerraformLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - TerraformerState ts; - TileIndex t; - int direction; - - TerraformerHeightMod modheight_data[576]; - TileIndex tile_table_data[625]; - - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - - _terraform_err_tile = 0; - - ts.direction = direction = p2 ? 1 : -1; - ts.flags = flags; - ts.modheight_count = ts.tile_table_count = 0; - ts.cost = 0; - ts.modheight = modheight_data; - ts.tile_table = tile_table_data; - - /* Make an extra check for map-bounds cause we add tiles to the originating tile */ - if (tile + TileDiffXY(1, 1) >= MapSize()) return CMD_ERROR; - - if (p1 & 1) { - t = tile + TileDiffXY(1, 0); - if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) { - return CMD_ERROR; - } - } - - if (p1 & 2) { - t = tile + TileDiffXY(1, 1); - if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) { - return CMD_ERROR; - } - } - - if (p1 & 4) { - t = tile + TileDiffXY(0, 1); - if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) { - return CMD_ERROR; - } - } - - if (p1 & 8) { - t = tile + TileDiffXY(0, 0); - if (!TerraformTileHeight(&ts, t, TileHeight(t) + direction)) { - return CMD_ERROR; - } - } - - { - /* Check if tunnel would take damage */ - int count; - TileIndex *ti = ts.tile_table; - - for (count = ts.tile_table_count; count != 0; count--, ti++) { - TileIndex tile = *ti; - - if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) { - return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST); - } - - if (direction == -1) { - uint z, t; - - z = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0)); - t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0)); - if (t <= z) z = t; - t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1)); - if (t <= z) z = t; - t = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1)); - if (t <= z) z = t; - - if (IsTunnelInWay(tile, z * TILE_HEIGHT)) { - return_cmd_error(STR_1002_EXCAVATION_WOULD_DAMAGE); - } - } - } - } - - if (flags & DC_EXEC) { - /* Clear the landscape at the tiles */ - { - int count; - TileIndex *ti = ts.tile_table; - for (count = ts.tile_table_count; count != 0; count--, ti++) { - DoCommand(*ti, 0, 0, flags, CMD_LANDSCAPE_CLEAR); - } - } - - /* change the height */ - { - int count; - TerraformerHeightMod *mod; - - mod = ts.modheight; - for (count = ts.modheight_count; count != 0; count--, mod++) { - TileIndex til = mod->tile; - - SetTileHeight(til, mod->height); - TerraformAddDirtyTileAround(&ts, til); - } - } - - /* finally mark the dirty tiles dirty */ - { - int count; - TileIndex *ti = ts.tile_table; - for (count = ts.tile_table_count; count != 0; count--, ti++) { - MarkTileDirtyByTile(*ti); - } - } - } - return ts.cost; -} - - -/** Levels a selected (rectangle) area of land - * @param tile end tile of area-drag - * @param p1 start tile of area drag - * @param p2 unused - */ -int32 CmdLevelLand(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - int size_x, size_y; - int ex; - int ey; - int sx, sy; - uint h, curh; - int32 ret, cost, money; - - if (p1 >= MapSize()) return CMD_ERROR; - - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - - // remember level height - h = TileHeight(p1); - - // make sure sx,sy are smaller than ex,ey - ex = TileX(tile); - ey = TileY(tile); - sx = TileX(p1); - sy = TileY(p1); - if (ex < sx) intswap(ex, sx); - if (ey < sy) intswap(ey, sy); - tile = TileXY(sx, sy); - - size_x = ex-sx+1; - size_y = ey-sy+1; - - money = GetAvailableMoneyForCommand(); - cost = 0; - - BEGIN_TILE_LOOP(tile2, size_x, size_y, tile) { - curh = TileHeight(tile2); - while (curh != h) { - ret = DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); - if (CmdFailed(ret)) break; - cost += ret; - - if (flags & DC_EXEC) { - if ((money -= ret) < 0) { - _additional_cash_required = ret; - return cost - ret; - } - DoCommand(tile2, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); - } - - curh += (curh > h) ? -1 : 1; - } - } END_TILE_LOOP(tile2, size_x, size_y, tile) - - return (cost == 0) ? CMD_ERROR : cost; -} - -/** Purchase a land area. Actually you only purchase one tile, so - * the name is a bit confusing ;p - * @param tile the tile the player is purchasing - * @param p1 unused - * @param p2 unused - */ -int32 CmdPurchaseLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - int32 cost; - - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - - if (!EnsureNoVehicle(tile)) return CMD_ERROR; - - if (IsOwnedLandTile(tile) && IsTileOwner(tile, _current_player)) { - return_cmd_error(STR_5807_YOU_ALREADY_OWN_IT); - } - - cost = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); - if (CmdFailed(cost)) return CMD_ERROR; - - if (flags & DC_EXEC) { - MakeOwnedLand(tile, _current_player); - MarkTileDirtyByTile(tile); - } - - return cost + _price.purchase_land * 10; -} - - -static int32 ClearTile_Clear(TileIndex tile, byte flags) -{ - static const int32* clear_price_table[] = { - &_price.clear_1, - &_price.purchase_land, - &_price.clear_2, - &_price.clear_3, - &_price.purchase_land, - &_price.purchase_land, - &_price.clear_2, // XXX unused? - }; - int32 price; - - if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) { - price = 0; - } else { - price = *clear_price_table[GetClearGround(tile)]; - } - - if (flags & DC_EXEC) DoClearSquare(tile); - - return price; -} - -/** Sell a land area. Actually you only sell one tile, so - * the name is a bit confusing ;p - * @param tile the tile the player is selling - * @param p1 unused - * @param p2 unused - */ -int32 CmdSellLandArea(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) -{ - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - - if (!IsOwnedLandTile(tile)) return CMD_ERROR; - if (!CheckTileOwnership(tile) && _current_player != OWNER_WATER) return CMD_ERROR; - - - if (!EnsureNoVehicle(tile)) return CMD_ERROR; - - if (flags & DC_EXEC) DoClearSquare(tile); - - return - _price.purchase_land * 2; -} - - -#include "table/clear_land.h" - - -void DrawClearLandTile(const TileInfo *ti, byte set) -{ - DrawGroundSprite(SPR_FLAT_BARE_LAND + _tileh_to_sprite[ti->tileh] + set * 19); -} - -void DrawHillyLandTile(const TileInfo *ti) -{ - if (ti->tileh != SLOPE_FLAT) { - DrawGroundSprite(SPR_FLAT_ROUGH_LAND + _tileh_to_sprite[ti->tileh]); - } else { - DrawGroundSprite(_landscape_clear_sprites[GB(ti->x ^ ti->y, 4, 3)]); - } -} - -void DrawClearLandFence(const TileInfo *ti) -{ - byte z = ti->z; - - if (ti->tileh & SLOPE_S) { - z += TILE_HEIGHT; - if (ti->tileh == SLOPE_STEEP_S) z += TILE_HEIGHT; - } - - if (GetFenceSW(ti->tile) != 0) { - DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSW(ti->tile) - 1] + _fence_mod_by_tileh[ti->tileh], ti->x, ti->y, z); - } - - if (GetFenceSE(ti->tile) != 0) { - DrawGroundSpriteAt(_clear_land_fence_sprites_1[GetFenceSE(ti->tile) - 1] + _fence_mod_by_tileh_2[ti->tileh], ti->x, ti->y, z); - } -} - -static void DrawTile_Clear(TileInfo *ti) -{ - switch (GetClearGround(ti->tile)) { - case CLEAR_GRASS: - DrawClearLandTile(ti, GetClearDensity(ti->tile)); - break; - - case CLEAR_ROUGH: - DrawHillyLandTile(ti); - break; - - case CLEAR_ROCKS: - DrawGroundSprite(SPR_FLAT_ROCKY_LAND_1 + _tileh_to_sprite[ti->tileh]); - break; - - case CLEAR_FIELDS: - DrawGroundSprite(_clear_land_sprites_1[GetFieldType(ti->tile)] + _tileh_to_sprite[ti->tileh]); - break; - - case CLEAR_SNOW: - DrawGroundSprite(_clear_land_sprites_2[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]); - break; - - case CLEAR_DESERT: - DrawGroundSprite(_clear_land_sprites_3[GetClearDensity(ti->tile)] + _tileh_to_sprite[ti->tileh]); - break; - } - - DrawClearLandFence(ti); - DrawBridgeMiddle(ti); -} - -static uint GetSlopeZ_Clear(TileIndex tile, uint x, uint y) -{ - uint z; - uint tileh = GetTileSlope(tile, &z); - - return z + GetPartialZ(x & 0xF, y & 0xF, tileh); -} - -static Slope GetSlopeTileh_Clear(TileIndex tile, Slope tileh) -{ - return tileh; -} - -static void GetAcceptedCargo_Clear(TileIndex tile, AcceptedCargo ac) -{ - /* unused */ -} - -static void AnimateTile_Clear(TileIndex tile) -{ - /* unused */ -} - -void TileLoopClearHelper(TileIndex tile) -{ - byte self; - byte neighbour; - TileIndex dirty = INVALID_TILE; - - self = (IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS)); - - neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS)); - if (GetFenceSW(tile) == 0) { - if (self != neighbour) { - SetFenceSW(tile, 3); - dirty = tile; - } - } else { - if (self == 0 && neighbour == 0) { - SetFenceSW(tile, 0); - dirty = tile; - } - } - - neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS)); - if (GetFenceSE(tile) == 0) { - if (self != neighbour) { - SetFenceSE(tile, 3); - dirty = tile; - } - } else { - if (self == 0 && neighbour == 0) { - SetFenceSE(tile, 0); - dirty = tile; - } - } - - if (dirty != INVALID_TILE) MarkTileDirtyByTile(dirty); -} - - -/* convert into snowy tiles */ -static void TileLoopClearAlps(TileIndex tile) -{ - int k = GetTileZ(tile) - _opt.snow_line + TILE_HEIGHT; - - if (k < 0) { // well below the snow line - if (!IsClearGround(tile, CLEAR_SNOW)) return; - if (GetClearDensity(tile) == 0) SetClearGroundDensity(tile, CLEAR_GRASS, 3); - } else { - if (!IsClearGround(tile, CLEAR_SNOW)) { - SetClearGroundDensity(tile, CLEAR_SNOW, 0); - } else { - uint density = min((uint)k / TILE_HEIGHT, 3); - - if (GetClearDensity(tile) < density) { - AddClearDensity(tile, 1); - } else if (GetClearDensity(tile) > density) { - AddClearDensity(tile, -1); - } else { - return; - } - } - } - - MarkTileDirtyByTile(tile); -} - -static void TileLoopClearDesert(TileIndex tile) -{ - if (IsClearGround(tile, CLEAR_DESERT)) return; - - if (GetTropicZone(tile) == TROPICZONE_DESERT) { - SetClearGroundDensity(tile, CLEAR_DESERT, 3); - } else { - if (GetTropicZone(tile + TileDiffXY( 1, 0)) != TROPICZONE_DESERT && - GetTropicZone(tile + TileDiffXY(-1, 0)) != TROPICZONE_DESERT && - GetTropicZone(tile + TileDiffXY( 0, 1)) != TROPICZONE_DESERT && - GetTropicZone(tile + TileDiffXY( 0, -1)) != TROPICZONE_DESERT) - return; - SetClearGroundDensity(tile, CLEAR_DESERT, 1); - } - - MarkTileDirtyByTile(tile); -} - -static void TileLoop_Clear(TileIndex tile) -{ - TileLoopClearHelper(tile); - - switch (_opt.landscape) { - case LT_DESERT: TileLoopClearDesert(tile); break; - case LT_HILLY: TileLoopClearAlps(tile); break; - } - - switch (GetClearGround(tile)) { - case CLEAR_GRASS: - if (GetClearDensity(tile) == 3) return; - - if (_game_mode != GM_EDITOR) { - if (GetClearCounter(tile) < 7) { - AddClearCounter(tile, 1); - return; - } else { - SetClearCounter(tile, 0); - AddClearDensity(tile, 1); - } - } else { - SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3); - } - break; - - case CLEAR_FIELDS: { - uint field_type; - - if (_game_mode == GM_EDITOR) return; - - if (GetClearCounter(tile) < 7) { - AddClearCounter(tile, 1); - return; - } else { - SetClearCounter(tile, 0); - } - - if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) { - /* This farmfield is no longer farmfield, so make it grass again */ - MakeClear(tile, CLEAR_GRASS, 2); - } else { - field_type = GetFieldType(tile); - field_type = (field_type < 8) ? field_type + 1 : 0; - SetFieldType(tile, field_type); - } - break; - } - - default: - return; - } - - MarkTileDirtyByTile(tile); -} - -void GenerateClearTile(void) -{ - uint i, gi; - TileIndex tile; - - /* 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) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3); - } while (--i); - - /* add rocky tiles */ - i = gi; - do { - uint32 r = Random(); - tile = RandomTileSeed(r); - - IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY); - if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) { - uint j = GB(r, 16, 4) + 5; - for (;;) { - TileIndex tile_new; - - SetClearGroundDensity(tile, CLEAR_ROCKS, 3); - do { - if (--j == 0) goto get_out; - tile_new = tile + TileOffsByDiagDir(GB(Random(), 0, 2)); - } while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT)); - tile = tile_new; - } -get_out:; - } - } while (--i); -} - -static void ClickTile_Clear(TileIndex tile) -{ - /* not used */ -} - -static uint32 GetTileTrackStatus_Clear(TileIndex tile, TransportType mode) -{ - return 0; -} - -static const StringID _clear_land_str[] = { - STR_080D_GRASS, - STR_080B_ROUGH_LAND, - STR_080A_ROCKS, - STR_080E_FIELDS, - STR_080F_SNOW_COVERED_LAND, - STR_0810_DESERT -}; - -static void GetTileDesc_Clear(TileIndex tile, TileDesc *td) -{ - if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) { - td->str = STR_080C_BARE_LAND; - } else { - td->str = _clear_land_str[GetClearGround(tile)]; - } - td->owner = GetTileOwner(tile); -} - -static void ChangeTileOwner_Clear(TileIndex tile, PlayerID old_player, PlayerID new_player) -{ - return; -} - -void InitializeClearLand(void) -{ - _opt.snow_line = _patches.snow_line_height * TILE_HEIGHT; -} - -const TileTypeProcs _tile_type_clear_procs = { - DrawTile_Clear, /* draw_tile_proc */ - GetSlopeZ_Clear, /* get_slope_z_proc */ - ClearTile_Clear, /* clear_tile_proc */ - GetAcceptedCargo_Clear, /* get_accepted_cargo_proc */ - GetTileDesc_Clear, /* get_tile_desc_proc */ - GetTileTrackStatus_Clear, /* get_tile_track_status_proc */ - ClickTile_Clear, /* click_tile_proc */ - AnimateTile_Clear, /* animate_tile_proc */ - TileLoop_Clear, /* tile_loop_clear */ - ChangeTileOwner_Clear, /* change_tile_owner_clear */ - NULL, /* get_produced_cargo_proc */ - NULL, /* vehicle_enter_tile_proc */ - GetSlopeTileh_Clear, /* get_slope_tileh_proc */ -}; diff --git a/clear_map.h b/clear_map.h deleted file mode 100644 --- a/clear_map.h +++ /dev/null @@ -1,145 +0,0 @@ -/* $Id$ */ - -#ifndef CLEAR_MAP_H -#define CLEAR_MAP_H - -#include "macros.h" -#include "tile.h" - -/* ground type, m5 bits 2...4 - * valid densities (bits 0...1) in comments after the enum - */ -typedef enum ClearGround { - CLEAR_GRASS = 0, // 0-3 - CLEAR_ROUGH = 1, // 3 - CLEAR_ROCKS = 2, // 3 - CLEAR_FIELDS = 3, // 3 - CLEAR_SNOW = 4, // 0-3 - CLEAR_DESERT = 5 // 1,3 -} ClearGround; - - -static inline ClearGround GetClearGround(TileIndex t) -{ - assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 2, 3); -} - -static inline bool IsClearGround(TileIndex t, ClearGround ct) -{ - return GetClearGround(t) == ct; -} - - -static inline uint GetClearDensity(TileIndex t) -{ - assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 0, 2); -} - -static inline void AddClearDensity(TileIndex t, int d) -{ - assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += d; -} - - -static inline uint GetClearCounter(TileIndex t) -{ - assert(IsTileType(t, MP_CLEAR)); - return GB(_m[t].m5, 5, 3); -} - -static inline void AddClearCounter(TileIndex t, int c) -{ - assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 += c << 5; -} - -static inline void SetClearCounter(TileIndex t, uint c) -{ - assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - SB(_m[t].m5, 5, 3, c); -} - - -/* Sets type and density in one go, also sets the counter to 0 */ -static inline void SetClearGroundDensity(TileIndex t, ClearGround type, uint density) -{ - assert(IsTileType(t, MP_CLEAR)); // XXX incomplete - _m[t].m5 = 0 << 5 | type << 2 | density; -} - - -static inline uint GetFieldType(TileIndex t) -{ - assert(GetClearGround(t) == CLEAR_FIELDS); - return GB(_m[t].m3, 0, 4); -} - -static inline void SetFieldType(TileIndex t, uint f) -{ - assert(GetClearGround(t) == CLEAR_FIELDS); // XXX incomplete - SB(_m[t].m3, 0, 4, f); -} - -static inline uint16 GetIndustryIndexOfField(TileIndex t) -{ - assert(GetClearGround(t) == CLEAR_FIELDS); - return _m[t].m2; -} - -static inline void SetIndustryIndexOfField(TileIndex t, uint16 i) -{ - assert(GetClearGround(t) == CLEAR_FIELDS); - _m[t].m2 = i; -} - -/* Is used by tree tiles, too */ -static inline uint GetFenceSE(TileIndex t) -{ - assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); - return GB(_m[t].m4, 2, 3); -} - -static inline void SetFenceSE(TileIndex t, uint h) -{ - assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m4, 2, 3, h); -} - -static inline uint GetFenceSW(TileIndex t) -{ - assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); - return GB(_m[t].m4, 5, 3); -} - -static inline void SetFenceSW(TileIndex t, uint h) -{ - assert(IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)); // XXX incomplete - SB(_m[t].m4, 5, 3, h); -} - - -static inline void MakeClear(TileIndex t, ClearGround g, uint density) -{ - SetTileType(t, MP_CLEAR); - SetTileOwner(t, OWNER_NONE); - _m[t].m2 = 0; - _m[t].m3 = 0; - _m[t].m4 = 0 << 5 | 0 << 2; - SetClearGroundDensity(t, g, density); -} - - -static inline void MakeField(TileIndex t, uint field_type, uint16 industry) -{ - SetTileType(t, MP_CLEAR); - SetTileOwner(t, OWNER_NONE); - _m[t].m2 = industry; - _m[t].m3 = field_type; - _m[t].m4 = 0 << 5 | 0 << 2; - SetClearGroundDensity(t, CLEAR_FIELDS, 3); -} - -#endif /* CLEAR_MAP_H */ diff --git a/command.c b/command.c deleted file mode 100644 --- a/command.c +++ /dev/null @@ -1,562 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "table/strings.h" -#include "functions.h" -#include "map.h" -#include "gui.h" -#include "command.h" -#include "player.h" -#include "network/network.h" -#include "variables.h" -#include "genworld.h" - -const char* _cmd_text = NULL; - -#define DEF_COMMAND(yyyy) int32 yyyy(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) - -DEF_COMMAND(CmdBuildRailroadTrack); -DEF_COMMAND(CmdRemoveRailroadTrack); -DEF_COMMAND(CmdBuildSingleRail); -DEF_COMMAND(CmdRemoveSingleRail); - -DEF_COMMAND(CmdLandscapeClear); - -DEF_COMMAND(CmdBuildBridge); - -DEF_COMMAND(CmdBuildRailroadStation); -DEF_COMMAND(CmdRemoveFromRailroadStation); -DEF_COMMAND(CmdConvertRail); - -DEF_COMMAND(CmdBuildSingleSignal); -DEF_COMMAND(CmdRemoveSingleSignal); - -DEF_COMMAND(CmdTerraformLand); - -DEF_COMMAND(CmdPurchaseLandArea); -DEF_COMMAND(CmdSellLandArea); - -DEF_COMMAND(CmdBuildTunnel); - -DEF_COMMAND(CmdBuildTrainDepot); -DEF_COMMAND(CmdBuildTrainWaypoint); -DEF_COMMAND(CmdRenameWaypoint); -DEF_COMMAND(CmdRemoveTrainWaypoint); - -DEF_COMMAND(CmdBuildRoadStop); - -DEF_COMMAND(CmdBuildLongRoad); -DEF_COMMAND(CmdRemoveLongRoad); -DEF_COMMAND(CmdBuildRoad); -DEF_COMMAND(CmdRemoveRoad); - -DEF_COMMAND(CmdBuildRoadDepot); - -DEF_COMMAND(CmdBuildAirport); - -DEF_COMMAND(CmdBuildDock); - -DEF_COMMAND(CmdBuildShipDepot); - -DEF_COMMAND(CmdBuildBuoy); - -DEF_COMMAND(CmdPlantTree); - -DEF_COMMAND(CmdBuildRailVehicle); -DEF_COMMAND(CmdMoveRailVehicle); - -DEF_COMMAND(CmdStartStopTrain); - -DEF_COMMAND(CmdSellRailWagon); - -DEF_COMMAND(CmdSendTrainToDepot); -DEF_COMMAND(CmdForceTrainProceed); -DEF_COMMAND(CmdReverseTrainDirection); - -DEF_COMMAND(CmdModifyOrder); -DEF_COMMAND(CmdSkipOrder); -DEF_COMMAND(CmdDeleteOrder); -DEF_COMMAND(CmdInsertOrder); -DEF_COMMAND(CmdChangeServiceInt); -DEF_COMMAND(CmdRestoreOrderIndex); - -DEF_COMMAND(CmdBuildIndustry); - -DEF_COMMAND(CmdBuildCompanyHQ); -DEF_COMMAND(CmdSetPlayerFace); -DEF_COMMAND(CmdSetPlayerColor); - -DEF_COMMAND(CmdIncreaseLoan); -DEF_COMMAND(CmdDecreaseLoan); - -DEF_COMMAND(CmdWantEnginePreview); - -DEF_COMMAND(CmdNameVehicle); -DEF_COMMAND(CmdRenameEngine); - -DEF_COMMAND(CmdChangeCompanyName); -DEF_COMMAND(CmdChangePresidentName); - -DEF_COMMAND(CmdRenameStation); - -DEF_COMMAND(CmdSellAircraft); -DEF_COMMAND(CmdStartStopAircraft); -DEF_COMMAND(CmdBuildAircraft); -DEF_COMMAND(CmdSendAircraftToHangar); -DEF_COMMAND(CmdRefitAircraft); - -DEF_COMMAND(CmdPlaceSign); -DEF_COMMAND(CmdRenameSign); - -DEF_COMMAND(CmdBuildRoadVeh); -DEF_COMMAND(CmdStartStopRoadVeh); -DEF_COMMAND(CmdSellRoadVeh); -DEF_COMMAND(CmdSendRoadVehToDepot); -DEF_COMMAND(CmdTurnRoadVeh); -DEF_COMMAND(CmdRefitRoadVeh); - -DEF_COMMAND(CmdPause); - -DEF_COMMAND(CmdBuyShareInCompany); -DEF_COMMAND(CmdSellShareInCompany); -DEF_COMMAND(CmdBuyCompany); - -DEF_COMMAND(CmdBuildTown); - -DEF_COMMAND(CmdRenameTown); -DEF_COMMAND(CmdDoTownAction); - -DEF_COMMAND(CmdSetRoadDriveSide); - -DEF_COMMAND(CmdChangeDifficultyLevel); -DEF_COMMAND(CmdChangePatchSetting); - -DEF_COMMAND(CmdStartStopShip); -DEF_COMMAND(CmdSellShip); -DEF_COMMAND(CmdBuildShip); -DEF_COMMAND(CmdSendShipToDepot); -DEF_COMMAND(CmdRefitShip); - -DEF_COMMAND(CmdOrderRefit); -DEF_COMMAND(CmdCloneOrder); - -DEF_COMMAND(CmdClearArea); - -DEF_COMMAND(CmdGiveMoney); -DEF_COMMAND(CmdMoneyCheat); -DEF_COMMAND(CmdBuildCanal); -DEF_COMMAND(CmdBuildLock); - -DEF_COMMAND(CmdPlayerCtrl); - -DEF_COMMAND(CmdLevelLand); - -DEF_COMMAND(CmdRefitRailVehicle); - -DEF_COMMAND(CmdBuildSignalTrack); -DEF_COMMAND(CmdRemoveSignalTrack); - -DEF_COMMAND(CmdSetAutoReplace); - -DEF_COMMAND(CmdCloneVehicle); -DEF_COMMAND(CmdMassStartStopVehicle); -DEF_COMMAND(CmdDepotSellAllVehicles); -DEF_COMMAND(CmdDepotMassAutoReplace); - -/* The master command table */ -static const Command _command_proc_table[] = { - {CmdBuildRailroadTrack, 0}, /* 0 */ - {CmdRemoveRailroadTrack, 0}, /* 1 */ - {CmdBuildSingleRail, 0}, /* 2 */ - {CmdRemoveSingleRail, 0}, /* 3 */ - {CmdLandscapeClear, 0}, /* 4 */ - {CmdBuildBridge, 0}, /* 5 */ - {CmdBuildRailroadStation, 0}, /* 6 */ - {CmdBuildTrainDepot, 0}, /* 7 */ - {CmdBuildSingleSignal, 0}, /* 8 */ - {CmdRemoveSingleSignal, 0}, /* 9 */ - {CmdTerraformLand, 0}, /* 10 */ - {CmdPurchaseLandArea, 0}, /* 11 */ - {CmdSellLandArea, 0}, /* 12 */ - {CmdBuildTunnel, 0}, /* 13 */ - {CmdRemoveFromRailroadStation, 0}, /* 14 */ - {CmdConvertRail, 0}, /* 15 */ - {CmdBuildTrainWaypoint, 0}, /* 16 */ - {CmdRenameWaypoint, 0}, /* 17 */ - {CmdRemoveTrainWaypoint, 0}, /* 18 */ - {NULL, 0}, /* 19 */ - {NULL, 0}, /* 20 */ - {CmdBuildRoadStop, 0}, /* 21 */ - {NULL, 0}, /* 22 */ - {CmdBuildLongRoad, 0}, /* 23 */ - {CmdRemoveLongRoad, 0}, /* 24 */ - {CmdBuildRoad, 0}, /* 25 */ - {CmdRemoveRoad, 0}, /* 26 */ - {CmdBuildRoadDepot, 0}, /* 27 */ - {NULL, 0}, /* 28 */ - {CmdBuildAirport, 0}, /* 29 */ - {CmdBuildDock, 0}, /* 30 */ - {CmdBuildShipDepot, 0}, /* 31 */ - {CmdBuildBuoy, 0}, /* 32 */ - {CmdPlantTree, 0}, /* 33 */ - {CmdBuildRailVehicle, 0}, /* 34 */ - {CmdMoveRailVehicle, 0}, /* 35 */ - {CmdStartStopTrain, 0}, /* 36 */ - {NULL, 0}, /* 37 */ - {CmdSellRailWagon, 0}, /* 38 */ - {CmdSendTrainToDepot, 0}, /* 39 */ - {CmdForceTrainProceed, 0}, /* 40 */ - {CmdReverseTrainDirection, 0}, /* 41 */ - - {CmdModifyOrder, 0}, /* 42 */ - {CmdSkipOrder, 0}, /* 43 */ - {CmdDeleteOrder, 0}, /* 44 */ - {CmdInsertOrder, 0}, /* 45 */ - - {CmdChangeServiceInt, 0}, /* 46 */ - - {CmdBuildIndustry, 0}, /* 47 */ - {CmdBuildCompanyHQ, 0}, /* 48 */ - {CmdSetPlayerFace, 0}, /* 49 */ - {CmdSetPlayerColor, 0}, /* 50 */ - - {CmdIncreaseLoan, 0}, /* 51 */ - {CmdDecreaseLoan, 0}, /* 52 */ - - {CmdWantEnginePreview, 0}, /* 53 */ - - {CmdNameVehicle, 0}, /* 54 */ - {CmdRenameEngine, 0}, /* 55 */ - - {CmdChangeCompanyName, 0}, /* 56 */ - {CmdChangePresidentName, 0}, /* 57 */ - - {CmdRenameStation, 0}, /* 58 */ - - {CmdSellAircraft, 0}, /* 59 */ - {CmdStartStopAircraft, 0}, /* 60 */ - - {CmdBuildAircraft, 0}, /* 61 */ - {CmdSendAircraftToHangar, 0}, /* 62 */ - {NULL, 0}, /* 63 */ - {CmdRefitAircraft, 0}, /* 64 */ - - {CmdPlaceSign, 0}, /* 65 */ - {CmdRenameSign, 0}, /* 66 */ - - {CmdBuildRoadVeh, 0}, /* 67 */ - {CmdStartStopRoadVeh, 0}, /* 68 */ - {CmdSellRoadVeh, 0}, /* 69 */ - {CmdSendRoadVehToDepot, 0}, /* 70 */ - {CmdTurnRoadVeh, 0}, /* 71 */ - {CmdRefitRoadVeh, 0}, /* 72 */ - - {CmdPause, CMD_SERVER}, /* 73 */ - - {CmdBuyShareInCompany, 0}, /* 74 */ - {CmdSellShareInCompany, 0}, /* 75 */ - {CmdBuyCompany, 0}, /* 76 */ - - {CmdBuildTown, CMD_OFFLINE}, /* 77 */ - {NULL, 0}, /* 78 */ - {NULL, 0}, /* 79 */ - {CmdRenameTown, CMD_SERVER}, /* 80 */ - {CmdDoTownAction, 0}, /* 81 */ - - {CmdSetRoadDriveSide, CMD_SERVER}, /* 82 */ - {NULL, 0}, /* 83 */ - {NULL, 0}, /* 84 */ - {CmdChangeDifficultyLevel, CMD_SERVER}, /* 85 */ - - {CmdStartStopShip, 0}, /* 86 */ - {CmdSellShip, 0}, /* 87 */ - {CmdBuildShip, 0}, /* 88 */ - {CmdSendShipToDepot, 0}, /* 89 */ - {NULL, 0}, /* 90 */ - {CmdRefitShip, 0}, /* 91 */ - - {NULL, 0}, /* 92 */ - {NULL, 0}, /* 93 */ - {NULL, 0}, /* 94 */ - {NULL, 0}, /* 95 */ - {NULL, 0}, /* 96 */ - {NULL, 0}, /* 97 */ - - {CmdOrderRefit, 0}, /* 98 */ - {CmdCloneOrder, 0}, /* 99 */ - - {CmdClearArea, 0}, /* 100 */ - {NULL, 0}, /* 101 */ - - {CmdMoneyCheat, CMD_OFFLINE}, /* 102 */ - {CmdBuildCanal, 0}, /* 103 */ - {CmdPlayerCtrl, 0}, /* 104 */ - - {CmdLevelLand, 0}, /* 105 */ - - {CmdRefitRailVehicle, 0}, /* 106 */ - {CmdRestoreOrderIndex, 0}, /* 107 */ - {CmdBuildLock, 0}, /* 108 */ - {NULL, 0}, /* 109 */ - {CmdBuildSignalTrack, 0}, /* 110 */ - {CmdRemoveSignalTrack, 0}, /* 111 */ - {NULL, 0}, /* 112 */ - {CmdGiveMoney, 0}, /* 113 */ - {CmdChangePatchSetting, CMD_SERVER}, /* 114 */ - {CmdSetAutoReplace, 0}, /* 115 */ - {CmdCloneVehicle, 0}, /* 116 */ - {CmdMassStartStopVehicle, 0}, /* 117 */ - {CmdDepotSellAllVehicles, 0}, /* 118 */ - {CmdDepotMassAutoReplace, 0}, /* 119 */ -}; - -/* This function range-checks a cmd, and checks if the cmd is not NULL */ -bool IsValidCommand(uint cmd) -{ - cmd &= 0xFF; - - return - cmd < lengthof(_command_proc_table) && - _command_proc_table[cmd].proc != NULL; -} - -byte GetCommandFlags(uint cmd) -{ - return _command_proc_table[cmd & 0xFF].flags; -} - - -static int _docommand_recursive; - -int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc) -{ - int32 res; - CommandProc *proc; - - /* Do not even think about executing out-of-bounds tile-commands */ - if (tile >= MapSize()) { - _cmd_text = NULL; - return CMD_ERROR; - } - - proc = _command_proc_table[procc].proc; - - if (_docommand_recursive == 0) _error_message = INVALID_STRING_ID; - - _docommand_recursive++; - - // only execute the test call if it's toplevel, or we're not execing. - if (_docommand_recursive == 1 || !(flags & DC_EXEC) || (flags & DC_FORCETEST) ) { - res = proc(tile, flags & ~DC_EXEC, p1, p2); - if (CmdFailed(res)) { - if (res & 0xFFFF) _error_message = res & 0xFFFF; - goto error; - } - - if (_docommand_recursive == 1 && - !(flags & DC_QUERY_COST) && - res != 0 && - !CheckPlayerHasMoney(res)) { - goto error; - } - - if (!(flags & DC_EXEC)) { - _docommand_recursive--; - _cmd_text = NULL; - return res; - } - } - - /* Execute the command here. All cost-relevant functions set the expenses type - * themselves with "SET_EXPENSES_TYPE(...);" at the beginning of the function */ - res = proc(tile, flags, p1, p2); - if (CmdFailed(res)) { - if (res & 0xFFFF) _error_message = res & 0xFFFF; -error: - _docommand_recursive--; - _cmd_text = NULL; - return CMD_ERROR; - } - - // if toplevel, subtract the money. - if (--_docommand_recursive == 0) { - SubtractMoneyFromPlayer(res); - // XXX - Old AI hack which doesn't use DoCommandDP; update last build coord of player - if (tile != 0 && IsValidPlayer(_current_player)) { - GetPlayer(_current_player)->last_build_coordinate = tile; - } - } - - _cmd_text = NULL; - return res; -} - -int32 GetAvailableMoneyForCommand(void) -{ - PlayerID pid = _current_player; - if (!IsValidPlayer(pid)) return 0x7FFFFFFF; // max int - return GetPlayer(pid)->player_money; -} - -// toplevel network safe docommand function for the current player. must not be called recursively. -// the callback is called when the command succeeded or failed. -bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd) -{ - int32 res = 0,res2; - CommandProc *proc; - uint32 flags; - bool notest; - StringID error_part1; - - int x = TileX(tile) * TILE_SIZE; - int y = TileY(tile) * TILE_SIZE; - - /* Do not even think about executing out-of-bounds tile-commands */ - if (tile >= MapSize()) { - _cmd_text = NULL; - return false; - } - - assert(_docommand_recursive == 0); - - _error_message = INVALID_STRING_ID; - error_part1 = GB(cmd, 16, 16); - _additional_cash_required = 0; - - /** Spectator has no rights except for the (dedicated) server which - * is/can be a spectator but as the server it can do anything */ - if (_current_player == PLAYER_SPECTATOR && !_network_server) { - ShowErrorMessage(_error_message, error_part1, x, y); - _cmd_text = NULL; - return false; - } - - flags = 0; - if (cmd & CMD_AUTO) flags |= DC_AUTO; - if (cmd & CMD_NO_WATER) flags |= DC_NO_WATER; - - // get pointer to command handler - assert((cmd & 0xFF) < lengthof(_command_proc_table)); - proc = _command_proc_table[cmd & 0xFF].proc; - if (proc == NULL) { - _cmd_text = NULL; - return false; - } - - // Some commands have a different output in dryrun than the realrun - // e.g.: if you demolish a whole town, the dryrun would say okay. - // but by really destroying, your rating drops and at a certain point - // it will fail. so res and res2 are different - // CMD_REMOVE_ROAD: This command has special local authority - // restrictions which may cause the test run to fail (the previous - // road fragments still stay there and the town won't let you - // disconnect the road system), but the exec will succeed and this - // fact will trigger an assertion failure. --pasky - notest = - (cmd & 0xFF) == CMD_CLEAR_AREA || - (cmd & 0xFF) == CMD_CONVERT_RAIL || - (cmd & 0xFF) == CMD_LEVEL_LAND || - (cmd & 0xFF) == CMD_REMOVE_ROAD || - (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD; - - _docommand_recursive = 1; - - // cost estimation only? - 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)) { - if (res & 0xFFFF) _error_message = res & 0xFFFF; - ShowErrorMessage(_error_message, error_part1, x, y); - } else { - ShowEstimatedCostOrIncome(res, x, y); - } - - _docommand_recursive = 0; - _cmd_text = NULL; - return false; - } - - - if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { - // first test if the command can be executed. - res = proc(tile, flags, p1, p2); - if (CmdFailed(res)) { - if (res & 0xFFFF) _error_message = res & 0xFFFF; - goto show_error; - } - // no money? Only check if notest is off - if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error; - } - -#ifdef ENABLE_NETWORK - /** If we are in network, and the command is not from the network - * send it to the command-queue and abort execution - * If we are a dedicated server temporarily switch local player, otherwise - * the other parties won't be able to execute our command and will desync. - * We also need to do this if the server's company has gone bankrupt - * @todo Rewrite (dedicated) server to something more than a dirty hack! - */ - if (_networking && !(cmd & CMD_NETWORK_COMMAND)) { - PlayerID pbck = _local_player; - if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = 0; - NetworkSend_Command(tile, p1, p2, cmd, callback); - if (_network_dedicated || (_network_server && pbck == PLAYER_SPECTATOR)) _local_player = pbck; - _docommand_recursive = 0; - _cmd_text = NULL; - return true; - } -#endif /* ENABLE_NETWORK */ - - // update last build coordinate of player. - if (tile != 0 && IsValidPlayer(_current_player)) { - GetPlayer(_current_player)->last_build_coordinate = tile; - } - - /* Actually try and execute the command. If no cost-type is given - * use the construction one */ - _yearly_expenses_type = EXPENSES_CONSTRUCTION; - res2 = proc(tile, flags | DC_EXEC, p1, p2); - - // If notest is on, it means the result of the test can be different than - // the real command.. so ignore the test - if (!notest && !((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) { - assert(res == res2); // sanity check - } else { - if (CmdFailed(res2)) { - if (res2 & 0xFFFF) _error_message = res2 & 0xFFFF; - goto show_error; - } - } - - SubtractMoneyFromPlayer(res2); - - if (IsLocalPlayer() && _game_mode != GM_EDITOR) { - if (res2 != 0) ShowCostOrIncomeAnimation(x, y, GetSlopeZ(x, y), res2); - if (_additional_cash_required) { - SetDParam(0, _additional_cash_required); - ShowErrorMessage(STR_0003_NOT_ENOUGH_CASH_REQUIRES, error_part1, x,y); - if (res2 == 0) goto callb_err; - } - } - - _docommand_recursive = 0; - - if (callback) callback(true, tile, p1, p2); - _cmd_text = NULL; - return true; - -show_error: - // show error message if the command fails? - if (IsLocalPlayer() && error_part1 != 0) { - ShowErrorMessage(_error_message, error_part1, x,y); - } - -callb_err: - _docommand_recursive = 0; - - if (callback) callback(false, tile, p1, p2); - _cmd_text = NULL; - return false; -} diff --git a/command.h b/command.h deleted file mode 100644 --- a/command.h +++ /dev/null @@ -1,213 +0,0 @@ -/* $Id$ */ - -#ifndef COMMAND_H -#define COMMAND_H - -enum { - CMD_BUILD_RAILROAD_TRACK = 0, - CMD_REMOVE_RAILROAD_TRACK = 1, - CMD_BUILD_SINGLE_RAIL = 2, - CMD_REMOVE_SINGLE_RAIL = 3, - CMD_LANDSCAPE_CLEAR = 4, - CMD_BUILD_BRIDGE = 5, - CMD_BUILD_RAILROAD_STATION = 6, - CMD_BUILD_TRAIN_DEPOT = 7, - CMD_BUILD_SIGNALS = 8, - CMD_REMOVE_SIGNALS = 9, - CMD_TERRAFORM_LAND = 10, - CMD_PURCHASE_LAND_AREA = 11, - CMD_SELL_LAND_AREA = 12, - CMD_BUILD_TUNNEL = 13, - - CMD_REMOVE_FROM_RAILROAD_STATION = 14, - CMD_CONVERT_RAIL = 15, - - CMD_BUILD_TRAIN_WAYPOINT = 16, - CMD_RENAME_WAYPOINT = 17, - CMD_REMOVE_TRAIN_WAYPOINT = 18, - - CMD_BUILD_ROAD_STOP = 21, - CMD_BUILD_LONG_ROAD = 23, - CMD_REMOVE_LONG_ROAD = 24, - CMD_BUILD_ROAD = 25, - CMD_REMOVE_ROAD = 26, - CMD_BUILD_ROAD_DEPOT = 27, - - CMD_BUILD_AIRPORT = 29, - - CMD_BUILD_DOCK = 30, - - CMD_BUILD_SHIP_DEPOT = 31, - CMD_BUILD_BUOY = 32, - - CMD_PLANT_TREE = 33, - - CMD_BUILD_RAIL_VEHICLE = 34, - CMD_MOVE_RAIL_VEHICLE = 35, - - CMD_START_STOP_TRAIN = 36, - - CMD_SELL_RAIL_WAGON = 38, - - CMD_SEND_TRAIN_TO_DEPOT = 39, - CMD_FORCE_TRAIN_PROCEED = 40, - CMD_REVERSE_TRAIN_DIRECTION = 41, - - CMD_MODIFY_ORDER = 42, - CMD_SKIP_ORDER = 43, - CMD_DELETE_ORDER = 44, - CMD_INSERT_ORDER = 45, - - CMD_CHANGE_SERVICE_INT = 46, - - CMD_BUILD_INDUSTRY = 47, - - CMD_BUILD_COMPANY_HQ = 48, - CMD_SET_PLAYER_FACE = 49, - CMD_SET_PLAYER_COLOR = 50, - - CMD_INCREASE_LOAN = 51, - CMD_DECREASE_LOAN = 52, - - CMD_WANT_ENGINE_PREVIEW = 53, - - CMD_NAME_VEHICLE = 54, - CMD_RENAME_ENGINE = 55, - CMD_CHANGE_COMPANY_NAME = 56, - CMD_CHANGE_PRESIDENT_NAME = 57, - CMD_RENAME_STATION = 58, - - CMD_SELL_AIRCRAFT = 59, - CMD_START_STOP_AIRCRAFT = 60, - CMD_BUILD_AIRCRAFT = 61, - CMD_SEND_AIRCRAFT_TO_HANGAR = 62, - CMD_REFIT_AIRCRAFT = 64, - - CMD_PLACE_SIGN = 65, - CMD_RENAME_SIGN = 66, - - CMD_BUILD_ROAD_VEH = 67, - CMD_START_STOP_ROADVEH = 68, - CMD_SELL_ROAD_VEH = 69, - CMD_SEND_ROADVEH_TO_DEPOT = 70, - CMD_TURN_ROADVEH = 71, - CMD_REFIT_ROAD_VEH = 72, - - CMD_PAUSE = 73, - - CMD_BUY_SHARE_IN_COMPANY = 74, - CMD_SELL_SHARE_IN_COMPANY = 75, - CMD_BUY_COMPANY = 76, - - CMD_BUILD_TOWN = 77, - - CMD_RENAME_TOWN = 80, - CMD_DO_TOWN_ACTION = 81, - - CMD_SET_ROAD_DRIVE_SIDE = 82, - - CMD_CHANGE_DIFFICULTY_LEVEL = 85, - - CMD_START_STOP_SHIP = 86, - CMD_SELL_SHIP = 87, - CMD_BUILD_SHIP = 88, - CMD_SEND_SHIP_TO_DEPOT = 89, - CMD_REFIT_SHIP = 91, - - CMD_ORDER_REFIT = 98, - CMD_CLONE_ORDER = 99, - CMD_CLEAR_AREA = 100, - - CMD_MONEY_CHEAT = 102, - CMD_BUILD_CANAL = 103, - - CMD_PLAYER_CTRL = 104, // used in multiplayer to create a new player etc. - CMD_LEVEL_LAND = 105, // level land - - CMD_REFIT_RAIL_VEHICLE = 106, - CMD_RESTORE_ORDER_INDEX = 107, - CMD_BUILD_LOCK = 108, - - CMD_BUILD_SIGNAL_TRACK = 110, - CMD_REMOVE_SIGNAL_TRACK = 111, - - CMD_GIVE_MONEY = 113, - CMD_CHANGE_PATCH_SETTING = 114, - - CMD_SET_AUTOREPLACE = 115, - - CMD_CLONE_VEHICLE = 116, - CMD_MASS_START_STOP = 117, - CMD_DEPOT_SELL_ALL_VEHICLES = 118, - CMD_DEPOT_MASS_AUTOREPLACE = 119, -}; - -enum { - DC_EXEC = 0x01, - DC_AUTO = 0x02, // don't allow building on structures - DC_QUERY_COST = 0x04, // query cost only, don't build. - DC_NO_WATER = 0x08, // don't allow building on water - DC_NO_RAIL_OVERLAP = 0x10, // don't allow overlap of rails (used in buildrail) - DC_AI_BUILDING = 0x20, // special building rules for AI - DC_NO_TOWN_RATING = 0x40, // town rating does not disallow you from building - DC_FORCETEST = 0x80, // force test too. - - CMD_ERROR = ((int32)0x80000000), -}; - -#define CMD_MSG(x) ((x)<<16) - -enum { - CMD_AUTO = 0x0200, - CMD_NO_WATER = 0x0400, - CMD_NETWORK_COMMAND = 0x0800, // execute the command without sending it on the network - CMD_NO_TEST_IF_IN_NETWORK = 0x1000, // When enabled, the command will bypass the no-DC_EXEC round if in network - CMD_SHOW_NO_ERROR = 0x2000, -}; - -/** Command flags for the command table - * @see _command_proc_table - */ -enum { - CMD_SERVER = 0x1, /// the command can only be initiated by the server - CMD_OFFLINE = 0x2, /// the command cannot be executed in a multiplayer game; single-player only -}; - -typedef int32 CommandProc(TileIndex tile, uint32 flags, uint32 p1, uint32 p2); - -typedef struct Command { - CommandProc *proc; - byte flags; -} Command; - -//#define return_cmd_error(errcode) do { _error_message=(errcode); return CMD_ERROR; } while(0) -#define return_cmd_error(errcode) do { return CMD_ERROR | (errcode); } while (0) - -/** - * Check the return value of a DoCommand*() function - * @param res the resulting value from the command to be checked - * @return Return true if the command failed, false otherwise - */ -static inline bool CmdFailed(int32 res) -{ - // lower 16bits are the StringID of the possible error - return res <= (CMD_ERROR | INVALID_STRING_ID); -} - -/* command.c */ -typedef void CommandCallback(bool success, TileIndex tile, uint32 p1, uint32 p2); -int32 DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc); -bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd); - -#ifdef ENABLE_NETWORK - -void NetworkSend_Command(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback); -#endif /* ENABLE_NETWORK */ - -extern const char* _cmd_text; // Text, which gets sent with a command - -bool IsValidCommand(uint cmd); -byte GetCommandFlags(uint cmd); -int32 GetAvailableMoneyForCommand(void); - -#endif /* COMMAND_H */ diff --git a/config.lib b/config.lib new file mode 100644 --- /dev/null +++ b/config.lib @@ -0,0 +1,2212 @@ + +log() { + if [ $1 = "1" ]; then echo "$2"; fi + echo "$2" >> $config_log +} + +set_default() { + ignore_extra_parameters="0" + # We set all kinds of defaults for params. Later on the user can override + # most of them; but if they don't, this default is used. + build="" + host="" + cc_build="" + cc_host="" + cxx_host="" + windres="" + strip="" + lipo="" + os="DETECT" + endian="AUTO" + revision="" + config_log="config.log" + prefix_dir="/usr/local" + binary_dir="games" + data_dir="share/games/openttd" + icon_dir="share/pixmaps" + personal_dir="" + custom_lang_dir="" + second_data_dir="" + install_dir="/" + enable_install="0" + enable_debug="0" + enable_profiling="0" + enable_dedicated="0" + enable_network="1" + enable_static="1" + enable_translator="0" + enable_assert="1" + enable_strip="1" + enable_universal="1" + enable_osx_g5="0" + with_osx_sysroot="1" + with_application_bundle="1" + with_sdl="1" + with_cocoa="1" + with_zlib="1" + with_png="1" + with_makedepend="1" + with_direct_music="1" + with_sort="1" + with_iconv="1" + with_midi="" + with_midi_arg="" + with_freetpye="1" + with_fontconfig="1" + + save_params_array="build host cc_build cc_host cxx_host windres strip lipo os revision endian config_log prefix_dir binary_dir data_dir icon_dir personal_dir install_dir custom_lang_dir second_data_dir enable_install enable_debug enable_profiling enable_dedicated enable_network enable_static enable_translator enable_assert enable_strip with_osx_sysroot enable_universal enable_osx_g5 with_application_bundle with_sdl with_cocoa with_zlib with_png with_makedepend with_direct_music with_sort with_iconv with_midi with_midi_arg with_freetype with_fontconfig CC CXX CFLAGS LDFLAGS" +} + +detect_params() { + # Walk over all params from the user and override any default settings if + # needed. This also handles any invalid option. + for p in "$@" + do + if [ -n "$prev_p" ] + then + eval "$prev_p=\$p" + prev_p= + continue + fi + + optarg=`expr "x$p" : 'x[^=]*=\(.*\)'` + + case "$p" in + --help | -h) + showhelp + exit 0 + ;; + + --config-log) + prev_p="config_log" + ;; + --config-log=*) + config_log="$optarg" + ;; + + --build) + prev_p="build" + ;; + --build=*) + build="$optarg" + ;; + + --host) + prev_p="host" + ;; + --host=*) + host="$optarg" + ;; + + --os) + prev_p="os" + ;; + --os=*) + os="$optarg" + ;; + + --revision=*) + revision="$optarg" + ;; + + --cc-build) + prevp_p="cc_build" + ;; + --cc-build=*) + cc_build="$optarg" + ;; + --cc-host) + prevp_p="cc_host" + ;; + --cc-host=*) + cc_host="$optarg" + ;; + --cxx-host) + prevp_p="cxx_host" + ;; + --cxx-host=*) + cxx_host="$optarg" + ;; + --windres) + prevp_p="windres" + ;; + --windres=*) + windres="$optarg" + ;; + --strip) + prevp_p="strip" + ;; + --strip=*) + strip="$optarg" + ;; + --lipo) + prevp_p="lipo" + ;; + --lipo=*) + lipo="$optarg" + ;; + + --endian) + prev_p="endian" + ;; + --endian=*) + endian="$optarg" + ;; + + + + --prefix-dir) + prevp_p="prefix-dir" + ;; + --prefix-dir=*) + prefix_dir="$optarg" + ;; + + --binary-dir) + prevp_p="binary-dir" + ;; + --binary-dir=*) + binary_dir="$optarg" + ;; + + --data-dir) + prevp_p="data-dir" + ;; + --data-dir=*) + data_dir="$optarg" + ;; + + --icon-dir) + prevp_p="icon-dir" + ;; + --icon-dir=*) + icon_dir="$optarg" + ;; + + --personal-dir) + prevp_p="personal-dir" + ;; + --personal-dir=*) + personal_dir="$optarg" + ;; + + --install-dir) + prevp_p="install-dir" + ;; + --install-dir=*) + install_dir="$optarg" + ;; + +# TODO: The next few cases will be removed when the search path patch is applied + --custom-lang-dir) + prevp_p="custom-lang-dir" + ;; + --custom-lang-dir=*) + custom_lang_dir="$optarg" + ;; + + --second-data-dir) + prevp_p="second-data-dir" + ;; + --second-data-dir=*) + second_data_dir="$optarg" + ;; + + --enable-install) + enable_install="1" + ;; + --enable-install=*) + enable_install="$optarg" + ;; +# TODO: End of to be removed cases + + + --enable-debug) + enable_debug="1" + ;; + --enable-debug=*) + enable_debug="$optarg" + ;; + --enable-profiling) + enable_profiling="1" + ;; + --enable-profiling=*) + enable_profiling="$optarg" + ;; + --enable-dedicated) + enable_dedicated="1" + ;; + --enable-dedicated=*) + enable_dedicated="$optarg" + ;; + --enable-network=*) + enable_network="$optarg" + ;; + --disable-network) + enable_network="0" + ;; + --disable-static) + enable_static="0" + ;; + --enable-static) + enable_static="2" + ;; + --enable-static=*) + enable_static="$optarg" + ;; + --disable-translator) + enable_translator="0" + ;; + --enable-translator) + enable_translator="2" + ;; + --enable-translator=*) + enable_translator="$optarg" + ;; + --disable-assert) + enable_assert="0" + ;; + --enable-assert) + enable_assert="2" + ;; + --enable-assert=*) + enable_assert="$optarg" + ;; + --disable-strip) + enable_strip="0" + ;; + --enable-strip) + enable_strip="2" + ;; + --enable-strip=*) + enable_strip="$optarg" + ;; + --disable-universal) + enable_universal="0" + ;; + --enable-universal) + enable_universal="2" + ;; + --enable-universal=*) + enable_universal="$optarg" + ;; + --disable-osx-g5) + enable_osx_g5="0" + ;; + --enable-osx-g5) + enable_osx_g5="2" + ;; + --enable-osx-g5=*) + enable_osx_g5="$optarg" + ;; + + --with-sdl) + with_sdl="2" + ;; + --without-sdl) + with_sdl="0" + ;; + --with-sdl=*) + with_sdl="$optarg" + ;; + + --with-cocoa) + with_cocoa="2" + ;; + --without-cocoa) + with_cocoa="0" + ;; + --with-cocoa=*) + with_cocoa="$optarg" + ;; + + --with-zlib) + with_zlib="2" + ;; + --without-zlib) + with_zlib="0" + ;; + --with-zlib=*) + with_zlib="$optarg" + ;; + + --with-png) + with_png="2" + ;; + --without-png) + with_png="0" + ;; + --with-png=*) + with_png="$optarg" + ;; + --with-libpng) + with_png="2" + ;; + --without-libpng) + with_png="0" + ;; + --with-libpng=*) + with_png="$optarg" + ;; + + --with-freetype) + with_freetype="2" + ;; + --without-freetype) + with_freetype="0" + ;; + --with-freetype=*) + with_freetype="$optarg" + ;; + --with-libfreetype) + with_freetype="2" + ;; + --without-libfreetype) + with_freetype="0" + ;; + --with-libfreetype=*) + with_freetype="$optarg" + ;; + + --with-fontconfig) + with_fontconfig="2" + ;; + --without-fontconfig) + with_fontconfig="0" + ;; + --with-fontconfig=*) + with_fontconfig="$optarg" + ;; + --with-libfontconfig) + with_fontconfig="2" + ;; + --without-libfontconfig) + with_fontconfig="0" + ;; + --with-libfontconfig=*) + with_fontconfig="$optarg" + ;; + + --with-makedepend) + with_makedepend="2" + ;; + --without-makedepend) + with_makedepend="0" + ;; + --with-makedepend=*) + with_makedepend="$optarg" + ;; + + --with-direct-music) + with_direct_music="2" + ;; + --without-direct-music) + with_direct_music="0" + ;; + --with-direct-music=*) + with_direct_music="$optarg" + ;; + + --with-sort) + with_sort="2" + ;; + --without-sort) + with_sort="0" + ;; + --with-sort=*) + with_sort="$optarg" + ;; + + --with-iconv) + with_iconv="2" + ;; + --without-iconv) + with_iconv="0" + ;; + --with-iconv=*) + with_iconv="$optarg" + ;; + + --with-midi=*) + with_midi="$optarg" + ;; + --with-midi-arg=*) + with_midi_arg="$optarg" + ;; + + --without-osx-sysroot) + with_osx_sysroot="0" + ;; + --with-osx-sysroot) + with_osx_sysroot="2" + ;; + --with-osx-sysroot=*) + with_osx_sysroot="$optarg" + ;; + + --without-application-bundle) + with_applicant_bundle="0" + ;; + --with-application-bundle) + with_applicant_bundle="1" + ;; + --with-application-bundle=*) + with_applicant_bundle="$optarg" + ;; + + CC=* | --CC=*) + CC="$optarg" + ;; + CXX=* | --CXX=*) + CXX="$optarg" + ;; + CFLAGS=* | --CFLAGS=*) + CFLAGS="$optarg" + ;; + LDFLAGS=* | --LDFLAGS=*) + LDFLAGS="$optarg" + ;; + + --ignore-extra-parameters) + ignore_extra_parameters="1" + ;; + + --*) + if [ "$ignore_extra_parameters" = "0" ] + then + echo "Unknown option $p" + exit 1 + else + echo "Unknown option $p ignored" + fi + ;; + esac + done + + if [ -n "$prev_p" ] + then + echo "configure: error: missing argument to --$prev_p" + exit 1 + fi + + # Clean the logfile + echo "" > $config_log +} + +save_params() { + # Here we save all params, so we can later on do an exact redo of this + # configuration, without having the user to re-input stuff + + echo "Running configure with following options:" >> $config_log + echo "" >> $config_log + + configure="$0 --ignore-extra-parameters" + for p in $save_params_array + do + eval "v=\$$p" + p=`echo "$p" | sed 's/_/-/g;s/\n//g;'` + # Only save those params that aren't empty + configure="$configure --$p=$v" + done + + echo "$configure" >> $config_log + echo "$configure" > config.cache + echo "" >> $config_log +} + +check_params() { + # Some params want to be in full uppercase, else they might not work as + # expected.. fix that here + + endian=`echo $endian | tr [:lower:] [:upper:]` + os=`echo $os | tr [:lower:] [:upper:]` + + # Check if all params have valid values + + # Endian only allows AUTO, LE and, BE + if ! echo $endian | grep -q "^AUTO$\|^LE$\|^BE$" + then + echo "configure: error: invalid option --endian=$endian" + echo " Available options are: --endian=[AUTO|LE|BE]" + exit 1 + fi + # OS only allows DETECT, UNIX, OSX, FREEBSD, MORPHOS, BEOS, SUNOS, CYGWIN, and MINGW, OS2 + if ! echo $os | grep -q "^DETECT$\|^UNIX$\|^OSX$\|^FREEBSD$\|^MORPHOS$\|^BEOS$\|^SUNOS$\|^CYGWIN$\|^MINGW$\|^OS2" + then + echo "configure: error: invalid option --os=$os" + echo " Available options are: --os=[DETECT|UNIX|OSX|FREEBSD|MORPHOS|BEOS|SUNOS|CYGWIN|MINGW|OS2]" + exit 1 + fi + # enable_debug should be between 0 and 4 + if ! echo $enable_debug | grep -q "^0$\|^1$\|^2$\|^3$" + then + echo "configure: error: invalid option --enable-debug=$enable_debug" + echo " Available options are: --enable-debug[=0123]" + exit 1 + fi + + check_build + check_host + + detect_os + +# We might enable universal builds always on OSX targets.. but currently we don't +# if [ "$enable_universal" = "1" ] && [ "$os" != "OSX" ] + if [ "$enable_universal" = "1" ] + then + enable_universal="0" + fi + if [ "$enable_universal" = "2" ] && [ "$os" != "OSX" ] + then + log 1 "configure: error: --enable-universal only works on OSX" + exit 1 + fi + if [ "$enable_universal" = "0" ] + then + log 1 "checking universal build... no" + else + log 1 "checking universal build... yes" + fi + + # Already detected by check_build + log 1 "checking for build gcc... $cc_build" + log 1 "checking for host gcc... $cc_host" + + check_cxx + check_windres + check_strip + check_lipo + check_makedepend + + if [ "$enable_static" = "1" ] + then + if [ "$os" = "MINGW" ] || [ "$os" = "CYGWIN" ] || [ "$os" = "MORPHOS" ] || [ "$os" = "OSX" ] + then + enable_static="2" + else + enable_static="0" + fi + fi + + if [ "$enable_static" != "0" ] + then + log 1 "checking for static... yes" + + if [ "$os" != "MINGW" ] && [ "$os" != "CYGWIN" ] && [ "$os" != "OSX" ] && [ "$os" != "MORPHOS" ] + then + log 1 "WARNING: static is only known to work on Windows, MacOSX and MorphOS" + log 1 "WARNING: use static at your own risk on this platform" + + sleep 5 + fi + else + log 1 "checking for static... no" + fi + + # Show what we configured + if [ "$enable_debug" = "0" ] + then + log 1 "using debug level... no" + elif [ "$enable_profiling" != "0" ] + then + log 1 "using debug level... profiling (debug level $enable_debug)" + else + log 1 "using debug level... level $enable_debug" + fi + + detect_sdl + detect_cocoa + + if [ "$enable_dedicated" != "0" ] + then + log 1 "checking GDI video driver... skipping" + log 1 "checking dedicated... found" + + if [ "$enable_network" != "0" ] + then + log 1 "WARNING: compiling a dedicated server without network is pointless" + sleep 5 + fi + else + if [ "$os" = "MINGW" ] || [ "$os" = "CYGWIN" ] + then + log 1 "checking GDI video driver... found" + else + log 1 "checking GDI video driver... not Windows, skipping" + fi + + if [ -z "$sdl_config" ] && [ "$with_cocoa" = 0 ] && [ "$os" != "MINGW" ] && [ "$os" != "CYGWIN" ] + then + log 1 "WARNING: no video driver found, building dedicated only" + enable_dedicated="1" + sleep 1 + + log 1 "checking dedicated... found" + else + log 1 "checking dedicated... not selected" + fi + fi + + if [ "$enable_network" != "0" ] + then + log 1 "checking network... found" + else + log 1 "checking network... disabled" + fi + + if [ "$enable_translator" != "0" ] + then + log 1 "checking translator... debug" + strgen_flags="" + else + log 1 "checking translator... no" + # -t shows TODO items, normally they are muted + strgen_flags="-t" + fi + + if [ "$enable_assert" != "0" ] + then + log 1 "checking assert... enabled" + else + log 1 "checking assert... disabled" + fi + + detect_zlib + detect_png + detect_freetype + detect_fontconfig + detect_iconv + + if [ "$with_direct_music" = "1" ] || [ "$with_direct_music" = "2" ] + then + if [ "$os" != "MINGW" ] && [ "$os" != "CYGWIN" ] + then + if [ "$with_direct_music" = "2" ] + then + log 1 "configure: error: direct-music is only supported on Win32 targets" + exit 1 + fi + with_direct_music="0" + + log 1 "checking direct-music... not Windows, skipping" + else + check_direct_music + fi + fi + + detect_sort + + if [ "$os" = "OSX" ] && [ "$endian" = "AUTO" ] + then + endian="PREPROCESSOR" + fi + + log 1 "checking endianess... $endian" + + # Suppress language errors when there is a version defined, indicating a release + # It just isn't pretty if any release produces warnings in the languages. + if [ -f "$ROOT_DIR/version" ] + then + lang_suppress="yes" + log 1 "suppress language errors... yes" + else + lang_suppress="" + log 1 "suppress language errors... no" + fi + + if [ "$enable_debug" = "0" ] && [ "$enable_profiling" = "0" ] && [ "$enable_strip" != "0" ] + then + if [ "$os" = "MORPHOS" ] + then + strip_arg="--strip-all --strip-unneeded --remove-section .comment" + elif [ "$os" = "OSX" ] + then + strip_arg="" + else + strip_arg="-s" + fi + + log 1 "checking stripping... $strip $strip_arg" + else + strip="" + log 1 "checking stripping... skipped" + fi + + if [ "$os" != "OSX" ] && [ "$with_osx_sysroot" != "0" ] + then + if [ "$with_osx_sysroot" = "1" ] + then + with_osx_sysroot="0" + + log 1 "checking OSX sysroot... not OSX, skipping" + else + log 1 "configure: error: --with-osx-sysroot only works if OSX is the target" + exit 1 + fi + fi + + if [ "$with_osx_sysroot" != "0" ] + then + if [ "$enable_universal" = "0" ] && [ "$with_osx_sysroot" != "1" ] && [ "$with_osx_sysroot" != "2" ] + then + log 1 "checking OSX sysroot... $with_osx_sysroot" + else + # If autodetect and no universal, use system default + if [ "$with_osx_sysroot" = "1" ] && [ "$enable_universal" = "0" ] + then + log 1 "checking OSX sysroot... no (use system default)" + with_osx_sysroot="0" + else + log 1 "checking OSX sysroot... automaticly" + with_osx_sysroot="3" + fi + fi + else + if [ "$os" = "OSX" ] + then + log 1 "checking OSX sysroot... no (use system default)" + fi + fi + + if [ "$os" != "OSX" ] && [ "$with_application_bundle" != "0" ] + then + if [ "$with_application_bundle" = "1" ] + then + with_application_bundle="0" + + log 1 "checking OSX application bundle... not OSX, skipping" + else + log 1 "configure: error: --with-application-bundle only works if OSX is the target" + exit 1 + fi + fi + + if [ "$os" = "OSX" ] && [ "$with_application_bundle" = "1" ] + then + OSXAPP="OpenTTD.app" + +# TODO: remove next few lines of code when the search path patch has been applied + if [ -n "$custom_lang_dir" ] && [ "$custom_lang_dir" != "$(OSXAPP)/Contents/Lang/" ] + then + log 1 "configure: error: --custom-lang-dir and --with-application-bundle are not compatible + exit 1 + fi + + if [ -n "$custom_lang_dir" ] && [ "$second_data_dir" != "$(OSXAPP)/Contents/Data/" ] + then + log 1 "configure: error: --second-data-dir and --with-application-bundle are not compatible + exit 1 + fi + + custom_lang_dir="${OSXAPP}/Contents/Lang/" + second_data_dir="${OSXAPP}/Contents/Data/" +# TODO: remove till here + else + OSXAPP="" + fi + + if [ "$os" = "OSX" ] + then + # Test on G5 + + if [ "$enable_osx_g5" != "0" ] + then + log 1 "detecting G5... yes (forced)" + else + # First, are we a real OSX system, else we can't detect it + native=`LC_ALL=C uname | tr [:upper:] [:lower:] | grep darwin` + # If $host doesn't match $build , we are cross-compiling + if [ -n "$native" ] && [ "$build" != "$host" ] + then + $cc_build $SRC_DIR/os/macosx/G5_detector.c -o G5_detector + res=`./G5_detector` + rm -f G5_detector + if [ -n "$res" ] + then + # This is G5, add flags for it + enable_osx_g5="2" + + log 1 "detecting G5... yes" + else + enable_osx_g5="0" + + log 1 "detecting G5... no" + fi + else + enable_osx_g5="0" + + log 1 "detecting G5... no (cross-compiling)" + fi + fi + else + if [ "$enable_osx_g5" != "0" ] + then + log 1 "configure: error: OSX G5 selected, but not compiling for OSX" + log 1 "configure: error: either select OSX as OS, or deselect OSX G5" + + exit 1 + fi + fi +} + +make_cflags_and_ldflags() { + # General CFlags for BUILD + CFLAGS_BUILD="" + # General CFlags for HOST + CFLAGS="$CFLAGS -D$os -DWITH_REV" + # CFlags for HOST and C-Compiler + CC_FLAGS="" + # Libs to compile. In fact this is just LDFLAGS + LIBS="-lstdc++" + # LDFLAGS used for HOST + LDFLAGS="$LDFLAGS" + + # Each debug level reduces the optimalization by a bit + if [ $enable_debug -ge 1 ] + then + CFLAGS="$CFLAGS -g -D_DEBUG" + OBJS_SUBDIR="debug" + else + OBJS_SUBDIR="release" + fi + if [ $enable_debug -ge 2 ] + then + CFLAGS="$CFLAGS -fno-inline" + fi + if [ $enable_debug -ge 3 ] + then + CFLAGS="$CFLAGS -O0" + fi + if [ $enable_debug = 0 ] + then + # No debug, add default stuff + if [ "$os" = "OSX" ] + then + # these compilerflags makes the app run as fast as possible without making the app unstable. It works on G3 or newer + CFLAGS="$CFLAGS -O3 -funroll-loops -fsched-interblock -falign-loops=16 -falign-jumps=16 -falign-functions=16 -falign-jumps-max-skip=15 -falign-loops-max-skip=15 -mdynamic-no-pic" + else + if [ "$os" = "MORPHOS" ] + then + CFLAGS="$CFLAGS -I/gg/os-include -noixemul -fstrict-aliasing -fexpensive-optimizations" + CFLAGS="$CFLAGS -mcpu=604 -fno-inline -mstring -mmultiple" + fi + + CFLAGS="$CFLAGS -O2 -fomit-frame-pointer" + fi + + if [ "$enable_profiling" != "0" ] + then + CFLAGS="$CFLAGS -pg" + LDFLAGS="$LDFLAGS -pg" + fi + else + if [ "$enable_profiling" != "0" ] + then + CFLAGS="$CFLAGS -p" + LDFLAGS="$LDFLAGS -pg" + fi + fi + + # Enable some things only for certain GCC versions + cc_version=`$cc_host -dumpversion | cut -c 1,3` + + if [ $cc_version -ge 29 ] + then + CFLAGS="$CFLAGS -O -Wall -Wno-multichar -Wsign-compare -Wundef" + CFLAGS="$CFLAGS -Wwrite-strings -Wpointer-arith" + + CC_CFLAGS="$CC_CFLAGS -Wstrict-prototypes" + fi + + if [ $cc_version -ge 30 ] + then + CFLAGS="$CFLAGS -W -Wno-unused-parameter" + fi + + if [ $cc_version -ge 34 ] + then + CC_CFLAGS="$CC_CFLAGS -Wdeclaration-after-statement -Wold-style-definition" + fi + + if [ "$os" = "CYGWIN" ] + then + CFLAGS="$CFLAGS -mwin32" + LDFLAGS="$LDFLAGS -mwin32" + fi + if [ "$os" = "MINGW" ] + then + CFLAGS="$CFLAGS -mno-cygwin" + LDFLAGS="$LDFLAGS -mno-cygwin" + fi + + if [ "$os" = "CYGWIN" ] || [ "$os" = "MINGW" ] + then + LDFLAGS="$LDFLAGS -Wl,--subsystem,windows" + LIBS="$LIBS -lws2_32 -lwinmm -lgdi32 -ldxguid -lole32" + fi + + if [ "$os" != "CYGWIN" ] && [ "$os" != "MINGW" ] && [ "$os" != "MORPHOS" ] && [ "$os" != "OSX" ] + then + LIBS="$LIBS -lpthread" + LIBS="$LIBS -lrt" + fi + + if [ "$os" != "MINGW" ] + then + LIBS="$LIBS -lc" + fi + + if [ "$os" = "MORPHOS" ] + then + # -Wstrict-prototypes generates much noise because of system headers + CFLAGS="$CFLAGS -Wno-strict-prototypes" + fi + + if [ "$os" = "OSX" ] + then + LDFLAGS="$LDFLAGS -framework Cocoa" + if [ "$enable_dedicated" = "0" ] + then + LIBS="$LIBS -framework QuickTime" + fi + fi + + if [ "$os" = "BEOS" ] + then + LIBS="$LIBS -lmidi -lbe" + fi + + # Most targets act like UNIX, just with some additions + if [ "$os" = "BEOS" ] || [ "$os" = "OSX" ] || [ "$os" = "MORPHOS" ] || [ "$os" = "FREEBSD" ] || [ "$os" = "SUNOS" ] || [ "$os" = "OS2" ] + then + CFLAGS="$CFLAGS -DUNIX" + fi + # And others like Windows + if [ "$os" = "MINGW" ] || [ "$os" = "CYGWIN" ] + then + CFLAGS="$CFLAGS -DWIN" + fi + + if [ -n "$sdl_config" ] + then + CFLAGS="$CFLAGS -DWITH_SDL" + CFLAGS="$CFLAGS `$sdl_config --cflags`" + if [ "$enable_static" != "0" ] + then + LIBS="$LIBS `$sdl_config --static-libs`" + else + LIBS="$LIBS `$sdl_config --libs`" + fi + fi + + if [ "$with_cocoa" != "0" ] + then + CFLAGS="$CFLAGS -DWITH_COCOA" + LIBS="$LIBS -F/System/Library/Frameworks -framework Cocoa -framework Carbon -framework AudioUnit" + fi + + if [ "$with_zlib" != "0" ] + then + if [ "$enable_static" != "0" ] && [ "$os" != "OSX" ] + then + LIBS="$LIBS $zlib" + else + LIBS="$LIBS -lz" + fi + CFLAGS="$CFLAGS -DWITH_ZLIB" + fi + + if [ -n "$png_config" ] + then + CFLAGS="$CFLAGS -DWITH_PNG" + CFLAGS="$CFLAGS `$png_config --cppflags --I_opts | tr '\n\r' ' '`" + + # The extra flags are unneeded for latest libpng-config, but some versions are so broken... + if [ "$enable_static" != "0" ] + then + if [ "$os" = "OSX" ] + then + LIBS="$LIBS `$png_config --prefix`/lib/libpng.a" + else + LIBS="$LIBS `$png_config --static --ldflags --libs --L_opts | tr '\n\r' ' '`" + fi + else + LIBS="$LIBS `$png_config --ldflags --libs --L_opts | tr '\n\r' ' '`" + fi + fi + + if [ -n "$freetype_config" ] + then + CFLAGS="$CFLAGS -DWITH_FREETYPE" + CFLAGS="$CFLAGS `$freetype_config --cflags | tr '\n\r' ' '`" + + if [ "$enable_static" != "0" ] + then + if [ "$os" = "OSX" ] + then + LIBS="$LIBS `$freetype_config --prefix`/lib/libfreetype.a" + else + # Is it possible to do static with freetype, if so: how? + LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" + fi + else + LIBS="$LIBS `$freetype_config --libs | tr '\n\r' ' '`" + fi + fi + + if [ -n "$fontconfig_config" ] + then + CFLAGS="$CFLAGS -DWITH_FONTCONFIG" + CFLAGS="$CFLAGS `$fontconfig_config --cflags | tr '\n\r' ' '`" + + if [ "$enable_static" != "0" ] + then + if [ "$os" = "OSX" ] + then + LIBS="$LIBS `$fontconfig_config --prefix`/lib/libfontconfig.a" + else + LIBS="$LIBS `$fontconfig_config --libs --static | tr '\n\r' ' '`" + fi + else + LIBS="$LIBS `$fontconfig_config --libs | tr '\n\r' ' '`" + fi + fi + + if [ "$with_direct_music" != "0" ] + then + CFLAGS="$CFLAGS -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT" + fi + + if [ "$with_iconv" != "0" ] + then + CFLAGS="$CFLAGS -DWITH_ICONV" + LIBS="$LIBS -liconv" + if [ "$with_iconv" != "2" ] + then + CFLAGS="$CFLAGS -I$with_iconv/include" + LIBS="$LIBS -L$with_iconv/lib" + fi + fi + + if [ -n "$with_midi" ] + then + CFLAGS="$CFLAGS -DEXTERNAL_PLAYER=\"$with_midi\"" + fi + if [ -n "$with_midi_arg" ] + then + CFLAGS="$CFLAGS -DMIDI_ARG=\"$with_midi_arg\"" + fi + + if [ "$enable_dedicated" != "0" ] + then + CFLAGS="$CFLAGS -DDEDICATED" + fi + + if [ "$enable_network" != "0" ] + then + CFLAGS="$CFLAGS -DENABLE_NETWORK" + + if [ "$os" = "BEOS" ] + then + LDFLAGS="$LDFLAGS -lbind -lsocket" + fi + + if [ "$os" = "SUNOS" ] + then + LDFLAGS="$LDFLAGS -lnsl -lsocket" + fi + fi + + if [ "$enable_static" != "0" ] + then + # OSX can't handle -static in LDFLAGS + if [ "$os" != "OSX" ] + then + LDFLAGS="$LDFLAGS -static" + fi + fi + + if [ "$enable_assert" = "0" ] + then + CFLAGS="$CFLAGS -DNDEBUG" + fi + + if [ "$enable_osx_g5" != "0" ] + then + CFLAGS="$CFLAGS -mtune=970 -mcpu=970 -mpowerpc-gpopt" + fi + + if [ "$with_osx_sysroot" != "0" ] && [ "$with_osx_sysroot" != "3" ] + then + CFLAGS="$CFLAGS -isysroot /Developer/SDKs/MacOSX$with_osx_sysroot.sdk" + LDFLAGS="$LDFLAGS -Wl,-syslibroot,/Developer/SDKs/MacOSX$with_osx_sysroot.sdk" + fi + +# TODO: remove next few lines of code when the search path patch has been applied + if [ -n "$second_data_dir" ] + then + CFLAGS="$CFLAGS -DSECOND_DATA_DIR=\\\\\"$second_data_dir\\\\\"" + fi + + if [ -n "$custom_lang_dir" ] + then + CFLAGS="$CFLAGS -DCUSTOM_LANG_DIR=\\\\\"$custom_lang_dir\\\\\"" + fi +# TODO: remove till here + + if [ "$enable_install" = "1" ] + then + if [ -n "$personal_dir" ] + then + CFLAGS="$CFLAGS -DUSE_HOMEDIR=1 -DPERSONAL_DIR=\\\\\"$personal_dir/\\\\\"" + fi + + if [ -n "$data_dir" ] + then + CFLAGS="$CFLAGS -DGAME_DATA_DIR=\\\\\"$prefix_dir/$data_dir/\\\\\"" + fi + + if [ -n "$icon_dir" ] + then + CFLAGS="$CFLAGS -DICON_DIR=\\\\\"$prefix_dir/$icon_dir/\\\\\"" + fi + fi + + if [ -n "$revision" ] + then + log 1 "checking revision... $revision" + log 1 "WARNING: we do not advise you to use this setting" + log 1 "WARNING: in most cases it is not safe for network use" + log 1 "WARNING: USE WITH CAUTION!" + + sleep 5 + elif [ -f "$ROOT_DIR/version" ] + then + revision="`cat $ROOT_DIR/version`" + + log 1 "checking revision... $revision" + else + revision="" + + log 1 "checking revision... svn detection" + fi + + log 1 "using CFLAGS... $CFLAGS $CC_CFLAGS" + log 1 "using LDFLAGS... $LIBS $LDFLAGS" + + # Makedepend doesn't like something like: -isysroot /OSX/blabla + # so convert it to: -isysroot -OSX/blabla. makedepend just ignores + # any - command it doesn't know, so we are pretty save. + # Lovely hackish, not? + # Btw, this almost always comes from outside the configure, so it is + # not something we can control. + if [ "$with_makedepend" != "0" ] + then + cflags_makedep="` echo "$CFLAGS" | sed 's# /# -#g'`" + else + makedepend="" + fi +} + +check_compiler() { + # Params: + # $1 - Type for message (build / host) + # $2 - What to fill with the found compiler + # $3 - System to try + # $4 - Compiler to try + # $5 - Env-setting to try + # $6 - GCC alike to try + # $7 - CC alike to try + # $8 - "0" gcc, "1" g++, "2" windres, "3" strip, "4" lipo + # $9 - What the command is to check for + + if [ -n "$3" ] + then + # Check for system + machine=`$3-$6 $9 2>/dev/null` + ret=$? + eval "$2=$3-$6" + + log 2 "executing $3-$6 $9" + log 2 " returned $machine" + log 2 " exit code $ret" + + if ( [ -z "$machine" ] && [ "$8" != "3" ] ) || [ "$ret" != "0" ] + then + log 1 "checking $1... $3-$6 not found" + log 1 "I couldn't detect any $6 binary for $3" + exit 1 + fi + + if [ "$machine" != "$3" ] && ( [ "$8" = "0" ] || [ "$8" = "1" ] ) + then + log 1 "checking $1... expected $3, found $machine" + log 1 "the compiler suggests it doesn't build code for the machine you specified" + exit 1 + fi + elif [ -n "$4" ] + then + # Check for manual compiler + machine=`$4 $9 2>/dev/null` + ret=$? + eval "$2=$4" + + log 2 "executing $4 $9" + log 2 " returned $machine" + log 2 " exit code $ret" + + if ( [ -z "$machine" ] && [ "$8" != "3" ] ) || [ "$ret" != "0" ] + then + log 1 "checking $1... $4 not found" + log 1 "the selected binary doesn't seem to be a $6 binary" + exit 1 + fi + else + # Nothing given, autodetect + + if [ -n "$5" ] + then + machine=`$5 $9 2>/dev/null` + ret=$? + eval "$2=$5" + + log 2 "executing $5 $9" + log 2 " returned $machine" + log 2 " exit code $ret" + + # The user defined a GCC that doesn't reply to $9.. abort + if ( [ -z "$machine" ] && [ "$8" != "3" ] ) || [ "$ret" != "0" ] + then + log 1 "checking $1... $5 unusable" + log 1 "the CC environment variable is set, but it doesn't seem to be a $6 binary" + log 1 "please redefine the CC/CXX environment to a $6 binary" + exit 1 + fi + else + log 2 "checking $1... CC/CXX not set (skipping)" + + # No $5, so try '$6' + machine=`$6 $9 2>/dev/null` + ret=$? + eval "$2=$6" + + log 2 "executing $6 $9" + log 2 " returned $machine" + log 2 " exit code $ret" + + if ( [ -z "$machine" ] && [ "$8" != "3" ] ) || [ "$ret" != "0" ] + then + # Maybe '$7'? + machine=`$7 $9 2>/dev/null` + ret=$? + eval "$2=$7" + + log 2 "executing $7 $9" + log 2 " returned $machine" + log 2 " exit code $ret" + + # All failed, abort + if [ -z "$machine" ] + then + log 1 "checking $1... $6 not found" + log 1 "I couldn't detect any $6 binary on your system" + log 1 "please define the CC/CXX environment to where it is located" + + exit 1 + fi + fi + fi + fi + + if [ "$8" != "0" ] + then + eval "res=\$$2" + log 1 "checking $1... $res" + else + log 1 "checking $1... $machine" + fi +} + +check_build() { + check_compiler "build system type" "cc_build" "$build" "$cc_build" "$CC" "gcc" "cc" "0" "-dumpmachine" +} + +check_host() { + # By default the host is the build + if [ -z "$host" ]; then host="$build"; fi + check_compiler "host system type" "cc_host" "$host" "$cc_host" "$CC" "gcc" "cc" "0" "-dumpmachine" +} + +check_cxx() { + check_compiler "host g++" "cxx_host" "$host" "$cxx_host" "$CXX" "g++" "c++" 1 "-dumpmachine" +} + +check_windres() { + if [ "$os" = "MINGW" ] || [ "$os" = "CYGWIN" ] + then + check_compiler "host windres" "windres" "$host" "$windres" "$WINDRES" "windres" "windres" "2" "-V" + fi +} + +check_strip() { + if [ "$os" = "OSX" ] + then + # Most targets have -V in strip, to see if they exists... OSX doesn't.. so execute something + echo "int main(int argc, char *argv[]) { }" > strip.test.c + $cc_host strip.test.c -o strip.test + check_compiler "host strip" "strip" "$host" "$strip" "$STRIP" "strip" "strip" "3" "strip.test" + rm -f strip.test.c strip.test + else + check_compiler "host strip" "strip" "$host" "$strip" "$STRIP" "strip" "strip" "3" "-V" + fi +} + +check_lipo() { + if [ "$os" = "OSX" ] && [ "$enable_universal" != "0" ] + then + echo "int main(int argc, char *argv[]) { }" > lipo.test.c + $cc_host lipo.test.c -o lipo.test + check_compiler "host lipo" "lipo" "$host" "$lipo" "$LIPO" "lipo" "lipo" "4" "-info lipo.test" + rm -f lipo.test.c lipo.test + fi +} + +check_direct_music() { + echo " + #include + #include + #include + #include + #include + int main(int argc, char *argv[]) { }" > direct_music.test.c + $cxx_host $CFLAGS direct_music.test.c -o direct_music.test 2> /dev/null + res=$? + rm -f direct_music.test.c direct_music.test + + if [ "$res" != "0" ] + then + if [ "$with_direct_music" = "2" ] + then + log 1 "configure: error: direct-music is not available on this system" + exit 1 + fi + with_direct_music="0" + + log 1 "checking direct-music... not found" + else + log 1 "checking direct-music... found" + fi +} + +check_makedepend() { + if [ "$with_makedepend" = "0" ] + then + log 1 "checking makedepend... disabled" + return + fi + + if [ "$with_makedepend" = "1" ] || [ "$with_makedepend" = "2" ] + then + makedepend="makedepend" + else + makedepend="$with_makedepend" + fi + + rm -f makedepend.tmp + touch makedepend.tmp + res=`$makedepend -fmakedepend.tmp 2>/dev/null` + res=$? + log 2 "executing $makedepend -f makedepend.tmp" + log 2 " returned `cat makedepend.tmp`" + log 2 " exit code $ret" + + if [ -z "`cat makedepend.tmp`" ] + then + rm -f makedepend.tmp makedepend.tmp.bak + + if [ "$with_makedepend" = "2" ] + then + log 1 "checking makedepend... not found" + + log 1 "I couldn't detect any makedepend on your system" + log 1 "please locate it via --makedepend=[binary]" + + exit 1 + elif [ "$with_makedepend" != "1" ] + then + log 1 "checking makedepend... $makedepend not found" + + log 1 "the selected file doesn't seem to be a valid makedepend binary" + + exit 1 + else + log 1 "checking makedepend... not found" + + with_makedepend="0" + return + fi + fi + + rm -f makedepend.tmp makedepend.tmp.bak + + log 1 "checking makedepend... $makedepend" +} + +detect_os() { + if [ $os = "DETECT" ] + then + # Detect UNIX, OSX, FREEBSD, MORPHOS, BEOS, SUNOS, CYGWIN, MINGW and OS2 + + # Try first via dumpmachine, then via uname + os=`echo "$host" | tr [:upper:] [:lower:] | awk ' + /linux/ { print "UNIX"; exit} + /darwin/ { print "OSX"; exit} + /freebsd/ { print "FREEBSD"; exit} + /morphos/ { print "MORPHOS"; exit} + /beos/ { print "BEOS"; exit} + /sunos/ { print "SUNOS"; exit} + /cygwin/ { print "CYGWIN"; exit} + /mingw/ { print "MINGW"; exit} + /os\/2/ { print "OS2"; exit} + '` + + if [ -z "$os" ] + then + os=`LC_ALL=C uname | tr [:upper:] [:lower:] | awk ' + /linux/ { print "UNIX"; exit} + /darwin/ { print "OSX"; exit} + /freebsd/ { print "FREEBSD"; exit} + /morphos/ { print "MORPHOS"; exit} + /beos/ { print "BEOS"; exit} + /sunos/ { print "SUNOS"; exit} + /cygwin/ { print "CYGWIN"; exit} + /mingw/ { print "MINGW"; exit} + /os\/2/ { print "OS2"; exit} + '` + fi + + if [ -z "$os" ] + then + log 1 "detecting OS... none detected" + log 1 "I couldn't detect your OS. Please use --with-os=OS to force one" + log 1 "Allowed values are: UNIX, OSX, FREEBSD, MORPHOS, BEOS, SUNOS, CYGWIN, and MINGW" + exit 1 + fi + + log 1 "detecting OS... $os" + else + log 1 "forcing OS... $os" + fi +} + +detect_sdl() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_sdl" = "0" ] + then + log 1 "checking SDL... disabled" + + sdl_config="" + return 0 + fi + + if [ "$with_sdl" = "2" ] && [ "$with_cocoa" = "2" ] + then + log 1 "configure: error: it is impossible to compile both SDL and COCOA" + log 1 "configure: error: please deselect one of them and try again" + exit 1 + fi + + if [ "$with_sdl" = "2" ] && [ "$enable_dedicated" != "0" ] + then + log 1 "configure: error: it is impossible to compile a dedicated with SDL" + log 1 "configure: error: please deselect one of them and try again" + exit 1 + fi + + if [ "$enable_dedicated" != "0" ] + then + log 1 "checking SDL... skipping" + + sdl_config="" + return 0 + fi + + # By default on OSX we don't use SDL. The rest is auto-detect + if [ "$with_sdl" = "1" ] && [ "$os" = "OSX" ] && [ "$with_cocoa" != "0" ] + then + log 1 "checking SDL... OSX, skipping" + + sdl_config="" + return 0 + fi + + if [ "$with_sdl" = "1" ] || [ "$with_sdl" = "" ] || [ "$with_sdl" = "2" ] + then + if [ "$os" = "FREEBSD" ] + then + sdl_config="sdl11-config" + else + sdl_config="sdl-config" + fi + else + sdl_config="$with_sdl" + fi + + version=`$sdl_config --version 2>/dev/null` + ret=$? + log 2 "executing $sdl_config --version" + log 2 " returned $version" + log 2 " exit code $ret" + + if [ -z $version ] || [ "$ret" != "0" ] + then + log 1 "checking SDL... not found" + + # It was forced, so it should be found. + if [ "$with_sdl" != "1" ] + then + log 1 "configure: error: sdl-config couldn't be found" + log 1 "configure: error: you supplied '$with_sdl', but it seems invalid" + exit 1 + fi + + sdl_config="" + return 0 + fi + + log 1 "checking SDL... found" +} + +detect_cocoa() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_cocoa" = "0" ] + then + log 1 "checking COCOA... disabled" + + return 0 + fi + + if [ "$with_cocoa" = "2" ] && [ "$enable_dedicated" != "0" ] + then + log 1 "configure: error: it is impossible to compile a dedicated with COCOA" + log 1 "configure: error: please deselect one of them and try again" + exit 1 + fi + + if [ "$enable_dedicated" != "0" ] + then + log 1 "checking COCOA... skipping" + + with_cocoa="0" + return 0 + fi + + # By default on OSX we use COCOA. The rest doesn't support it + if [ "$with_cocoa" = "1" ] && [ "$os" != "OSX" ] + then + log 1 "checking COCOA... not OSX, skipping" + + with_cocoa="0" + return 0 + fi + + if [ "$os" != "OSX" ] + then + log 1 "checking COCOA... not OSX" + + log 1 "configure: error: COCOA video driver is only supported for OSX" + exit 1 + fi + + log 1 "checking COCOA... found" +} + +detect_zlib() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_zlib" = "0" ] + then + log 1 "checking zlib... disabled" + + zlib="" + return 0 + fi + + log 2 "detecting zlib" + + if [ "$with_zlib" = "1" ] || [ "$with_zlib" = "" ] || [ "$with_zlib" = "2" ] + then + zlib=`ls -1 /usr/include/*.h 2>/dev/null | grep "\/zlib.h$"` + if [ -z "$zlib" ] + then + log 2 " trying /usr/include/zlib.h... no" + zlib=`ls -1 /usr/local/include/*.h 2>/dev/null | grep "\/zlib.h$"` + fi + if [ -z "$zlib" ] + then + log 2 " trying /usr/local/include/zlib.h... no" + fi + + if [ -n "$zlib" ] && [ "$enable_static" != "0" ] && [ "$os" != "OSX" ] + then + log 2 " trying $zlib... found" + # Now find the static lib, if needed + zlib=`ls /lib/*.a 2>/dev/null | grep "\/libz.a$"` + if [ -z "$zlib" ] + then + log 2 " trying /lib/libz.a... no" + zlib=`ls /usr/lib/*.a 2>/dev/null | grep "\/libz.a$"` + fi + if [ -z "$zlib" ] + then + log 2 " trying /usr/lib/libz.a... no" + zlib=`ls /usr/local/lib/*.a 2>/dev/null | grep "\/libz.a$"` + fi + if [ -z "$zlib" ] + then + log 2 " trying /usr/local/lib/libz.a... no" + log 1 "configure: error: zlib couldn't be found" + log 1 "configure: error: you requested a static link, but I can't find zlib.a" + + exit 1 + fi + fi + else + # Make sure it exists + zlib=`ls $with_zlib 2>/dev/null` + fi + + if [ -z "$zlib" ] + then + log 1 "checking zlib... not found" + if [ "$with_zlib" = "2" ] + then + log 1 "configure: error: zlib couldn't be found" + + exit 1 + elif [ "$with_zlib" != "1" ] + then + log 1 "configure: error: zlib couldn't be found" + log 1 "configure: error: you supplied '$with_zlib', but it seems invalid" + + exit 1 + fi + + return 0 + fi + + log 2 " trying $zlib... found" + + log 1 "checking zlib... found" +} + +detect_png() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_png" = "0" ] + then + log 1 "checking libpng... disabled" + + png_config="" + return 0 + fi + + if [ "$with_zlib" = "0" ] || [ "$zlib" = "" ] + then + log 1 "configure: error: libpng depends on zlib, which couldn't be found / was disabled" + log 1 "configure: error: please supply --with-zlib, with a valid zlib location" + exit 1 + fi + + if [ "$with_png" = "1" ] || [ "$with_png" = "" ] || [ "$with_png" = "2" ] + then + png_config="libpng-config" + else + png_config="$with_png" + fi + + version=`$png_config --version 2>/dev/null` + ret=$? + log 2 "executing $png_config --version" + log 2 " returned $version" + log 2 " exit code $ret" + + if [ -z "$version" ] || [ "$ret" != "0" ] + then + log 1 "checking libpng... not found" + + # It was forced, so it should be found. + if [ "$with_png" != "1" ] + then + log 1 "configure: error: libpng-config couldn't be found" + log 1 "configure: error: you supplied '$with_png', but it seems invalid" + exit 1 + fi + + png_config="" + return 0 + fi + + log 1 "checking libpng... found" +} + +detect_freetype() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_freetype" = "0" ] + then + log 1 "checking libfreetype... disabled" + + freetype_config="" + return 0 + fi + + if [ "$with_zlib" = "0" ] || [ "$zlib" = "" ] + then + log 1 "configure: error: libfreetype depends on zlib, which couldn't be found / was disabled" + log 1 "configure: error: please supply --with-zlib, with a valid zlib location" + exit 1 + fi + + if [ "$with_freetype" = "1" ] || [ "$with_freetype" = "" ] || [ "$with_freetype" = "2" ] + then + freetype_config="freetype-config" + else + freetype_config="$with_freetype" + fi + + version=`$freetype_config --version 2>/dev/null` + ret=$? + log 2 "executing freetype_config --version" + log 2 " returned $version" + log 2 " exit code $ret" + + if [ -z "$version" ] || [ "$ret" != "0" ] + then + log 1 "checking libfreetype... not found" + + # It was forced, so it should be found. + if [ "$with_freetype" != "1" ] + then + log 1 "configure: error: freetype-config couldn't be found" + log 1 "configure: error: you supplied '$with_freetype', but it seems invalid" + exit 1 + fi + + freetype_config="" + return 0 + fi + + log 1 "checking libfreetype... found" +} + +detect_fontconfig() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_fontconfig" = "0" ] + then + log 1 "checking libfontconfig... disabled" + + fontconfig_config="" + return 0 + fi + + if [ "$with_fontconfig" = "1" ] || [ "$with_fontconfig" = "" ] || [ "$with_fontconfig" = "2" ] + then + fontconfig_config="pkg-config fontconfig" + else + fontconfig_config="$with_fontconfig" + fi + + version=`$fontconfig_config --modversion 2>/dev/null` + ret=$? + shortversion=`echo $version | cut -c 1,3` + log 2 "executing $fontconfig_config --modversion" + log 2 " returned $version" + log 2 " exit code $ret" + + if [ -z "$version" ] || [ "$ret" != "0" ] || [ "$shortversion" -le "22" ] + then + if [ -n "$shortversion" ] && [ "$shortversion" -le "22" ] + then + log 1 "checking libfontconfig... needs at least version 2.3.0, fontconfig NOT enabled" + else + log 1 "checking libfontconfig... not found" + fi + + # It was forced, so it should be found. + if [ "$with_fontconfig" != "1" ] + then + log 1 "configure: error: fontconfig-config couldn't be found" + log 1 "configure: error: you supplied '$with_fontconfig', but it seems invalid" + exit 1 + fi + + fontconfig_config="" + return 0 + fi + + log 1 "checking libfontconfig... found" +} + +detect_iconv() { + # 0 means no, 1 is auto-detect, 2 is force + if [ "$with_iconv" = "0" ] + then + log 1 "checking iconv... disabled" + + return 0 + fi + + if [ "$with_iconv" = "1" ] && [ "$os" != "OSX" ] + then + log 1 "checking iconv... not OSX, skipping" + with_iconv="0" + + return 0 + fi + + # Try to find iconv.h, seems to only thing to detect iconv with + + if [ "$with_iconv" = "1" ] || [ "$with_iconv" = "" ] || [ "$with_iconv" = "2" ] + then + iconv=`ls -1 /usr/include 2>/dev/null | grep "iconv.h"` + if [ -z "$iconv" ] + then + iconv=`ls -1 /usr/local/include 2>/dev/null | grep "iconv.h"` + fi + else + # Make sure it exists + iconv=`ls $with_iconv/include/iconv.h 2>/dev/null` + fi + + if [ -z "$iconv" ] + then + log 1 "checking iconv... not found" + if [ "$with_iconv" = "2" ] + then + log 1 "configure: error: iconv couldn't be found" + + exit 1 + elif [ "$with_iconv" != "1" ] + then + log 1 "configure: error: iconv couldn't be found" + log 1 "configure: error: you supplied '$with_iconv', but I couldn't detect iconv in it" + + exit 1 + fi + + return 0 + fi + + if [ "$with_iconv" = "1" ] + then + with_iconv="2" + fi + + log 2 "found iconv in $iconv" + + log 1 "checking iconv... found" +} + +_detect_sort() { + sort_test_in="d +a +c +b" + + sort_test_out="a +b +c +d" + + log 2 "running echo | $1" + + if [ "`echo \"$sort_test_in\" | $1 2>/dev/null`" = "$sort_test_out" ] + then + sort="$1" + log 2 " result was valid" + else + log 2 " result was invalid" + fi +} + +detect_sort() { + if [ "$with_sort" = "0" ] + then + log 1 "checking sort... disabled" + + return + fi + + if [ "$with_sort" = "1" ] || [ "$with_sort" = "2" ] + then + _detect_sort "sort" + if [ -z "$sort" ]; then _detect_sort "/sbin/sort"; fi + if [ -z "$sort" ]; then _detect_sort "/usr/sbin/sort"; fi + if [ -z "$sort" ]; then _detect_sort "/usr/local/sbin/sort"; fi + if [ -z "$sort" ]; then _detect_sort "/bin/sort"; fi + if [ -z "$sort" ]; then _detect_sort "/usr/bin/sort"; fi + if [ -z "$sort" ]; then _detect_sort "/usr/local/bin/sort"; fi + else + _detect_sort "$with_sort" + fi + + if [ -z "$sort" ] + then + if [ "$with_sort" = "2" ] + then + log 1 "checking sort... not found" + + log 1 "configure: error: couldn't detect sort on your system" + exit 1 + elif [ "$with_sort" != "1" ] + then + log 1 "checking sort... $with_sort not found" + + log 1 "configure: error: '$with_sort' doesn't look like a sort to me" + log 1 "configure: error: please verify its location and function and try again" + + exit 1 + else + log 1 "checking sort... not found" + fi + else + log 1 "checking sort... $sort" + fi +} + +make_sed() { + # We check here if we are PPC, because then we need to enable FOUR_BYTE_BOOL + # We do this here, and not sooner, so universal builds also have this + # automaticly correct + # FOUR_BYTE_BOOL is needed, because C++ uses 4byte for a bool on PPC, where + # we use 1 byte per bool normally in C part. So convert that last one to 4 + # bytes too, but only for PPC. + ppc=`$cc_host -dumpmachine | grep "powerpc\|ppc"` + if [ -n "$ppc" ] + then + T_CFLAGS="$CFLAGS -DFOUR_BYTE_BOOL" + osx_sysroot_version=10.3.9 + else + T_CFLAGS="$CFLAGS" + osx_sysroot_version=10.4u + fi + + T_LDFLAGS="$LDFLAGS" + if [ "$with_osx_sysroot" = "3" ] + then + T_CFLAGS="$T_CFLAGS -isysroot /Developer/SDKs/MacOSX$osx_sysroot_version.sdk" + T_LDFLAGS="$T_LDFLAGS -Wl,-syslibroot,/Developer/SDKs/MacOSX$osx_sysroot_version.sdk" + fi + + SRC_OBJS_DIR="$BASE_SRC_OBJS_DIR/$OBJS_SUBDIR" + + # All the data needed to compile a single target + # Make sure if you compile multiple targets to + # use multiple OBJS_DIR, because all in-between + # binaries are stored in there, and nowhere else. + SRC_REPLACE=" + s#!!CC_HOST!!#$cc_host#g; + s#!!CXX_HOST!!#$cxx_host#g; + s#!!CC_BUILD!!#$cc_build#g; + s#!!WINDRES!!#$windres#g; + s#!!STRIP!!#$strip $strip_arg#g; + s#!!LIPO!!#$lipo#g; + s#!!CC_CFLAGS!!#$CC_CFLAGS#g; + s#!!CFLAGS!!#$T_CFLAGS#g; + s#!!CFLAGS_BUILD!!#$CFLAGS_BUILD#g; + s#!!STRGEN_FLAGS!!#$strgen_flags#g; + s#!!LIBS!!#$LIBS#g; + s#!!LDFLAGS!!#$T_LDFLAGS#g; + s#!!BIN_DIR!!#$BIN_DIR#g; + s#!!ROOT_DIR!!#$ROOT_DIR#g; + s#!!MEDIA_DIR!!#$MEDIA_DIR#g; + s#!!SOURCE_LIST!!#$SOURCE_LIST#g; + s#!!SRC_OBJS_DIR!!#$SRC_OBJS_DIR#g; + s#!!LANG_OBJS_DIR!!#$LANG_OBJS_DIR#g; + s#!!SRC_DIR!!#$SRC_DIR#g; + s#!!OSXAPP!!#$OSXAPP#g; + s#!!LANG_DIR!!#$LANG_DIR#g; + s#!!TTD!!#$TTD#g; + s#!!BINARY_DIR!!#$prefix_dir/$binary_dir#g; + s#!!DATA_DIR!!#$prefix_dir/$data_dir#g; + s#!!ICON_DIR!!#$prefix_dir/$icon_dir#g; + s#!!PERSONAL_DIR!!#$personal_dir#g; + s#!!INSTALL_DIR!!#$install_dir#g; + s#!!STRGEN!!#$STRGEN#g; + s#!!ENDIAN_CHECK!!#$ENDIAN_CHECK#g; + s#!!ENDIAN_FORCE!!#$endian#g; + s#!!STAGE!!#$STAGE#g; + s#!!MAKEDEPEND!!#$makedepend#g; + s#!!CFLAGS_MAKEDEP!!#$cflags_makedep#g; + s#!!SORT!!#$sort#g; + s#!!CONFIG_CACHE_COMPILER!!#config.cache.compiler#g; + s#!!CONFIG_CACHE_LINKER!!#config.cache.linker#g; + s#!!CONFIG_CACHE_ENDIAN!!#config.cache.endian#g; + s#!!CONFIG_CACHE_SOURCE!!#config.cache.source#g; + s#!!CONFIG_CACHE_VERSION!!#config.cache.version#g; + s#!!CONFIG_CACHE_SOURCE_LIST!!#config.cache.source.list#g; + s#!!LANG_SUPPRESS!!#$lang_suppress#g; + s#!!OBJS_C!!#$OBJS_C#g; + s#!!OBJS_CPP!!#$OBJS_CPP#g; + s#!!OBJS_M!!#$OBJS_M#g; + s#!!OBJS_RC!!#$OBJS_RC#g; + s#!!SRCS!!#$SRCS#g; + s#!!OS!!#$os#g; + s#!!CONFIGURE_FILES!!#$CONFIGURE_FILES#g; + s#!!REVISION!!#$revision#g; + s#!!ENABLE_INSTALL!!#$enable_install#g; + " +} + +generate_main() { + STAGE="[MAIN]" + + make_sed + + # Create the main Makefile + echo "Generating Makefile..." + cat $ROOT_DIR/Makefile.in | sed "$SRC_REPLACE" > Makefile + echo "# Auto-generated file -- DO NOT EDIT" > Makefile.am + echo "" > Makefile.am + # Make the copy of the source-list, so we don't trigger an unwanted recompile + cp $SOURCE_LIST config.cache.source.list + # Make sure config.cache is OLDER then config.cache.source.list + touch config.cache +} + +generate_lang() { + STAGE="[LANG]" + + make_sed + + # Create the language file + mkdir -p $LANG_OBJS_DIR + + echo "Generating lang/Makefile..." + cat $ROOT_DIR/Makefile.lang.in | sed "$SRC_REPLACE" > $LANG_OBJS_DIR/Makefile + echo "DIRS += $LANG_OBJS_DIR" >> Makefile.am + echo "LANG_DIRS += $LANG_OBJS_DIR" >> Makefile.am +} + +generate_src_normal() { + STAGE=$1 + + make_sed + + # Create the source file + mkdir -p $SRC_OBJS_DIR + + echo "Generating $2/Makefile..." + cat $ROOT_DIR/Makefile.src.in | sed "$SRC_REPLACE" > $SRC_OBJS_DIR/Makefile + echo "DIRS += $SRC_OBJS_DIR" >> Makefile.am + echo "SRC_DIRS += $SRC_OBJS_DIR" >> Makefile.am +} + +generate_src_osx() { + cc_host_orig="$cc_host" + cxx_host_orig="$cxx_host" + + BASE_SRC_OBJS_DIR="$OBJS_DIR/ppc" + cc_host="$cc_host_orig -arch ppc" + cxx_host="$cxx_host_orig -arch ppc" + generate_src_normal "[PowerPC]" "objs/ppc" + + BASE_SRC_OBJS_DIR="$OBJS_DIR/i386" + cc_host="$cc_host_orig -arch i386" + cxx_host="$cxx_host_orig -arch i386" + generate_src_normal "[i386]" "objs/i386" + + BASE_SRC_OBJS_DIR="$OBJS_DIR/ppc970" + cc_host="$cc_host_orig -arch ppc970" + cxx_host="$cxx_host_orig -arch ppc970" + CFLAGS="$CFLAGS -mtune=970 -mcpu=970 -mpowerpc-gpopt" + generate_src_normal "[PowerPC G5]" "objs/ppc970" +} + +generate_src() { + if [ "$os" = "OSX" ] && [ "$enable_universal" != "0" ] + then + generate_src_osx + else + generate_src_normal "[SRC]" "objs" + fi +} + +showhelp() { + echo "'configure' configures OpenTTD." + echo "" + echo "Usage: $0 [OPTION]... [VAR=VALUE]..." + echo "" + echo "To assign environment variables (e.g., CC, CFLAGS...), specify them as" + echo "VAR=VALUE. See below for descriptions of some of the useful variables." + echo "" + echo "Defaults for the options are specified in brackets." + echo "" + echo "Configuration:" + echo " -h, --help display this help and exit" + echo "" + echo "System types:" + echo " --build=BUILD configure for building on BUILD [guessed]" + echo " --host=HOST cross-compile to build programs to run on HOST [BUILD]" + echo " --windres=WINDRES the windres to use [HOST-windres]" + echo " --strip=STRIP the strip to use [HOST-strip]" + echo " --lipo=LIPO the lipo to use (OSX ONLY) [HOST-lipo]" + echo " --os=OS the OS we are compiling for [DETECT]" + echo " DETECT/UNIX/OSX/FREEBSD/MORPHOS/BEOS/SUNOS/CYGWIN/MINGW" + echo " --endian=ENDIAN set the endian of the HOST (AUTO/LE/BE) [AUTO]" + echo " --revision=rXXXX overwrite the revision detection. Use with care!" + echo "" + echo "Paths:" + echo " --prefix-dir=dir specifies the prefix for all installed files [/usr/local]" + echo " --binary-dir=dir location of the binary. Will be prefixed with the" + echo " prefix-dir [games]" + echo " --data-dir=dir location of data files (lang, data, gm, scenario)." + echo " Will be prefixed with the prefix-dir [share/games/openttd]" + echo " --icon-dir=dir location of icons. Will be prefixed with the" + echo " prefix-dir [share/pixmaps]" + echo " --personal-dir=dir location of the personal directory []" + echo " --install-dir=dir specifies the root to install to. Useful to install" + echo " into jails [/]" + echo "" +# TODO: The Following 3 tags will be removed when the 'search path patch' is applied + echo " --second-data-dir=dir specifies a second directory for the data files" + echo " --custom-lang-dir=dir specifies a custom directory for the language files" + echo " --enable-install make a binary that uses the specified data-dir and icon-dir" + echo "" + echo "Features and packages:" + echo " --enable-debug[=LVL] enable debug-mode (LVL=[0123], 0 is release) [LVL=0]" + echo " --enable-profiling enables profiling (can be mixed with --enable-debug)" + echo " --enable-dedicated compile a dedicated server (without video-drivers)" + echo " --enable-static enable static compile (doesn't work for all HOSTs)" + echo " --enable-translator enable extra output for translators" + echo " --enable-universal enable universal builds (OSX ONLY)" + echo " --enable-osx-g5 enables optimalizations for G5 (OSX ONLY)" + echo " --disable-network disable network support" + echo " --disable-assert disable asserts (on error, program just continues)" + echo " --disable-strip disable any possible stripping" + echo " --without-osx-sysroot disable the automatic adding of sysroot (OSX ONLY)" + echo " --without-application-bundle disable generation of application bundle (OSX ONLY)" + echo " --with-direct-music enable direct music support (Win32 ONLY)" + echo " --with-sort=sort define a non-default location for sort" + echo " --with-midi=midi define which midi-player to use" + echo " --with-midi-arg=arg define which args to use for the midi-player" + echo " --with-cocoa enables COCOA video driver (OSX ONLY) support" + echo " --with-sdl[=sdl-config] enables SDL video driver support" + echo " --with-zlib[=zlib.a] enables zlib support" + echo " --with-png[=libpng-config] enables libpng support" + echo " --with-freetype[=freetype-config]" + echo " enables libfreetype support" + echo " --with-fontconfig[=pkg-config fontconfig]" + echo " enables fontconfig support" + echo " --with-iconv[=iconv-path] enables iconv support" + echo " --with-makedepend[=makedepend] enables makedepend support" + echo "" + echo "Some influential environment variables:" + echo " CC C compiler command" + echo " CXX C++ compiler command" + echo " CFLAGS C compiler flags" + echo " WINDRES windres command" + echo " LDFLAGS linker flags, e.g. -L if you have libraries" + echo " in a nonstandard directory " + echo "" + echo "Use these variables to override the choices made by 'configure' or to help" + echo "it to find libraries and programs with nonstandard names/locations." +} diff --git a/configure b/configure --- a/configure +++ b/configure @@ -1,323 +1,121 @@ #!/bin/sh -# This 'configure' script is a very easy wrapper around 'make updateconf' -# It allows cross-compilers to do their job much more easy. +# Find out where configure is (in what dir) +ROOT_DIR="`dirname $0`" +ROOT_DIR="`cd $ROOT_DIR && pwd`" -function showhelp() { - echo "Configure for OpenTTD" - echo "" - echo "Usage:" - echo " $0 --your_options" - echo "" - echo "Params:" - echo " --debug Create debug-release [no]" - echo " --profile Create profile-release [no]" - echo " --dedicated Make a dedicated build [no]" - echo " --revision Set the revision of the compilation [detected]" - echo " --target-cc Sets the target-compiler [\$CC]" - echo " --target-cxx Sets the C++ target-compiler []" - echo " --host-cc Sets the host-compiler [\$CC]" - echo " --host-cxx Sets the C++ host-compiler []" - echo " --os Sets the OS. Listens to: [detected]" - echo " UNIX, OSX, FREEBSD, MORPHOS" - echo " BEOS, SUNOS, CYGWIN, MINGW, OS2" - echo " --windres Sets the windres (Windows) [windres]" - echo " --force-le Force LE platform [no]" - echo " --force-be Force BE platform [no]" - echo "" - echo "Params that can be used with --with or --without" - echo " (e.g.: --without-static disables static (default))" - echo " static Do you want a static build? [no]" - echo " directmusic Do you want direct-music? [no]" - echo " zlib Do you want zlib-support? [yes]" - echo " sdl Do you want SDL-support? [yes]" - echo " png Do you want PNG-support? [yes]" - echo " iconv Do you want iconv-support? [no]" - echo " network Do you want network-support? [yes]" - echo " cocoa Do you want cocoa-support? (MacOSX) [no]" - echo " freetype Do you want freetype-support? [yes]" - echo " fontconfig Do you want fontconfig-support? [yes]" - echo "" - echo "Params used to configure external libs:" - echo " --static-zlib-path Set the path to your static zlib []" - echo " --sdl-config Where is your sdl-config [sdl-config]" - echo " --libpng-config Where is your libpng-config [libpng-config]" - echo " --freetype-config Where is your freetype-config [freetype-config]" - echo " --fontconfig-config Where is your fontconfig-config [pkg-config fontconfig]" - echo " --with-iconv Set the path to your iconv headers []" - echo " " -} +PWD="`pwd`" +PREFIX="`pwd`/bin" -function handle() { - PARAM="$PARAM \"$1=`awk 'BEGIN { FS="="; $0="'"$2"'"; print $2;}'`\"" -} - -# The things you can use inside this case: -# handle NAME VALUE - Sets the value to give the 'make upgradeconf' -# Value is in form: tag=REAL_VALUE -# ITEM="NAME" - Will set the value as above, only with the next param -# SITEM="NAME" - Will set the var $NAME to the next param -for n in "$@" -do - case "$n" in - --help | -h) - showhelp - exit 0 - ;; +. $ROOT_DIR/config.lib - --debug) - DEBUG_SET=1 - ITEM="DEBUG" - ;; - --debug=*) - handle "DEBUG" "$n" - ;; - --profile) - PARAM="$PARAM PROFILE=1" - ;; - --dedicated) - PARAM="$PARAM DEDICATED=1" - ;; - --revision=*) - RELEASE=`awk 'BEGIN { FS="="; $0="'"$n"'"; print $2;}'` - ;; - --revision) - SITEM="RELEASE" - ;; - --target-cc=*) - handle "CC_TARGET" "$n" - ;; - --target-cc) - ITEM="CC_TARGET" - ;; - --target-cxx=*) - handle "CXX_TARGET" "$n" - ;; - --target-cxx) - SITEM="CXX_TARGET" - ;; - --host-cc=*) - handle "CC_HOST" "$n" - ;; - --host-cc) - ITEM="CC_HOST" - ;; - --host-cxx=*) - handle "CXX_HOST" "$n" - ;; - --host-cxx) - ITEM="CXX_HOST" - ;; - --host-cflags=*) - handle CFLAGS_HOST "$n" - ;; - --host-cflags) - ITEM="CFLAGS_HOST" - ;; - --os=*) - TARGET_OS=`awk 'BEGIN { FS="="; $0="'"$n"'"; print $2;}'` - ;; - --os) - SITEM="TARGET_OS" - ;; - --windres=*) - handle WINDRES "$n" - ;; - --windres) - ITEM="WINDRES" - ;; - --force-le) - PARAM="$PARAM ENDIAN_FORCE=LE" - ;; - --force-be) - PARAM="$PARAM ENDIAN_FORCE=BE" - ;; +# Set default dirs +OBJS_DIR="$PWD/objs" +BASE_SRC_OBJS_DIR="$OBJS_DIR" +LANG_OBJS_DIR="$OBJS_DIR/lang" +BIN_DIR="$PREFIX" +SRC_DIR="$ROOT_DIR/src" +LANG_DIR="$SRC_DIR/lang" +MEDIA_DIR="$ROOT_DIR/media" +SOURCE_LIST="$ROOT_DIR/source.list" - --with-static) - PARAM="$PARAM STATIC=1" - ;; - --without-static) - PARAM="$PARAM STATIC=" - ;; - --with-directmusic) - PARAM="$PARAM WITH_DIRECTMUSIC=1" - ;; - --without-directmusic) - PARAM="$PARAM WITH_DIRECTMUSIC=" - ;; - --with-zlib) - PARAM="$PARAM WITH_ZLIB=1" - ;; - --without-zlib) - PARAM="$PARAM WITH_ZLIB=" - ;; - --with-sdl) - PARAM="$PARAM WITH_SDL=1" - ;; - --without-sdl) - PARAM="$PARAM WITH_SDL=" - ;; - --with-png) - PARAM="$PARAM WITH_PNG=1" - ;; - --without-png) - PARAM="$PARAM WITH_PNG=" - ;; - --with-iconv) - PARAM="$PARAM WITH_ICONV=1" - ;; - --with-iconv=*) - PARAM="$PARAM WITH_ICONV=1" - handle WITH_ICONV_PATH "$n" - ;; - --without-iconv) - PARAM="$PARAM WITH_ICONV=" - ;; - --with-cocoa) - PARAM="$PARAM WITH_COCOA=1" - ;; - --with-network) - PARAM="$PARAM WITH_NETWORK=1" - ;; - --without-network) - PARAM="$PARAM WITH_NETWORK=" - ;; - --without-cocoa) - PARAM="$PARAM WITH_COCOA=" - ;; - --with-freetype) - PARAM="$PARAM WITH_FREETYPE=1" - ;; - --without-freetype) - PARAM="$PARAM WITH_FREETYPE=" - ;; - --with-fontconfig) - PARAM="$PARAM WITH_FONTCONFIG=1" - ;; - --without-fontconfig) - PARAM="$PARAM WITH_FONTCONFIG=" - ;; - --static-zlib-path=*) - handle STATIC_ZLIB_PATH "$n" - ;; - --static-zlib-path) - ITEM="STATIC_ZLIB_PATH" - ;; - --sdl-config=*) - handle SDL_CONFIG "$n" - ;; - --sdl-config) - ITEM="SDL_CONFIG" - ;; - --libpng-config=*) - handle LIBPNG_CONFIG "$n" - ;; - --libpng-config) - ITEM="LIBPNG_CONFIG" - ;; - --freetype-config=*) - handle FREETYPE_CONFIG "$n" - ;; - --freetype-config) - ITEM="FREETYPE_CONFIG" - ;; - --fontconfig-config=*) - handle FONTCONFIG_CONFIG "$n" - ;; - --fontconfig-config) - ITEM="FONTCONFIG_CONFIG" - ;; +if [ "$1" = "--reconfig" ] +then + if ! [ -f "config.cache" ] + then + echo "can't reconfigure, because never configured before" + exit 1 + fi + # Make sure we don't lock config.cache + configure=`cat config.cache` + $configure + exit $? +fi - --*=*) - echo -n "Unknown switch " - echo `awk 'BEGIN { FS="="; $0="'"$n"'"; print $1;}'` - exit 1 - ;; - -*) - echo "Unknown switch $n" - exit 1 - ;; +set_default +detect_params $@ +save_params +check_params +make_cflags_and_ldflags - *) - if ! test -z "$ITEM" - then - PARAM="$PARAM $ITEM=\"$n\"" - ITEM=""; - elif ! test -z "$SITEM" - then - export $SITEM="$n" - SITEM="" - else - echo "Unknown switch $n" - exit 1 - fi - ;; - esac -done - -if ! test -z "$TARGET_OS" +EXE="" +if [ "$os" = "MINGW" ] || [ "$os" = "CYGWIN" ] || [ "$os" = "OS2" ] then - TARGET_OS=`echo $TARGET_OS | tr '[:lower:]' '[:upper:]'` - case "$TARGET_OS" in - WIN32) - PARAM="$PARAM WIN32=1" - ;; - UNIX) - PARAM="$PARAM UNIX=1" - ;; - OSX) - PARAM="$PARAM OSX=1 UNIX=1" - ;; - FREEBSD) - PARAM="$PARAM FREEBSD=1" - ;; - MORPHOS) - PARAM="$PARAM MORPHOS=1 UNIX=1" - ;; - BEOS) - PARAM="$PARAM BEOS=1 UNIX=1" - ;; - OS2) - PARAM="$PARAM OS2=1 UNIX=1" - ;; - SUNOS) - PARAM="$PARAM SUNOS=1 UNIX=1" - ;; - CYGWIN) - PARAM="$PARAM CYGWIN=1 WIN32=1" - ;; - MINGW) - PARAM="$PARAM MINGW=1 WIN32=1" - ;; - *) - echo "Unknown OS: $TARGET_OS" - exit 1 - ;; - esac - PARAM="$PARAM BYPASS_OS_DETECT=1" + EXE=".exe" +fi + +TTD="openttd$EXE" +STRGEN="strgen$EXE" +ENDIAN_CHECK="endian_check$EXE" + +if [ -z "$sort" ] +then + PIPE_SORT="sed s/a/a/" +else + PIPE_SORT="$sort" fi -if ! test -z "$DEBUG_SET" +if ! [ -f "$LANG_DIR/english.txt" ] then - if test -z "`echo $PARAM | grep "DEBUG="`" - then - # Someone did --debug, without assigning a value, assume 1 - PARAM="$PARAM DEBUG=1" - fi + echo "Languages not found in $LANG_DIR. Can't continue without it." + echo "Please make sure the dir exists and contains at least english.txt" fi -# First remove the Makefile.config, else you can have double entries -rm -f Makefile.config +# Read the source.list and process it +SRCS="`cat $ROOT_DIR/source.list | tr '\r' '\n' | awk ' + { } + /^( *)#end/ { if (deep == skip) { skip -= 1; } deep -= 1; next; } + /^( *)#else/ { if (deep == skip) { skip -= 1; } else if (deep - 1 == skip) { skip += 1; } next; } + /^( *)#if/ { + gsub(" ", "", $0); + gsub("^#if ", "", $0); + + if (deep != skip) { deep += 1; next; } -echo "make upgradeconf $PARAM" > Makefile.run -. Makefile.run -rm -f Makefile.run + deep += 1; + + if ($0 == "SDL" && "'$sdl_config'" == "") { next; } + if ($0 == "OSX" && "'$os'" != "OSX") { next; } + if ($0 == "DEDICATED" && "'$enable_dedicated'" != "1") { next; } + if ($0 == "COCOA" && "'$with_cocoa'" == "0") { next; } + if ($0 == "BEOS" && "'$os'" != "BEOS") { next; } + if ($0 == "WIN32" && "'$os'" != "MINGW" && + "'$os'" != "CYGWIN" && "'$os'" != "MSVC" ) { next; } + if ($0 == "MSVC" && "'$os'" != "MSVC") { next; } + if ($0 == "DIRECTMUSIC" && "'$with_direct_music'" != "1") { next; } + + skip += 1; -# Makefile.config currently doesn't support custom RELEASE (revision), so, we add the line -# yourself! + next; + } + /^( *)#/ { next } + /^$/ { next } + /\.h$/ { next } + /\.hpp$/ { next } + { + if (deep == skip) { + gsub(" ", "", $0); + print $0; + } + } +' | $PIPE_SORT`" -if ! test -z "$RELEASE" +OBJS_C="` echo \"$SRCS\" | awk ' { ORS = " " } /\.c$/ { gsub(".c$", ".o", $0); print $0; }'`" +OBJS_CPP="`echo \"$SRCS\" | awk ' { ORS = " " } /\.cpp$/ { gsub(".cpp$", ".o", $0); print $0; }'`" +OBJS_M="` echo \"$SRCS\" | awk ' { ORS = " " } /\.m$/ { gsub(".m$", ".o", $0); print $0; }'`" +OBJS_RC="` echo \"$SRCS\" | awk ' { ORS = " " } /\.rc$/ { gsub(".rc$", ".o", $0); print $0; }'`" +SRCS="` echo \"$SRCS\" | awk ' { ORS = " " } { print $0; }'`" + +# In makefiles, we always use -u for sort +if [ -z "$sort" ] then - echo "RELEASE=$RELEASE" >> Makefile.config + sort="sed s/a/a/" +else + sort="$sort -u" fi +CONFIGURE_FILES="$ROOT_DIR/configure $ROOT_DIR/config.lib $ROOT_DIR/Makefile.in $ROOT_DIR/Makefile.lang.in $ROOT_DIR/Makefile.src.in" + +generate_main +generate_lang +generate_src diff --git a/console.c b/console.c deleted file mode 100644 --- a/console.c +++ /dev/null @@ -1,1150 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "table/strings.h" -#include "functions.h" -#include "window.h" -#include "gui.h" -#include "gfx.h" -#include "player.h" -#include "variables.h" -#include "string.h" -#include -#include -#include "console.h" -#include "network/network.h" -#include "network/network_data.h" -#include "network/network_server.h" - -#define ICON_BUFFER 79 -#define ICON_HISTORY_SIZE 20 -#define ICON_LINE_HEIGHT 12 -#define ICON_RIGHT_BORDERWIDTH 10 -#define ICON_BOTTOM_BORDERWIDTH 12 -#define ICON_MAX_ALIAS_LINES 40 -#define ICON_TOKEN_COUNT 20 - -// ** main console ** // -static char *_iconsole_buffer[ICON_BUFFER + 1]; -static uint16 _iconsole_cbuffer[ICON_BUFFER + 1]; -static Textbuf _iconsole_cmdline; - -// ** stdlib ** // -byte _stdlib_developer = 1; -bool _stdlib_con_developer = false; -FILE *_iconsole_output_file; - -// ** main console cmd buffer -static char *_iconsole_history[ICON_HISTORY_SIZE]; -static byte _iconsole_historypos; - -/* *************** */ -/* end of header */ -/* *************** */ - -static void IConsoleClearCommand(void) -{ - memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE); - _iconsole_cmdline.length = 0; - _iconsole_cmdline.width = 0; - _iconsole_cmdline.caretpos = 0; - _iconsole_cmdline.caretxoffs = 0; - SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); -} - -static inline void IConsoleResetHistoryPos(void) {_iconsole_historypos = ICON_HISTORY_SIZE - 1;} - - -static void IConsoleHistoryAdd(const char *cmd); -static void IConsoleHistoryNavigate(int direction); - -// ** console window ** // -static void IConsoleWndProc(Window *w, WindowEvent *e) -{ - static byte iconsole_scroll = ICON_BUFFER; - - switch (e->event) { - case WE_PAINT: { - int i = iconsole_scroll; - int max = (w->height / ICON_LINE_HEIGHT) - 1; - int delta = 0; - GfxFillRect(w->left, w->top, w->width, w->height - 1, 0); - while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) { - DoDrawString(_iconsole_buffer[i], 5, - w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); - i--; - } - /* If the text is longer than the window, don't show the starting ']' */ - delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; - if (delta > 0) { - DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd); - delta = 0; - } - - DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd); - - if (_iconsole_cmdline.caret) - DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12); - break; - } - case WE_MOUSELOOP: - if (HandleCaret(&_iconsole_cmdline)) - SetWindowDirty(w); - break; - case WE_DESTROY: - _iconsole_mode = ICONSOLE_CLOSED; - break; - case WE_KEYPRESS: - e->we.keypress.cont = false; - switch (e->we.keypress.keycode) { - case WKC_UP: - IConsoleHistoryNavigate(+1); - SetWindowDirty(w); - break; - case WKC_DOWN: - IConsoleHistoryNavigate(-1); - SetWindowDirty(w); - break; - case WKC_SHIFT | WKC_PAGEUP: - if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) { - iconsole_scroll = 0; - } else { - iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1; - } - SetWindowDirty(w); - break; - case WKC_SHIFT | WKC_PAGEDOWN: - if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) { - iconsole_scroll = ICON_BUFFER; - } else { - iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1; - } - SetWindowDirty(w); - break; - case WKC_SHIFT | WKC_UP: - if (iconsole_scroll <= 0) { - iconsole_scroll = 0; - } else { - --iconsole_scroll; - } - SetWindowDirty(w); - break; - case WKC_SHIFT | WKC_DOWN: - if (iconsole_scroll >= ICON_BUFFER) { - iconsole_scroll = ICON_BUFFER; - } else { - ++iconsole_scroll; - } - SetWindowDirty(w); - break; - case WKC_BACKQUOTE: - IConsoleSwitch(); - break; - case WKC_RETURN: case WKC_NUM_ENTER: - IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf); - IConsoleHistoryAdd(_iconsole_cmdline.buf); - - IConsoleCmdExec(_iconsole_cmdline.buf); - IConsoleClearCommand(); - break; - case WKC_CTRL | WKC_RETURN: - _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; - IConsoleResize(w); - MarkWholeScreenDirty(); - break; - case (WKC_CTRL | 'V'): - if (InsertTextBufferClipboard(&_iconsole_cmdline)) { - IConsoleResetHistoryPos(); - SetWindowDirty(w); - } - break; - case (WKC_CTRL | 'L'): - IConsoleCmdExec("clear"); - break; - case (WKC_CTRL | 'U'): - DeleteTextBufferAll(&_iconsole_cmdline); - SetWindowDirty(w); - break; - case WKC_BACKSPACE: case WKC_DELETE: - if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) { - IConsoleResetHistoryPos(); - SetWindowDirty(w); - } - break; - case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: - if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) { - IConsoleResetHistoryPos(); - SetWindowDirty(w); - } - break; - default: - if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) { - iconsole_scroll = ICON_BUFFER; - InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key); - IConsoleResetHistoryPos(); - SetWindowDirty(w); - } else { - e->we.keypress.cont = true; - } - break; - } - } -} - -static const Widget _iconsole_window_widgets[] = { - {WIDGETS_END} -}; - -static const WindowDesc _iconsole_window_desc = { - 0, 0, 2, 2, - WC_CONSOLE, 0, - WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, - _iconsole_window_widgets, - IConsoleWndProc, -}; - -void IConsoleInit(void) -{ - extern const char _openttd_revision[]; - _iconsole_output_file = NULL; - _icolour_def = 1; - _icolour_err = 3; - _icolour_warn = 13; - _icolour_dbg = 5; - _icolour_cmd = 2; - _iconsole_historypos = ICON_HISTORY_SIZE - 1; - _iconsole_mode = ICONSOLE_CLOSED; - -#ifdef ENABLE_NETWORK /* Initialize network only variables */ - _redirect_console_to_client = 0; -#endif - - memset(_iconsole_history, 0, sizeof(_iconsole_history)); - memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer)); - memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer)); - _iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it - _iconsole_cmdline.maxlength = ICON_CMDLN_SIZE; - - IConsolePrintF(13, "OpenTTD Game Console Revision 7 - %s", _openttd_revision); - IConsolePrint(12, "------------------------------------"); - IConsolePrint(12, "use \"help\" for more information"); - IConsolePrint(12, ""); - IConsoleStdLibRegister(); - IConsoleClearCommand(); - IConsoleHistoryAdd(""); -} - -void IConsoleClearBuffer(void) -{ - uint i; - for (i = 0; i <= ICON_BUFFER; i++) { - free(_iconsole_buffer[i]); - _iconsole_buffer[i] = NULL; - } -} - -static void IConsoleClear(void) -{ - free(_iconsole_cmdline.buf); - IConsoleClearBuffer(); -} - -static void IConsoleWriteToLogFile(const char *string) -{ - if (_iconsole_output_file != NULL) { - // if there is an console output file ... also print it there - fwrite(string, strlen(string), 1, _iconsole_output_file); - fwrite("\n", 1, 1, _iconsole_output_file); - } -} - -bool CloseConsoleLogIfActive(void) -{ - if (_iconsole_output_file != NULL) { - IConsolePrintF(_icolour_def, "file output complete"); - fclose(_iconsole_output_file); - _iconsole_output_file = NULL; - return true; - } - - return false; -} - -void IConsoleFree(void) -{ - IConsoleClear(); - CloseConsoleLogIfActive(); -} - -void IConsoleResize(Window *w) -{ - switch (_iconsole_mode) { - case ICONSOLE_OPENED: - w->height = _screen.height / 3; - w->width = _screen.width; - break; - case ICONSOLE_FULL: - w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH; - w->width = _screen.width; - break; - default: return; - } - - MarkWholeScreenDirty(); -} - -void IConsoleSwitch(void) -{ - switch (_iconsole_mode) { - case ICONSOLE_CLOSED: { - Window *w = AllocateWindowDesc(&_iconsole_window_desc); - w->height = _screen.height / 3; - w->width = _screen.width; - _iconsole_mode = ICONSOLE_OPENED; - SETBIT(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll - } break; - case ICONSOLE_OPENED: case ICONSOLE_FULL: - DeleteWindowById(WC_CONSOLE, 0); - _iconsole_mode = ICONSOLE_CLOSED; - CLRBIT(_no_scroll, SCROLL_CON); - break; - } - - MarkWholeScreenDirty(); -} - -void IConsoleClose(void) {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();} -void IConsoleOpen(void) {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();} - -/** - * Add the entered line into the history so you can look it back - * scroll, etc. Put it to the beginning as it is the latest text - * @param cmd Text to be entered into the 'history' - */ -static void IConsoleHistoryAdd(const char *cmd) -{ - free(_iconsole_history[ICON_HISTORY_SIZE - 1]); - - memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1)); - _iconsole_history[0] = strdup(cmd); - IConsoleResetHistoryPos(); -} - -/** - * Navigate Up/Down in the history of typed commands - * @param direction Go further back in history (+1), go to recently typed commands (-1) - */ -static void IConsoleHistoryNavigate(int direction) -{ - int i = _iconsole_historypos + direction; - - // watch out for overflows, just wrap around - if (i < 0) i = ICON_HISTORY_SIZE - 1; - if (i >= ICON_HISTORY_SIZE) i = 0; - - if (direction > 0) - if (_iconsole_history[i] == NULL) i = 0; - - if (direction < 0) { - while (i > 0 && _iconsole_history[i] == NULL) i--; - } - - _iconsole_historypos = i; - IConsoleClearCommand(); - // copy history to 'command prompt / bash' - assert(_iconsole_history[i] != NULL && IS_INT_INSIDE(i, 0, ICON_HISTORY_SIZE)); - ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength); - UpdateTextBufferSize(&_iconsole_cmdline); -} - -/** - * Handle the printing of text entered into the console or redirected there - * by any other means. Text can be redirected to other players in a network game - * as well as to a logfile. If the network server is a dedicated server, all activities - * are also logged. All lines to print are added to a temporary buffer which can be - * used as a history to print them onscreen - * @param color_code the colour of the command. Red in case of errors, etc. - * @param string the message entered or output on the console (notice, error, etc.) - */ -void IConsolePrint(uint16 color_code, const char *string) -{ - char *str; -#ifdef ENABLE_NETWORK - if (_redirect_console_to_client != 0) { - /* Redirect the string to the client */ - SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string); - return; - } -#endif - - /* Create a copy of the string, strip if of colours and invalid - * characters and (when applicable) assign it to the console buffer */ - str = strdup(string); - str_strip_colours(str); - str_validate(str); - - if (_network_dedicated) { - printf("%s\n", str); - IConsoleWriteToLogFile(str); - free(str); // free duplicated string since it's not used anymore - return; - } - - /* move up all the strings in the buffer one place and do the same for colour - * to accomodate for the new command/message */ - free(_iconsole_buffer[0]); - memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER); - _iconsole_buffer[ICON_BUFFER] = str; - - memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER); - _iconsole_cbuffer[ICON_BUFFER] = color_code; - - IConsoleWriteToLogFile(_iconsole_buffer[ICON_BUFFER]); - - SetWindowDirty(FindWindowById(WC_CONSOLE, 0)); -} - -/** - * Handle the printing of text entered into the console or redirected there - * by any other means. Uses printf() style format, for more information look - * at @IConsolePrint() - */ -void CDECL IConsolePrintF(uint16 color_code, const char *s, ...) -{ - va_list va; - char buf[ICON_MAX_STREAMSIZE]; - - va_start(va, s); - vsnprintf(buf, sizeof(buf), s, va); - va_end(va); - - IConsolePrint(color_code, buf); -} - -/** - * It is possible to print debugging information to the console, - * which is achieved by using this function. Can only be used by - * @debug() in debug.c. You need at least a level 2 (developer) for debugging - * messages to show up - * @param dbg debugging category - * @param string debugging message - */ -void IConsoleDebug(const char *dbg, const char *string) -{ - if (_stdlib_developer > 1) - IConsolePrintF(_icolour_dbg, "dbg: [%s] %s", dbg, string); -} - -/** - * It is possible to print warnings to the console. These are mostly - * errors or mishaps, but non-fatal. You need at least a level 1 (developer) for - * debugging messages to show up - */ -void IConsoleWarning(const char *string) -{ - if (_stdlib_developer > 0) - IConsolePrintF(_icolour_warn, "WARNING: %s", string); -} - -/** - * It is possible to print error information to the console. This can include - * game errors, or errors in general you would want the user to notice - */ -void IConsoleError(const char *string) -{ - IConsolePrintF(_icolour_err, "ERROR: %s", string); -} - -/** - * Change a string into its number representation. Supports - * decimal and hexadecimal numbers as well as 'on'/'off' 'true'/'false' - * @param *value the variable a successfull conversion will be put in - * @param *arg the string to be converted - * @return Return true on success or false on failure - */ -bool GetArgumentInteger(uint32 *value, const char *arg) -{ - char *endptr; - - if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) { - *value = 1; - return true; - } - if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) { - *value = 0; - return true; - } - - *value = strtoul(arg, &endptr, 0); - return arg != endptr; -} - -// * ************************* * // -// * hooking code * // -// * ************************* * // -/** - * General internal hooking code that is the same for both commands and variables - * @param hooks @IConsoleHooks structure that will be set according to - * @param type type access trigger - * @param proc function called when the hook criteria is met - */ -static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc) -{ - if (hooks == NULL || proc == NULL) return; - - switch (type) { - case ICONSOLE_HOOK_ACCESS: - hooks->access = proc; - break; - case ICONSOLE_HOOK_PRE_ACTION: - hooks->pre = proc; - break; - case ICONSOLE_HOOK_POST_ACTION: - hooks->post = proc; - break; - default: NOT_REACHED(); - } -} - -/** - * Handle any special hook triggers. If the hook type is met check if - * there is a function associated with that and if so, execute it - * @param hooks @IConsoleHooks structure that will be checked - * @param type type of hook, trigger that needs to be activated - * @return true on a successfull execution of the hook command or if there - * is no hook/trigger present at all. False otherwise - */ -static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type) -{ - IConsoleHook *proc = NULL; - if (hooks == NULL) return false; - - switch (type) { - case ICONSOLE_HOOK_ACCESS: - proc = hooks->access; - break; - case ICONSOLE_HOOK_PRE_ACTION: - proc = hooks->pre; - break; - case ICONSOLE_HOOK_POST_ACTION: - proc = hooks->post; - break; - default: NOT_REACHED(); - } - - return (proc == NULL) ? true : proc(); -} - -/** - * Add a hook to a command that will be triggered at certain points - * @param name name of the command that the hook is added to - * @param type type of hook that is added (ACCESS, BEFORE and AFTER change) - * @param proc function called when the hook criteria is met - */ -void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc) -{ - IConsoleCmd *cmd = IConsoleCmdGet(name); - if (cmd == NULL) return; - IConsoleHookAdd(&cmd->hook, type, proc); -} - -/** - * Add a hook to a variable that will be triggered at certain points - * @param name name of the variable that the hook is added to - * @param type type of hook that is added (ACCESS, BEFORE and AFTER change) - * @param proc function called when the hook criteria is met - */ -void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc) -{ - IConsoleVar *var = IConsoleVarGet(name); - if (var == NULL) return; - IConsoleHookAdd(&var->hook, type, proc); -} - -/** - * Perhaps ugly macro, but this saves us the trouble of writing the same function - * three types, just with different variables. Yes, templates would be handy. It was - * either this define or an even more ugly void* magic function - */ -#define IConsoleAddSorted(_base, item_new, IConsoleType, type) \ -{ \ - IConsoleType *item, *item_before; \ - /* first command */ \ - if (_base == NULL) { \ - _base = item_new; \ - return; \ - } \ - \ - item_before = NULL; \ - item = _base; \ - \ - /* BEGIN - Alphabetically insert the commands into the linked list */ \ - while (item != NULL) { \ - int i = strcmp(item->name, item_new->name); \ - if (i == 0) { \ - IConsoleError(type " with this name already exists; insertion aborted"); \ - free(item_new); \ - return; \ - } \ - \ - if (i > 0) break; /* insert at this position */ \ - \ - item_before = item; \ - item = item->next; \ - } \ - \ - if (item_before == NULL) { \ - _base = item_new; \ - } else { \ - item_before->next = item_new; \ - } \ - \ - item_new->next = item; \ - /* END - Alphabetical insert */ \ -} - -/** - * Register a new command to be used in the console - * @param name name of the command that will be used - * @param proc function that will be called upon execution of command - */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc) -{ - char *new_cmd = strdup(name); - IConsoleCmd *item_new = malloc(sizeof(IConsoleCmd)); - - item_new->next = NULL; - item_new->proc = proc; - item_new->name = new_cmd; - - item_new->hook.access = NULL; - item_new->hook.pre = NULL; - item_new->hook.post = NULL; - - IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command"); -} - -/** - * Find the command pointed to by its string - * @param name command to be found - * @return return Cmdstruct of the found command, or NULL on failure - */ -IConsoleCmd *IConsoleCmdGet(const char *name) -{ - IConsoleCmd *item; - - for (item = _iconsole_cmds; item != NULL; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - return NULL; -} - -/** - * Register a an alias for an already existing command in the console - * @param name name of the alias that will be used - * @param cmd name of the command that 'name' will be alias of - */ -void IConsoleAliasRegister(const char *name, const char *cmd) -{ - char *new_alias = strdup(name); - char *cmd_aliased = strdup(cmd); - IConsoleAlias *item_new = malloc(sizeof(IConsoleAlias)); - - item_new->next = NULL; - item_new->cmdline = cmd_aliased; - item_new->name = new_alias; - - IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias"); -} - -/** - * Find the alias pointed to by its string - * @param name alias to be found - * @return return Aliasstruct of the found alias, or NULL on failure - */ -IConsoleAlias *IConsoleAliasGet(const char *name) -{ - IConsoleAlias* item; - - for (item = _iconsole_aliases; item != NULL; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - - return NULL; -} - -/** copy in an argument into the aliasstream */ -static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos) -{ - int len = min(ICON_MAX_STREAMSIZE - bufpos, (uint)strlen(src)); - strncpy(dst, src, len); - - return len; -} - -/** - * An alias is just another name for a command, or for more commands - * Execute it as well. - * @param *alias is the alias of the command - * @param tokencount the number of parameters passed - * @param *tokens are the parameters given to the original command (0 is the first param) - */ -static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT]) -{ - const char *cmdptr; - char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE]; - uint i; - uint a_index, astream_i; - - memset(&aliases, 0, sizeof(aliases)); - memset(&aliasstream, 0, sizeof(aliasstream)); - - if (_stdlib_con_developer) - IConsolePrintF(_icolour_dbg, "condbg: requested command is an alias; parsing..."); - - aliases[0] = aliasstream; - for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) { - if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break; - - switch (*cmdptr) { - case '\'': /* ' will double for "" */ - aliasstream[astream_i++] = '"'; - break; - case ';': /* Cmd seperator, start new command */ - aliasstream[astream_i] = '\0'; - aliases[++a_index] = &aliasstream[++astream_i]; - cmdptr++; - break; - case '%': /* Some or all parameters */ - cmdptr++; - switch (*cmdptr) { - case '+': { /* All parameters seperated: "[param 1]" "[param 2]" */ - for (i = 0; i != tokencount; i++) { - aliasstream[astream_i++] = '"'; - astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i); - aliasstream[astream_i++] = '"'; - aliasstream[astream_i++] = ' '; - } - } break; - case '!': { /* Merge the parameters to one: "[param 1] [param 2] [param 3...]" */ - aliasstream[astream_i++] = '"'; - for (i = 0; i != tokencount; i++) { - astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i); - aliasstream[astream_i++] = ' '; - } - aliasstream[astream_i++] = '"'; - - } break; - default: { /* One specific parameter: %A = [param 1] %B = [param 2] ... */ - int param = *cmdptr - 'A'; - - if (param < 0 || param >= tokencount) { - IConsoleError("too many or wrong amount of parameters passed to alias, aborting"); - IConsolePrintF(_icolour_warn, "Usage of alias '%s': %s", alias->name, alias->cmdline); - return; - } - - aliasstream[astream_i++] = '"'; - astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i); - aliasstream[astream_i++] = '"'; - } break; - } break; - - default: - aliasstream[astream_i++] = *cmdptr; - break; - } - } - - for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn -} - -/** - * Special function for adding string-type variables. They in addition - * also need a 'size' value saying how long their string buffer is. - * @param size the length of the string buffer - * For more information see @IConsoleVarRegister() - */ -void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help) -{ - IConsoleVar *var; - IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help); - var = IConsoleVarGet(name); - var->size = size; -} - -/** - * Register a new variable to be used in the console - * @param name name of the variable that will be used - * @param addr memory location the variable will point to - * @param help the help string shown for the variable - * @param type the type of the variable (simple atomic) so we know which values it can get - */ -void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help) -{ - char *new_cmd = strdup(name); - IConsoleVar *item_new = malloc(sizeof(IConsoleVar)); - - item_new->help = (help != NULL) ? strdup(help) : NULL; - - item_new->next = NULL; - item_new->name = new_cmd; - item_new->addr = addr; - item_new->proc = NULL; - item_new->type = type; - - item_new->hook.access = NULL; - item_new->hook.pre = NULL; - item_new->hook.post = NULL; - - IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable"); -} - -/** - * Find the variable pointed to by its string - * @param name variable to be found - * @return return Varstruct of the found variable, or NULL on failure - */ -IConsoleVar *IConsoleVarGet(const char *name) -{ - IConsoleVar *item; - for (item = _iconsole_vars; item != NULL; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - - return NULL; -} - -/** - * Set a new value to a console variable - * @param *var the variable being set/changed - * @param value the new value given to the variable, cast properly - */ -static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value) -{ - IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION); - switch (var->type) { - case ICONSOLE_VAR_BOOLEAN: - *(bool*)var->addr = (value != 0); - break; - case ICONSOLE_VAR_BYTE: - *(byte*)var->addr = (byte)value; - break; - case ICONSOLE_VAR_UINT16: - *(uint16*)var->addr = (uint16)value; - break; - case ICONSOLE_VAR_INT16: - *(int16*)var->addr = (int16)value; - break; - case ICONSOLE_VAR_UINT32: - *(uint32*)var->addr = (uint32)value; - break; - case ICONSOLE_VAR_INT32: - *(int32*)var->addr = (int32)value; - break; - default: NOT_REACHED(); - } - - IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION); - IConsoleVarPrintSetValue(var); -} - -/** - * Set a new value to a string-type variable. Basically this - * means to copy the new value over to the container. - * @param *var the variable in question - * @param *value the new value - */ -static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value) -{ - if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return; - - IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION); - ttd_strlcpy(var->addr, value, var->size); - IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION); - IConsoleVarPrintSetValue(var); // print out the new value, giving feedback - return; -} - -/** - * Query the current value of a variable and return it - * @param *var the variable queried - * @return current value of the variable - */ -static uint32 IConsoleVarGetValue(const IConsoleVar *var) -{ - uint32 result = 0; - - switch (var->type) { - case ICONSOLE_VAR_BOOLEAN: - result = *(bool*)var->addr; - break; - case ICONSOLE_VAR_BYTE: - result = *(byte*)var->addr; - break; - case ICONSOLE_VAR_UINT16: - result = *(uint16*)var->addr; - break; - case ICONSOLE_VAR_INT16: - result = *(int16*)var->addr; - break; - case ICONSOLE_VAR_UINT32: - result = *(uint32*)var->addr; - break; - case ICONSOLE_VAR_INT32: - result = *(int32*)var->addr; - break; - default: NOT_REACHED(); - } - return result; -} - -/** - * Get the value of the variable and put it into a printable - * string form so we can use it for printing - */ -static char *IConsoleVarGetStringValue(const IConsoleVar *var) -{ - static char tempres[50]; - char *value = tempres; - - switch (var->type) { - case ICONSOLE_VAR_BOOLEAN: - snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off"); - break; - case ICONSOLE_VAR_BYTE: - snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr); - break; - case ICONSOLE_VAR_UINT16: - snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr); - break; - case ICONSOLE_VAR_UINT32: - snprintf(tempres, sizeof(tempres), "%u", *(uint32*)var->addr); - break; - case ICONSOLE_VAR_INT16: - snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr); - break; - case ICONSOLE_VAR_INT32: - snprintf(tempres, sizeof(tempres), "%i", *(int32*)var->addr); - break; - case ICONSOLE_VAR_STRING: - value = (char*)var->addr; - break; - default: NOT_REACHED(); - } - - return value; -} - -/** - * Print out the value of the variable when asked - */ -void IConsoleVarPrintGetValue(const IConsoleVar *var) -{ - char *value; - /* Some variables need really specific handling, handle this in its - * callback function */ - if (var->proc != NULL) { - var->proc(0, NULL); - return; - } - - value = IConsoleVarGetStringValue(var); - IConsolePrintF(_icolour_warn, "Current value for '%s' is: %s", var->name, value); -} - -/** - * Print out the value of the variable after it has been assigned - * a new value, thus giving us feedback on the action - */ -void IConsoleVarPrintSetValue(const IConsoleVar *var) -{ - char *value = IConsoleVarGetStringValue(var); - IConsolePrintF(_icolour_warn, "'%s' changed to: %s", var->name, value); -} - -/** - * Execute a variable command. Without any parameters, print out its value - * with parameters it assigns a new value to the variable - * @param *var the variable that we will be querying/changing - * @param tokencount how many additional parameters have been given to the commandline - * @param *token the actual parameters the variable was called with - */ -void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT]) -{ - const char *tokenptr = token[0]; - byte t_index = tokencount; - uint32 value; - - if (_stdlib_con_developer) - IConsolePrintF(_icolour_dbg, "condbg: requested command is a variable"); - - if (tokencount == 0) { /* Just print out value */ - IConsoleVarPrintGetValue(var); - return; - } - - /* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */ - if (strcmp(tokenptr, "=") == 0) tokencount--; - - if (tokencount == 1) { - /* Some variables need really special handling, handle it in their callback procedure */ - if (var->proc != NULL) { - var->proc(tokencount, &token[t_index - tokencount]); // set the new value - return; - } - /* Strings need special processing. No need to convert the argument to - * an integer value, just copy over the argument on a one-by-one basis */ - if (var->type == ICONSOLE_VAR_STRING) { - IConsoleVarSetStringvalue(var, token[t_index - tokencount]); - return; - } else if (GetArgumentInteger(&value, token[t_index - tokencount])) { - IConsoleVarSetValue(var, value); - return; - } - - /* Increase or decrease the value by one. This of course can only happen to 'number' types */ - if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) { - IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1); - return; - } - - if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) { - IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1); - return; - } - } - - IConsoleError("invalid variable assignment"); -} - -/** - * Add a callback function to the variable. Some variables need - * very special processing, which can only be done with custom code - * @param name name of the variable the callback function is added to - * @param proc the function called - */ -void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc) -{ - IConsoleVar *var = IConsoleVarGet(name); - if (var == NULL) return; - var->proc = proc; -} - -/** - * Execute a given command passed to us. First chop it up into - * individual tokens (seperated by spaces), then execute it if possible - * @param cmdstr string to be parsed and executed - */ -void IConsoleCmdExec(const char *cmdstr) -{ - IConsoleCmd *cmd = NULL; - IConsoleAlias *alias = NULL; - IConsoleVar *var = NULL; - - const char *cmdptr; - char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE]; - uint t_index, tstream_i; - - bool longtoken = false; - bool foundtoken = false; - - if (cmdstr[0] == '#') return; // comments - - for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) { - if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) { - IConsoleError("command contains malformed characters, aborting"); - IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr); - return; - } - } - - if (_stdlib_con_developer) - IConsolePrintF(_icolour_dbg, "condbg: executing cmdline: '%s'", cmdstr); - - memset(&tokens, 0, sizeof(tokens)); - memset(&tokenstream, 0, sizeof(tokenstream)); - - /* 1. Split up commandline into tokens, seperated by spaces, commands - * enclosed in "" are taken as one token. We can only go as far as the amount - * of characters in our stream or the max amount of tokens we can handle */ - for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) { - if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break; - - switch (*cmdptr) { - case ' ': /* Token seperator */ - if (!foundtoken) break; - - if (longtoken) { - tokenstream[tstream_i] = *cmdptr; - } else { - tokenstream[tstream_i] = '\0'; - foundtoken = false; - } - - tstream_i++; - break; - case '"': /* Tokens enclosed in "" are one token */ - longtoken = !longtoken; - break; - case '\\': /* Escape character for "" */ - if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) { - tokenstream[tstream_i++] = *++cmdptr; - break; - } - /* fallthrough */ - default: /* Normal character */ - tokenstream[tstream_i++] = *cmdptr; - - if (!foundtoken) { - tokens[t_index++] = &tokenstream[tstream_i - 1]; - foundtoken = true; - } - break; - } - } - - if (_stdlib_con_developer) { - uint i; - - for (i = 0; tokens[i] != NULL; i++) { - IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]); - } - } - - if (tokens[0] == '\0') return; // don't execute empty commands - /* 2. Determine type of command (cmd, alias or variable) and execute - * First try commands, then aliases, and finally variables. Execute - * the found action taking into account its hooking code - */ - cmd = IConsoleCmdGet(tokens[0]); - if (cmd != NULL) { - if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) { - IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION); - if (cmd->proc(t_index, tokens)) { // index started with 0 - IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION); - } else { - cmd->proc(0, NULL); // if command failed, give help - } - } - return; - } - - t_index--; // ignore the variable-name for comfort for both aliases and variaables - alias = IConsoleAliasGet(tokens[0]); - if (alias != NULL) { - IConsoleAliasExec(alias, t_index, &tokens[1]); - return; - } - - var = IConsoleVarGet(tokens[0]); - if (var != NULL) { - if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) { - IConsoleVarExec(var, t_index, &tokens[1]); - } - return; - } - - IConsoleError("command or variable not found"); -} diff --git a/console.h b/console.h deleted file mode 100644 --- a/console.h +++ /dev/null @@ -1,160 +0,0 @@ -/* $Id$ */ - -#ifndef CONSOLE_H -#define CONSOLE_H - -// maximum length of a typed in command -#define ICON_CMDLN_SIZE 255 -// maximum length of a totally expanded command -#define ICON_MAX_STREAMSIZE 1024 - -typedef enum IConsoleVarTypes { - ICONSOLE_VAR_BOOLEAN, - ICONSOLE_VAR_BYTE, - ICONSOLE_VAR_UINT16, - ICONSOLE_VAR_UINT32, - ICONSOLE_VAR_INT16, - ICONSOLE_VAR_INT32, - ICONSOLE_VAR_STRING -} IConsoleVarTypes; - -typedef enum IConsoleModes { - ICONSOLE_FULL, - ICONSOLE_OPENED, - ICONSOLE_CLOSED -} IConsoleModes; - -typedef enum IConsoleHookTypes { - ICONSOLE_HOOK_ACCESS, - ICONSOLE_HOOK_PRE_ACTION, - ICONSOLE_HOOK_POST_ACTION -} IConsoleHookTypes; - -/** --Hooks-- - * Hooks are certain triggers get get accessed/executed on either - * access, before execution/change or after execution/change. This allows - * for general flow of permissions or special action needed in some cases - */ -typedef bool IConsoleHook(void); -typedef struct IConsoleHooks{ - IConsoleHook *access; // trigger when accessing the variable/command - IConsoleHook *pre; // trigger before the variable/command is changed/executed - IConsoleHook *post; // trigger after the variable/command is changed/executed -} IConsoleHooks; - -/** --Commands-- - * Commands are commands, or functions. They get executed once and any - * effect they produce are carried out. The arguments to the commands - * are given to them, each input word seperated by a double-quote (") is an argument - * If you want to handle multiple words as one, enclose them in double-quotes - * eg. 'say "hello sexy boy"' - */ -typedef bool (IConsoleCmdProc)(byte argc, char *argv[]); - -struct IConsoleCmd; -typedef struct IConsoleCmd { - char *name; // name of command - struct IConsoleCmd *next; // next command in list - - IConsoleCmdProc *proc; // process executed when command is typed - IConsoleHooks hook; // any special trigger action that needs executing -} IConsoleCmd; - -/** --Variables-- - * Variables are pointers to real ingame variables which allow for - * changing while ingame. After changing they keep their new value - * and can be used for debugging, gameplay, etc. It accepts: - * - no arguments; just print out current value - * - '= ' to assign a new value to the variable - * - '++' to increase value by one - * - '--' to decrease value by one - */ -struct IConsoleVar; -typedef struct IConsoleVar { - char *name; // name of the variable - struct IConsoleVar *next; // next variable in list - - void *addr; // the address where the variable is pointing at - uint32 size; // size of the variable, used for strings - char *help; // the optional help string shown when requesting information - IConsoleVarTypes type; // type of variable (for correct assignment/output) - IConsoleCmdProc *proc; // some variables need really special handling, use a callback function for that - IConsoleHooks hook; // any special trigger action that needs executing -} IConsoleVar; - -/** --Aliases-- - * Aliases are like shortcuts for complex functions, variable assignments, - * etc. You can use a simple alias to rename a longer command (eg 'lv' for - * 'list_vars' for example), or concatenate more commands into one - * (eg. 'ng' for 'load %A; unpause; debug_level 5'). Aliases can parse the arguments - * given to them in the command line. - * - "%A - %Z" substitute arguments 1 t/m 26 - * - "%+" lists all parameters keeping them seperated - * - "%!" also lists all parameters but presenting them to the aliased command as one argument - * - ";" allows for combining commands (see example 'ng') - */ -struct IConsoleAlias; -typedef struct IConsoleAlias { - char *name; // name of the alias - struct IConsoleAlias *next; // next alias in list - - char *cmdline; // command(s) that is/are being aliased -} IConsoleAlias; - -/* console parser */ -VARDEF IConsoleCmd *_iconsole_cmds; // list of registred commands -VARDEF IConsoleVar *_iconsole_vars; // list of registred vars -VARDEF IConsoleAlias *_iconsole_aliases; // list of registred aliases - -/* console colors/modes */ -VARDEF byte _icolour_def; -VARDEF byte _icolour_err; -VARDEF byte _icolour_warn; -VARDEF byte _icolour_dbg; -VARDEF byte _icolour_cmd; -VARDEF IConsoleModes _iconsole_mode; - -/* console functions */ -void IConsoleInit(void); -void IConsoleFree(void); -void IConsoleClearBuffer(void); -void IConsoleResize(Window *w); -void IConsoleSwitch(void); -void IConsoleClose(void); -void IConsoleOpen(void); - -/* console output */ -void IConsolePrint(uint16 color_code, const char *string); -void CDECL IConsolePrintF(uint16 color_code, const char *s, ...); -void IConsoleDebug(const char *dbg, const char *string); -void IConsoleWarning(const char *string); -void IConsoleError(const char *string); - -/* Commands */ -void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc); -void IConsoleAliasRegister(const char *name, const char *cmd); -IConsoleCmd *IConsoleCmdGet(const char *name); -IConsoleAlias *IConsoleAliasGet(const char *name); - -/* Variables */ -void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help); -void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help); -IConsoleVar* IConsoleVarGet(const char *name); -void IConsoleVarPrintGetValue(const IConsoleVar *var); -void IConsoleVarPrintSetValue(const IConsoleVar *var); - -/* Parser */ -void IConsoleCmdExec(const char *cmdstr); -void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[]); - -/* console std lib (register ingame commands/aliases/variables) */ -void IConsoleStdLibRegister(void); - -/* Hooking code */ -void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc); -void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc); -void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc); - -/* Supporting functions */ -bool GetArgumentInteger(uint32 *value, const char *arg); -#endif /* CONSOLE_H */ diff --git a/console_cmds.c b/console_cmds.c deleted file mode 100644 --- a/console_cmds.c +++ /dev/null @@ -1,1608 +0,0 @@ -/* $Id$ */ - -#include "stdafx.h" -#include "openttd.h" -#include "console.h" -#include "debug.h" -#include "engine.h" -#include "functions.h" -#include "saveload.h" -#include "string.h" -#include "variables.h" -#include "network/network_data.h" -#include "network/network_client.h" -#include "network/network_server.h" -#include "network/network_udp.h" -#include "command.h" -#include "settings.h" -#include "fios.h" -#include "vehicle.h" -#include "station.h" -#include "strings.h" -#include "screenshot.h" -#include "genworld.h" -#include "date.h" -#include "network/network.h" - -// ** scriptfile handling ** // -static FILE *_script_file; -static bool _script_running; - -// ** console command / variable defines ** // -#define DEF_CONSOLE_CMD(function) static bool function(byte argc, char *argv[]) -#define DEF_CONSOLE_HOOK(function) static bool function(void) - - -/* **************************** */ -/* variable and command hooks */ -/* **************************** */ - -#ifdef ENABLE_NETWORK - -static inline bool NetworkAvailable(void) -{ - if (!_network_available) { - IConsoleError("You cannot use this command because there is no network available."); - return false; - } - return true; -} - -DEF_CONSOLE_HOOK(ConHookServerOnly) -{ - if (!NetworkAvailable()) return false; - - if (!_network_server) { - IConsoleError("This command/variable is only available to a network server."); - return false; - } - return true; -} - -DEF_CONSOLE_HOOK(ConHookClientOnly) -{ - if (!NetworkAvailable()) return false; - - if (_network_server) { - IConsoleError("This command/variable is not available to a network server."); - return false; - } - return true; -} - -DEF_CONSOLE_HOOK(ConHookNeedNetwork) -{ - if (!NetworkAvailable()) return false; - - if (!_networking) { - IConsoleError("Not connected. This command/variable is only available in multiplayer."); - return false; - } - return true; -} - -DEF_CONSOLE_HOOK(ConHookNoNetwork) -{ - if (_networking) { - IConsoleError("This command/variable is forbidden in multiplayer."); - return false; - } - return true; -} - -#endif /* ENABLE_NETWORK */ - -static void IConsoleHelp(const char *str) -{ - IConsolePrintF(_icolour_warn, "- %s", str); -} - -DEF_CONSOLE_CMD(ConResetEngines) -{ - if (argc == 0) { - IConsoleHelp("Reset status data of all engines. This might solve some issues with 'lost' engines. Usage: 'resetengines'"); - return true; - } - - StartupEngines(); - return true; -} - -#ifdef _DEBUG -DEF_CONSOLE_CMD(ConResetTile) -{ - if (argc == 0) { - IConsoleHelp("Reset a tile to bare land. Usage: 'resettile '"); - IConsoleHelp("Tile can be either decimal (34161) or hexadecimal (0x4a5B)"); - return true; - } - - if (argc == 2) { - uint32 result; - if (GetArgumentInteger(&result, argv[1])) { - DoClearSquare((TileIndex)result); - return true; - } - } - - return false; -} - -DEF_CONSOLE_CMD(ConStopAllVehicles) -{ - Vehicle* v; - if (argc == 0) { - IConsoleHelp("Stops all vehicles in the game. For debugging only! Use at your own risk... Usage: 'stopall'"); - return true; - } - - FOR_ALL_VEHICLES(v) { - /* Code ripped from CmdStartStopTrain. Can't call it, because of - * ownership problems, so we'll duplicate some code, for now */ - if (v->type == VEH_Train) - v->u.rail.days_since_order_progr = 0; - v->vehstatus |= VS_STOPPED; - InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); - InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); - } - return true; -} -#endif /* _DEBUG */ - -DEF_CONSOLE_CMD(ConScrollToTile) -{ - if (argc == 0) { - IConsoleHelp("Center the screen on a given tile. Usage: 'scrollto '"); - IConsoleHelp("Tile can be either decimal (34161) or hexadecimal (0x4a5B)"); - return true; - } - - if (argc == 2) { - uint32 result; - if (GetArgumentInteger(&result, argv[1])) { - if (result >= MapSize()) { - IConsolePrint(_icolour_err, "Tile does not exist"); - return true; - } - ScrollMainWindowToTile((TileIndex)result); - return true; - } - } - - return false; -} - -extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm); -extern void BuildFileList(void); -extern void SetFiosType(const byte fiostype); - -/* Save the map to a file */ -DEF_CONSOLE_CMD(ConSave) -{ - if (argc == 0) { - IConsoleHelp("Save the current game. Usage: 'save '"); - return true; - } - - if (argc == 2) { - char buf[200]; - - snprintf(buf, lengthof(buf), "%s%s%s.sav", _paths.save_dir, PATHSEP, argv[1]); - IConsolePrint(_icolour_def, "Saving map..."); - - if (SaveOrLoad(buf, SL_SAVE) != SL_OK) { - IConsolePrint(_icolour_err, "SaveMap failed"); - } else { - IConsolePrintF(_icolour_def, "Map sucessfully saved to %s", buf); - } - return true; - } - - return false; -} - -static const FiosItem* GetFiosItem(const char* file) -{ - int i; - - _saveload_mode = SLD_LOAD_GAME; - BuildFileList(); - - for (i = 0; i < _fios_num; i++) { - if (strcmp(file, _fios_list[i].name) == 0) break; - if (strcmp(file, _fios_list[i].title) == 0) break; - } - - if (i == _fios_num) { /* If no name matches, try to parse it as number */ - char* endptr; - - i = strtol(file, &endptr, 10); - if (file == endptr || *endptr != '\0') i = -1; - } - - return IS_INT_INSIDE(i, 0, _fios_num) ? &_fios_list[i] : NULL; -} - - -DEF_CONSOLE_CMD(ConLoad) -{ - const FiosItem *item; - const char *file; - - if (argc == 0) { - IConsoleHelp("Load a game by name or index. Usage: 'load '"); - return true; - } - - if (argc != 2) return false; - - file = argv[1]; - item = GetFiosItem(file); - if (item != NULL) { - switch (item->type) { - case FIOS_TYPE_FILE: case FIOS_TYPE_OLDFILE: { - _switch_mode = SM_LOAD; - SetFiosType(item->type); - - ttd_strlcpy(_file_to_saveload.name, FiosBrowseTo(item), sizeof(_file_to_saveload.name)); - ttd_strlcpy(_file_to_saveload.title, item->title, sizeof(_file_to_saveload.title)); - } break; - default: IConsolePrintF(_icolour_err, "%s: Not a savegame.", file); - } - } else { - IConsolePrintF(_icolour_err, "%s: No such file or directory.", file); - } - - FiosFreeSavegameList(); - return true; -} - - -DEF_CONSOLE_CMD(ConRemove) -{ - const FiosItem* item; - const char* file; - - if (argc == 0) { - IConsoleHelp("Remove a savegame by name or index. Usage: 'rm '"); - return true; - } - - if (argc != 2) return false; - - file = argv[1]; - item = GetFiosItem(file); - if (item != NULL) { - if (!FiosDelete(item->name)) - IConsolePrintF(_icolour_err, "%s: Failed to delete file", file); - } else { - IConsolePrintF(_icolour_err, "%s: No such file or directory.", file); - } - - FiosFreeSavegameList(); - return true; -} - - -/* List all the files in the current dir via console */ -DEF_CONSOLE_CMD(ConListFiles) -{ - int i; - - if (argc == 0) { - IConsoleHelp("List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'"); - return true; - } - - BuildFileList(); - - for (i = 0; i < _fios_num; i++) { - const FiosItem *item = &_fios_list[i]; - IConsolePrintF(_icolour_def, "%d) %s", i, item->title); - } - - FiosFreeSavegameList(); - return true; -} - -/* Change the dir via console */ -DEF_CONSOLE_CMD(ConChangeDirectory) -{ - const FiosItem *item; - const char *file; - - if (argc == 0) { - IConsoleHelp("Change the dir via console. Usage: 'cd '"); - return true; - } - - if (argc != 2) return false; - - file = argv[1]; - item = GetFiosItem(file); - if (item != NULL) { - switch (item->type) { - case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT: - FiosBrowseTo(item); - break; - default: IConsolePrintF(_icolour_err, "%s: Not a directory.", file); - } - } else { - IConsolePrintF(_icolour_err, "%s: No such file or directory.", file); - } - - FiosFreeSavegameList(); - return true; -} - -DEF_CONSOLE_CMD(ConPrintWorkingDirectory) -{ - const char *path; - - if (argc == 0) { - IConsoleHelp("Print out the current working directory. Usage: 'pwd'"); - return true; - } - - // XXX - Workaround for broken file handling - FiosGetSavegameList(SLD_LOAD_GAME); - FiosFreeSavegameList(); - - FiosGetDescText(&path, NULL); - IConsolePrint(_icolour_def, path); - return true; -} - -DEF_CONSOLE_CMD(ConClearBuffer) -{ - if (argc == 0) { - IConsoleHelp("Clear the console buffer. Usage: 'clear'"); - return true; - } - - IConsoleClearBuffer(); - InvalidateWindow(WC_CONSOLE, 0); - return true; -} - - -// ********************************* // -// * Network Core Console Commands * // -// ********************************* // -#ifdef ENABLE_NETWORK - -DEF_CONSOLE_CMD(ConBan) -{ - NetworkClientInfo *ci; - const char *banip = NULL; - uint32 index; - - if (argc == 0) { - IConsoleHelp("Ban a player from a network game. Usage: 'ban '"); - IConsoleHelp("For client-id's, see the command 'clients'"); - IConsoleHelp("If the client is no longer online, you can still ban his/her IP"); - return true; - } - - if (argc != 2) return false; - - if (strchr(argv[1], '.') == NULL) { // banning with ID - index = atoi(argv[1]); - ci = NetworkFindClientInfoFromIndex(index); - } else { // banning IP - ci = NetworkFindClientInfoFromIP(argv[1]); - if (ci == NULL) { - banip = argv[1]; - index = (uint32)-1; - } else { - index = ci->client_index; - } - } - - if (index == NETWORK_SERVER_INDEX) { - IConsoleError("Silly boy, you can not ban yourself!"); - return true; - } - - if (index == 0 || (ci == NULL && index != (uint32)-1)) { - IConsoleError("Invalid client"); - return true; - } - - if (ci != NULL) { - banip = inet_ntoa(*(struct in_addr *)&ci->client_ip); - SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED); - IConsolePrint(_icolour_def, "Client banned"); - } else { - IConsolePrint(_icolour_def, "Client not online, banned IP"); - } - - /* Add user to ban-list */ - for (index = 0; index < lengthof(_network_ban_list); index++) { - if (_network_ban_list[index] == NULL) { - _network_ban_list[index] = strdup(banip); - break; - } - } - - return true; -} - -DEF_CONSOLE_CMD(ConUnBan) -{ - uint i, index; - - if (argc == 0) { - IConsoleHelp("Unban a player from a network game. Usage: 'unban '"); - IConsoleHelp("For a list of banned IP's, see the command 'banlist'"); - return true; - } - - if (argc != 2) return false; - - index = (strchr(argv[1], '.') == NULL) ? atoi(argv[1]) : 0; - index--; - - for (i = 0; i < lengthof(_network_ban_list); i++) { - if (_network_ban_list[i] == NULL) continue; - - if (strcmp(_network_ban_list[i], argv[1]) == 0 || index == i) { - free(_network_ban_list[i]); - _network_ban_list[i] = NULL; - IConsolePrint(_icolour_def, "IP unbanned."); - return true; - } - } - - IConsolePrint(_icolour_def, "IP not in ban-list."); - return true; -} - -DEF_CONSOLE_CMD(ConBanList) -{ - uint i; - - if (argc == 0) { - IConsoleHelp("List the IP's of banned clients: Usage 'banlist'"); - return true; - } - - IConsolePrint(_icolour_def, "Banlist: "); - - for (i = 0; i < lengthof(_network_ban_list); i++) { - if (_network_ban_list[i] != NULL) - IConsolePrintF(_icolour_def, " %d) %s", i + 1, _network_ban_list[i]); - } - - return true; -} - -DEF_CONSOLE_CMD(ConPauseGame) -{ - if (argc == 0) { - IConsoleHelp("Pause a network game. Usage: 'pause'"); - return true; - } - - if (_pause == 0) { - DoCommandP(0, 1, 0, NULL, CMD_PAUSE); - IConsolePrint(_icolour_def, "Game paused."); - } else { - IConsolePrint(_icolour_def, "Game is already paused."); - } - - return true; -} - -DEF_CONSOLE_CMD(ConUnPauseGame) -{ - if (argc == 0) { - IConsoleHelp("Unpause a network game. Usage: 'unpause'"); - return true; - } - - if (_pause != 0) { - DoCommandP(0, 0, 0, NULL, CMD_PAUSE); - IConsolePrint(_icolour_def, "Game unpaused."); - } else { - IConsolePrint(_icolour_def, "Game is already unpaused."); - } - - return true; -} - -DEF_CONSOLE_CMD(ConRcon) -{ - if (argc == 0) { - IConsoleHelp("Remote control the server from another client. Usage: 'rcon '"); - IConsoleHelp("Remember to enclose the command in quotes, otherwise only the first parameter is sent"); - return true; - } - - if (argc < 3) return false; - - SEND_COMMAND(PACKET_CLIENT_RCON)(argv[1], argv[2]); - return true; -} - -DEF_CONSOLE_CMD(ConStatus) -{ - static const char* const stat_str[] = { - "inactive", - "authorized", - "waiting", - "loading map", - "map done", - "ready", - "active" - }; - - const NetworkClientState *cs; - - if (argc == 0) { - IConsoleHelp("List the status of all clients connected to the server. Usage 'status'"); - return true; - } - - FOR_ALL_CLIENTS(cs) { - int lag = NetworkCalculateLag(cs); - const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); - const char* status; - - status = (cs->status < lengthof(stat_str) ? stat_str[cs->status] : "unknown"); - IConsolePrintF(8, "Client #%1d name: '%s' status: '%s' frame-lag: %3d company: %1d IP: %s unique-id: '%s'", - cs->index, ci->client_name, status, lag, - ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0), - GetPlayerIP(ci), ci->unique_id); - } - - return true; -} - -DEF_CONSOLE_CMD(ConServerInfo) -{ - const NetworkGameInfo *gi; - - if (argc == 0) { - IConsoleHelp("List current and maximum client/player limits. Usage 'server_info'"); - IConsoleHelp("You can change these values by setting the variables 'max_clients', 'max_companies' and 'max_spectators'"); - return true; - } - - gi = &_network_game_info; - IConsolePrintF(_icolour_def, "Current/maximum clients: %2d/%2d", gi->clients_on, gi->clients_max); - IConsolePrintF(_icolour_def, "Current/maximum companies: %2d/%2d", ActivePlayerCount(), gi->companies_max); - IConsolePrintF(_icolour_def, "Current/maximum spectators: %2d/%2d", NetworkSpectatorCount(), gi->spectators_max); - - return true; -} - -DEF_CONSOLE_HOOK(ConHookValidateMaxClientsCount) -{ - /* XXX - hardcoded, string limiation -- TrueLight - * XXX - also see network.c:NetworkStartup ~1356 */ - if (_network_game_info.clients_max > 10) { - _network_game_info.clients_max = 10; - IConsoleError("Maximum clients out of bounds, truncating to limit."); - } - - return true; -} - -DEF_CONSOLE_HOOK(ConHookValidateMaxCompaniesCount) -{ - if (_network_game_info.companies_max > MAX_PLAYERS) { - _network_game_info.companies_max = MAX_PLAYERS; - IConsoleError("Maximum companies out of bounds, truncating to limit."); - } - - return true; -} - -DEF_CONSOLE_HOOK(ConHookValidateMaxSpectatorsCount) -{ - /* XXX @see ConHookValidateMaxClientsCount */ - if (_network_game_info.spectators_max > 10) { - _network_game_info.spectators_max = 10; - IConsoleError("Maximum spectators out of bounds, truncating to limit."); - } - - return true; -} - -DEF_CONSOLE_HOOK(ConHookCheckMinPlayers) -{ - CheckMinPlayers(); - return true; -} - -DEF_CONSOLE_CMD(ConKick) -{ - NetworkClientInfo *ci; - uint32 index; - - if (argc == 0) { - IConsoleHelp("Kick a player from a network game. Usage: 'kick '"); - IConsoleHelp("For client-id's, see the command 'clients'"); - return true; - } - - if (argc != 2) return false; - - if (strchr(argv[1], '.') == NULL) { - index = atoi(argv[1]); - ci = NetworkFindClientInfoFromIndex(index); - } else { - ci = NetworkFindClientInfoFromIP(argv[1]); - index = (ci == NULL) ? 0 : ci->client_index; - } - - if (index == NETWORK_SERVER_INDEX) { - IConsoleError("Silly boy, you can not kick yourself!"); - return true; - } - - if (index == 0) { - IConsoleError("Invalid client"); - return true; - } - - if (ci != NULL) { - SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED); - } else { - IConsoleError("Client not found"); - } - - return true; -} - -DEF_CONSOLE_CMD(ConResetCompany) -{ - const Player *p; - const NetworkClientState *cs; - const NetworkClientInfo *ci; - PlayerID index; - - if (argc == 0) { - IConsoleHelp("Remove an idle company from the game. Usage: 'reset_company '"); - IConsoleHelp("For company-id's, see the list of companies from the dropdown menu. Player 1 is 1, etc."); - return true; - } - - if (argc != 2) return false; - - index = atoi(argv[1]) - 1; - - /* Check valid range */ - if (!IsValidPlayer(index)) { - IConsolePrintF(_icolour_err, "Company does not exist. Company-id must be between 1 and %d.", MAX_PLAYERS); - return true; - } - - /* Check if company does exist */ - p = GetPlayer(index); - if (!p->is_active) { - IConsoleError("Company does not exist."); - return true; - } - - if (p->is_ai) { - IConsoleError("Company is owned by an AI."); - return true; - } - - /* Check if the company has active players */ - FOR_ALL_CLIENTS(cs) { - ci = DEREF_CLIENT_INFO(cs); - if (ci->client_playas == index) { - IConsoleError("Cannot remove company: a client is connected to that company."); - return true; - } - } - ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX); - if (ci->client_playas == index) { - IConsoleError("Cannot remove company: the server is connected to that company."); - return true; - } - - /* It is safe to remove this company */ - DoCommandP(0, 2, index, NULL, CMD_PLAYER_CTRL); - IConsolePrint(_icolour_def, "Company deleted."); - - return true; -} - -DEF_CONSOLE_CMD(ConNetworkClients) -{ - NetworkClientInfo *ci; - - if (argc == 0) { - IConsoleHelp("Get a list of connected clients including their ID, name, company-id, and IP. Usage: 'clients'"); - return true; - } - - FOR_ALL_ACTIVE_CLIENT_INFOS(ci) { - IConsolePrintF(8, "Client #%1d name: '%s' company: %1d IP: %s", - ci->client_index, ci->client_name, - ci->client_playas + (IsValidPlayer(ci->client_playas) ? 1 : 0), - GetPlayerIP(ci)); - } - - return true; -} - -DEF_CONSOLE_CMD(ConNetworkConnect) -{ - char *ip; - const char *port = NULL; - const char *player = NULL; - uint16 rport; - - if (argc == 0) { - IConsoleHelp("Connect to a remote OTTD server and join the game. Usage: 'connect '"); - IConsoleHelp("IP can contain port and player: 'IP[[#Player]:Port]', eg: 'server.ottd.org#2:443'"); - IConsoleHelp("Player #255 is spectator all others are a certain company with Company 1 being #1"); - return true; - } - - if (argc < 2) return false; - if (_networking) NetworkDisconnect(); // we are in network-mode, first close it! - - ip = argv[1]; - /* Default settings: default port and new company */ - rport = NETWORK_DEFAULT_PORT; - _network_playas = PLAYER_NEW_COMPANY; - - ParseConnectionString(&player, &port, ip); - - IConsolePrintF(_icolour_def, "Connecting to %s...", ip); - if (player != NULL) { - _network_playas = atoi(player); - IConsolePrintF(_icolour_def, " player-no: %d", _network_playas); - - /* From a user pov 0 is a new player, internally it's different and all - * players are offset by one to ease up on users (eg players 1-8 not 0-7) */ - if (_network_playas != PLAYER_SPECTATOR) { - _network_playas--; - if (!IsValidPlayer(_network_playas)) return false; - } - } - if (port != NULL) { - rport = atoi(port); - IConsolePrintF(_icolour_def, " port: %s", port); - } - - NetworkClientConnectGame(ip, rport); - - return true; -} - -#endif /* ENABLE_NETWORK */ - -/* ******************************** */ -/* script file console commands */ -/* ******************************** */ - -DEF_CONSOLE_CMD(ConExec) -{ - char cmdline[ICON_CMDLN_SIZE]; - char *cmdptr; - - if (argc == 0) { - IConsoleHelp("Execute a local script file. Usage: 'exec