Changeset - r0:d63b455452f6
[Not reviewed]
master
! ! !
truelight - 20 years ago 2004-08-09 17:04:08
truelight@openttd.org
(svn r1) Import of revision 975 of old (crashed) SVN
10 files changed:
COPYING
340
Makefile
615
ai.c
3943
airport.c
283
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
COPYING
Show inline comments
 
new file 100644
 
		    GNU GENERAL PUBLIC LICENSE
 
		       Version 2, June 1991
 

	
 
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 
                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 Everyone is permitted to copy and distribute verbatim copies
 
 of this license document, but changing it is not allowed.
 

	
 
			    Preamble
 

	
 
  The licenses for most software are designed to take away your
 
freedom to share and change it.  By contrast, the GNU General Public
 
License is intended to guarantee your freedom to share and change free
 
software--to make sure the software is free for all its users.  This
 
General Public License applies to most of the Free Software
 
Foundation's software and to any other program whose authors commit to
 
using it.  (Some other Free Software Foundation software is covered by
 
the GNU Library General Public License instead.)  You can apply it to
 
your programs, too.
 

	
 
  When we speak of free software, we are referring to freedom, not
 
price.  Our General Public Licenses are designed to make sure that you
 
have the freedom to distribute copies of free software (and charge for
 
this service if you wish), that you receive source code or can get it
 
if you want it, that you can change the software or use pieces of it
 
in new free programs; and that you know you can do these things.
 

	
 
  To protect your rights, we need to make restrictions that forbid
 
anyone to deny you these rights or to ask you to surrender the rights.
 
These restrictions translate to certain responsibilities for you if you
 
distribute copies of the software, or if you modify it.
 

	
 
  For example, if you distribute copies of such a program, whether
 
gratis or for a fee, you must give the recipients all the rights that
 
you have.  You must make sure that they, too, receive or can get the
 
source code.  And you must show them these terms so they know their
 
rights.
 

	
 
  We protect your rights with two steps: (1) copyright the software, and
 
(2) offer you this license which gives you legal permission to copy,
 
distribute and/or modify the software.
 

	
 
  Also, for each author's protection and ours, we want to make certain
 
that everyone understands that there is no warranty for this free
 
software.  If the software is modified by someone else and passed on, we
 
want its recipients to know that what they have is not the original, so
 
that any problems introduced by others will not reflect on the original
 
authors' reputations.
 

	
 
  Finally, any free program is threatened constantly by software
 
patents.  We wish to avoid the danger that redistributors of a free
 
program will individually obtain patent licenses, in effect making the
 
program proprietary.  To prevent this, we have made it clear that any
 
patent must be licensed for everyone's free use or not licensed at all.
 

	
 
  The precise terms and conditions for copying, distribution and
 
modification follow.
 

 
		    GNU GENERAL PUBLIC LICENSE
 
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 

	
 
  0. This License applies to any program or other work which contains
 
a notice placed by the copyright holder saying it may be distributed
 
under the terms of this General Public License.  The "Program", below,
 
refers to any such program or work, and a "work based on the Program"
 
means either the Program or any derivative work under copyright law:
 
that is to say, a work containing the Program or a portion of it,
 
either verbatim or with modifications and/or translated into another
 
language.  (Hereinafter, translation is included without limitation in
 
the term "modification".)  Each licensee is addressed as "you".
 

	
 
Activities other than copying, distribution and modification are not
 
covered by this License; they are outside its scope.  The act of
 
running the Program is not restricted, and the output from the Program
 
is covered only if its contents constitute a work based on the
 
Program (independent of having been made by running the Program).
 
Whether that is true depends on what the Program does.
 

	
 
  1. You may copy and distribute verbatim copies of the Program's
 
source code as you receive it, in any medium, provided that you
 
conspicuously and appropriately publish on each copy an appropriate
 
copyright notice and disclaimer of warranty; keep intact all the
 
notices that refer to this License and to the absence of any warranty;
 
and give any other recipients of the Program a copy of this License
 
along with the Program.
 

	
 
You may charge a fee for the physical act of transferring a copy, and
 
you may at your option offer warranty protection in exchange for a fee.
 

	
 
  2. You may modify your copy or copies of the Program or any portion
 
of it, thus forming a work based on the Program, and copy and
 
distribute such modifications or work under the terms of Section 1
 
above, provided that you also meet all of these conditions:
 

	
 
    a) You must cause the modified files to carry prominent notices
 
    stating that you changed the files and the date of any change.
 

	
 
    b) You must cause any work that you distribute or publish, that in
 
    whole or in part contains or is derived from the Program or any
 
    part thereof, to be licensed as a whole at no charge to all third
 
    parties under the terms of this License.
 

	
 
    c) If the modified program normally reads commands interactively
 
    when run, you must cause it, when started running for such
 
    interactive use in the most ordinary way, to print or display an
 
    announcement including an appropriate copyright notice and a
 
    notice that there is no warranty (or else, saying that you provide
 
    a warranty) and that users may redistribute the program under
 
    these conditions, and telling the user how to view a copy of this
 
    License.  (Exception: if the Program itself is interactive but
 
    does not normally print such an announcement, your work based on
 
    the Program is not required to print an announcement.)
 

 
These requirements apply to the modified work as a whole.  If
 
identifiable sections of that work are not derived from the Program,
 
and can be reasonably considered independent and separate works in
 
themselves, then this License, and its terms, do not apply to those
 
sections when you distribute them as separate works.  But when you
 
distribute the same sections as part of a whole which is a work based
 
on the Program, the distribution of the whole must be on the terms of
 
this License, whose permissions for other licensees extend to the
 
entire whole, and thus to each and every part regardless of who wrote it.
 

	
 
Thus, it is not the intent of this section to claim rights or contest
 
your rights to work written entirely by you; rather, the intent is to
 
exercise the right to control the distribution of derivative or
 
collective works based on the Program.
 

	
 
In addition, mere aggregation of another work not based on the Program
 
with the Program (or with a work based on the Program) on a volume of
 
a storage or distribution medium does not bring the other work under
 
the scope of this License.
 

	
 
  3. You may copy and distribute the Program (or a work based on it,
 
under Section 2) in object code or executable form under the terms of
 
Sections 1 and 2 above provided that you also do one of the following:
 

	
 
    a) Accompany it with the complete corresponding machine-readable
 
    source code, which must be distributed under the terms of Sections
 
    1 and 2 above on a medium customarily used for software interchange; or,
 

	
 
    b) Accompany it with a written offer, valid for at least three
 
    years, to give any third party, for a charge no more than your
 
    cost of physically performing source distribution, a complete
 
    machine-readable copy of the corresponding source code, to be
 
    distributed under the terms of Sections 1 and 2 above on a medium
 
    customarily used for software interchange; or,
 

	
 
    c) Accompany it with the information you received as to the offer
 
    to distribute corresponding source code.  (This alternative is
 
    allowed only for noncommercial distribution and only if you
 
    received the program in object code or executable form with such
 
    an offer, in accord with Subsection b above.)
 

	
 
The source code for a work means the preferred form of the work for
 
making modifications to it.  For an executable work, complete source
 
code means all the source code for all modules it contains, plus any
 
associated interface definition files, plus the scripts used to
 
control compilation and installation of the executable.  However, as a
 
special exception, the source code distributed need not include
 
anything that is normally distributed (in either source or binary
 
form) with the major components (compiler, kernel, and so on) of the
 
operating system on which the executable runs, unless that component
 
itself accompanies the executable.
 

	
 
If distribution of executable or object code is made by offering
 
access to copy from a designated place, then offering equivalent
 
access to copy the source code from the same place counts as
 
distribution of the source code, even though third parties are not
 
compelled to copy the source along with the object code.
 

 
  4. You may not copy, modify, sublicense, or distribute the Program
 
except as expressly provided under this License.  Any attempt
 
otherwise to copy, modify, sublicense or distribute the Program is
 
void, and will automatically terminate your rights under this License.
 
However, parties who have received copies, or rights, from you under
 
this License will not have their licenses terminated so long as such
 
parties remain in full compliance.
 

	
 
  5. You are not required to accept this License, since you have not
 
signed it.  However, nothing else grants you permission to modify or
 
distribute the Program or its derivative works.  These actions are
 
prohibited by law if you do not accept this License.  Therefore, by
 
modifying or distributing the Program (or any work based on the
 
Program), you indicate your acceptance of this License to do so, and
 
all its terms and conditions for copying, distributing or modifying
 
the Program or works based on it.
 

	
 
  6. Each time you redistribute the Program (or any work based on the
 
Program), the recipient automatically receives a license from the
 
original licensor to copy, distribute or modify the Program subject to
 
these terms and conditions.  You may not impose any further
 
restrictions on the recipients' exercise of the rights granted herein.
 
You are not responsible for enforcing compliance by third parties to
 
this License.
 

	
 
  7. If, as a consequence of a court judgment or allegation of patent
 
infringement or for any other reason (not limited to patent issues),
 
conditions are imposed on you (whether by court order, agreement or
 
otherwise) that contradict the conditions of this License, they do not
 
excuse you from the conditions of this License.  If you cannot
 
distribute so as to satisfy simultaneously your obligations under this
 
License and any other pertinent obligations, then as a consequence you
 
may not distribute the Program at all.  For example, if a patent
 
license would not permit royalty-free redistribution of the Program by
 
all those who receive copies directly or indirectly through you, then
 
the only way you could satisfy both it and this License would be to
 
refrain entirely from distribution of the Program.
 

	
 
If any portion of this section is held invalid or unenforceable under
 
any particular circumstance, the balance of the section is intended to
 
apply and the section as a whole is intended to apply in other
 
circumstances.
 

	
 
It is not the purpose of this section to induce you to infringe any
 
patents or other property right claims or to contest validity of any
 
such claims; this section has the sole purpose of protecting the
 
integrity of the free software distribution system, which is
 
implemented by public license practices.  Many people have made
 
generous contributions to the wide range of software distributed
 
through that system in reliance on consistent application of that
 
system; it is up to the author/donor to decide if he or she is willing
 
to distribute software through any other system and a licensee cannot
 
impose that choice.
 

	
 
This section is intended to make thoroughly clear what is believed to
 
be a consequence of the rest of this License.
 

 
  8. If the distribution and/or use of the Program is restricted in
 
certain countries either by patents or by copyrighted interfaces, the
 
original copyright holder who places the Program under this License
 
may add an explicit geographical distribution limitation excluding
 
those countries, so that distribution is permitted only in or among
 
countries not thus excluded.  In such case, this License incorporates
 
the limitation as if written in the body of this License.
 

	
 
  9. The Free Software Foundation may publish revised and/or new versions
 
of the General Public License from time to time.  Such new versions will
 
be similar in spirit to the present version, but may differ in detail to
 
address new problems or concerns.
 

	
 
Each version is given a distinguishing version number.  If the Program
 
specifies a version number of this License which applies to it and "any
 
later version", you have the option of following the terms and conditions
 
either of that version or of any later version published by the Free
 
Software Foundation.  If the Program does not specify a version number of
 
this License, you may choose any version ever published by the Free Software
 
Foundation.
 

	
 
  10. If you wish to incorporate parts of the Program into other free
 
programs whose distribution conditions are different, write to the author
 
to ask for permission.  For software which is copyrighted by the Free
 
Software Foundation, write to the Free Software Foundation; we sometimes
 
make exceptions for this.  Our decision will be guided by the two goals
 
of preserving the free status of all derivatives of our free software and
 
of promoting the sharing and reuse of software generally.
 

	
 
			    NO WARRANTY
 

	
 
  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 
REPAIR OR CORRECTION.
 

	
 
  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 
POSSIBILITY OF SUCH DAMAGES.
 

	
 
		     END OF TERMS AND CONDITIONS
 

 
	    How to Apply These Terms to Your New Programs
 

	
 
  If you develop a new program, and you want it to be of the greatest
 
possible use to the public, the best way to achieve this is to make it
 
free software which everyone can redistribute and change under these terms.
 

	
 
  To do so, attach the following notices to the program.  It is safest
 
to attach them to the start of each source file to most effectively
 
convey the exclusion of warranty; and each file should have at least
 
the "copyright" line and a pointer to where the full notice is found.
 

	
 
    <one line to give the program's name and a brief idea of what it does.>
 
    Copyright (C) <year>  <name of author>
 

	
 
    This program is free software; you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation; either version 2 of the License, or
 
    (at your option) any later version.
 

	
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 

	
 
    You should have received a copy of the GNU General Public License
 
    along with this program; if not, write to the Free Software
 
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 

	
 

	
 
Also add information on how to contact you by electronic and paper mail.
 

	
 
If the program is interactive, make it output a short notice like this
 
when it starts in an interactive mode:
 

	
 
    Gnomovision version 69, Copyright (C) year name of author
 
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 
    This is free software, and you are welcome to redistribute it
 
    under certain conditions; type `show c' for details.
 

	
 
The hypothetical commands `show w' and `show c' should show the appropriate
 
parts of the General Public License.  Of course, the commands you use may
 
be called something other than `show w' and `show c'; they could even be
 
mouse-clicks or menu items--whatever suits your program.
 

	
 
You should also get your employer (if you work as a programmer) or your
 
school, if any, to sign a "copyright disclaimer" for the program, if
 
necessary.  Here is a sample; alter the names:
 

	
 
  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 
  `Gnomovision' (which makes passes at compilers) written by James Hacker.
 

	
 
  <signature of Ty Coon>, 1 April 1989
 
  Ty Coon, President of Vice
 

	
 
This General Public License does not permit incorporating your program into
 
proprietary programs.  If your program is a subroutine library, you may
 
consider it more useful to permit linking proprietary applications with the
 
library.  If this is what you want to do, use the GNU Library General
 
Public License instead of this License.
Jamfile.next
Show inline comments
 
new file 100644
 
CFILES = 	ai.c aircraft_cmd.c aircraft_gui.c airport_gui.c
 
	bridge_gui.c clear_cmd.c command.c disaster_cmd.c
 
	dock_gui.c dummy_land.c economy.c engine.c engine_gui.c
 
	fileio.c gfx.c graph_gui.c industry_cmd.c industry_gui.c
 
	intro_gui.c landscape.c main_gui.c minilzo.c misc.c
 
	misc_cmd.c misc_gui.c music_gui.c namegen.c network.c
 
	news_gui.c oldloader.c order_cmd.c order_gui.c pathfind.c
 
	player_gui.c players.c rail_cmd.c rail_gui.c road_cmd.c
 
	road_gui.c roadveh_cmd.c roadveh_gui.c saveload.c sdl.c
 
	settings.c settings_gui.c ship_cmd.c ship_gui.c smallmap_gui.c
 
	sound.c spritecache.c station_cmd.c station_gui.c
 
	strings.c subsidy_gui.c texteff.c town_cmd.c town_gui.c
 
	train_cmd.c train_gui.c tree_cmd.c ttd.c
 
	tunnelbridge_cmd.c unmovable_cmd.c vehicle.c
 
	viewport.c water_cmd.c widget.c window.c screenshot.c 
 
	airport.c grfspecial.c terraform_gui.c ;
 

	
 

	
 
LANGFILES = english.txt swedish.txt french.txt german.txt italian.txt slovak.txt hungarian.txt norwegian.txt danish.txt czech.txt galician.txt polish.txt;
 

	
 
####################
 
# On UNIX we use gcc
 
####################
 
if $(UNIX) {
 
	SDL_CONFIG_CFLAGS = `XX_SDL_CONFIG_PLACEHOLDER_XX --cflags` ;
 
	SDL_CONFIG_LIBS = `XX_SDL_CONFIG_PLACEHOLDER_XX --libs` ;
 

	
 
	LINKFLAGS += $(SDL_CONFIG_LIBS) ;
 
	CC = gcc ;
 
	CCFLAGS += -Wall -Wno-multichar -DUNIX -DWITH_SDL ;
 
	
 
	OPTIMFLAGS = -O2 -fomit-frame-pointer ;
 
	DEBUGFLAGS = -g ;
 
	
 
# also include extmidi
 
	CFILES += extmidi.c unix.c ;
 

	
 
# compile in PNG support?
 
	if $(WITH_PNG) {
 
		CCFLAGS += -DWITH_PNG -I$(WITH_PNG) ;
 
		LINKFLAGS += -lpng ;
 
	}
 

	
 
# compile in zlib support?
 
	if $(WITH_ZLIB) {
 
		CCFLAGS += -DWITH_ZLIB ;
 
		LINKFLAGS += -lz ;
 
	}
 
# compile for BeOS 5.1 and higher
 
	if $(WITH_BONE_NETWORKING) {
 
		CCFLAGS += -DENABLE_NETWORK ;
 
		LINKFLAGS += -lsocket -lbind ;
 
	}
 
# link in BeOS MIDI and Be API libraries
 
	if $(BEOS_MIDI) {
 
		CCFLAGS += -DBEOS_MIDI ;
 
		LINKFLAGS += -lbe -lmidi ;
 
		CFILES += bemidi.cpp ;
 
	}
 
}
 

	
 
####################
 
# MSVC on Win32
 
####################
 

	
 
actions ActWin32Res {
 
	$(VISUALC)\\..\\common\\msdev98\\bin\\rc /r /i $(STDHDRS) /fo $(<) $(>) 
 
}
 

	
 
rule Win32Res { ActWin32Res $(<) : $(>) ; DEPENDS $(<) : $(>) ; }
 

	
 
if $(TOOLSET) = VISUALC {
 
	OPTIMFLAGS = /Oa /Os /Ow /Oy /Oi /Og /Ox /Gr /Gf /Gy /Zp4 /J	/WX /W3 -DNDEBUG ;
 

	
 
	CCFLAGS += -DWIN32 -DWIN32_EXCEPTION_TRACKER ;
 
	CFILES += win32.c ;	
 
	LINKFLAGS += /opt:nowin98 /LIBPATH:$(VISUALC)\\lib ;
 
	
 
	LINKLIBS = ws2_32.lib winmm.lib user32.lib gdi32.lib ;
 
		
 
# compile resources too
 
	EOBJ = ttd.res ;
 
	Win32Res ttd.res : ttd.rc ;
 

	
 
# png screenshots?
 
	if $(WITH_PNG) {
 
		CCFLAGS += -DWITH_PNG -I$(WITH_PNG) ;
 
		LINKLIBS += libpng.lib ;
 
	}
 

	
 
# zlib savegames?
 
	if $(WITH_ZLIB) {
 
		CCFLAGS += -DWITH_ZLIB ;
 
		LINKLIBS += zlibstat.lib ;
 
	}
 
	
 
# build release by default
 
	RELEASE = 1 ;
 
}
 

	
 

	
 
####################
 
# Common
 
####################
 
rule MyObjects {
 
	local _i _t _s ;
 

	
 
	_t = $(OUTDIR)/$(>:S=$(SUFOBJ)) ;
 
	OPTIM on $(_t) = $(3) ;
 
	
 
	MkDir $(OUTDIR) ;
 
	Depends $(_t) : $(OUTDIR) ;
 

	
 
	for _i in $(>) {
 
		_s = $(OUTDIR)/$(_i:S=$(SUFOBJ)) ;
 
		Object $(_s) : $(_i) ;
 

	
 
# special handling for sdl.c and unix.c
 
		if $(_i) = sdl.c || $(_i) = unix.c { CCFLAGS on $(_s) += $(SDL_CONFIG_CFLAGS) ; }
 
	}
 
	MainFromObjects $(OUTDIR)/$(<) : $(_t) $(EOBJ) ;
 
}
 

	
 
rule MyMain {
 
	if $(RELEASE) {
 
		OUTDIR = release ;
 
		MyObjects ttd : $(>) : $(OPTIMFLAGS) ;
 
	} else {
 
		OUTDIR = debug ;
 
		MyObjects ttd : $(>) : -D_DEBUG $(DEBUGFLAGS) ;
 
	}
 
}
 

	
 
actions CompileLang {
 
	strgen$(SLASH)strgen $(>)
 
}
 

	
 
rule LangFile {
 
	if $(>) = lang/english.txt {
 
		CompileLang $(<) table/strings.h : ;
 
		DEPENDS table/string.h : $(>) ;
 
	} else {
 
		CompileLang $(<) : $(>) ;
 
	}
 
	
 
	Clean clean : $(<) ;
 
	DEPENDS $(<) : $(>) ;
 
	DEPENDS all : $(<) ;
 
	DEPENDS $(<) : strgen/strgen ;
 
}
 

	
 
rule LangFiles {
 
	local _i ;
 
	for _i in $(<) { LangFile $(_i:S=.lng) : $(_i) ; }
 
	Clean clean : table/strings.h ;
 
}
 

	
 
LangFiles lang/$(LANGFILES) ;
 

	
 
Main strgen/strgen : strgen/strgen.c ;
 

	
 
MyMain ttd : $(CFILES) ;
 

	
Makefile
Show inline comments
 
new file 100644
 
# This Makefile is partially based on "a completely generic Makefile",
 
# originally created by Justin Husted <husted@cs>
 
#
 
# Rewrite and sane dependencies support by Petr Baudis <pasky@ucw.cz>
 
# Cygwin support and configuration by Jaen Saul <slowbyte@hot.ee>
 
# A lot of modifications by Bjarni Corfitzen <bjarni@openttd.com>
 
#
 
# Last modified by: $Author: strigeus $
 
# On: $Date: 2004/03/11 19:15:06 $
 

	
 

	
 
##############################################################################
 
#
 
# 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
 

	
 
# 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
 
#
 
# Summary of library choice defines
 
# WITH_ZLIB: savegames using zlib
 
# WITH_PNG: screenshots using PNG
 
# WITH_SDL: SDL video driver support
 
#
 
# Summary of other defines:
 
# MANUAL_CONFIG: do not use Makefile.config, config options set manually
 
# DEBUG: build in debug mode
 
# PROFILE: build in profile mode, disables -s and -fomit-frame-pointer
 
# DISPLAY_WARNINGS: when off, some errors are not displayed while compiling
 
# TRANSLATOR: build in translator mode (untranslated strings are prepended by
 
#             a <TODO> 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
 
#
 
# DATA_DIR_PREFIX: This sets the dir OpenTTD looks for the needed files. 
 
#   MUST END WITH / if defined
 
#
 
# STATIC: link statically
 
# CYGWIN: build in Cygwin environment
 
# MINGW: build with MingW compiler, link with MingW libraries
 
#
 
# Experimental (does not work properly):
 
# WITH_NETWORK: enable networking
 
# WITH_DIRECTMUSIC: enable DirectMusic MIDI support
 

	
 

	
 
##############################################################################
 
#
 
# Configuration
 
#
 

	
 
# CONFIG_WRITER have to be found even for manual configuration
 
CONFIG_WRITER=makefiledir/Makefile.config_writer
 

	
 
ifndef MANUAL_CONFIG
 
# 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)
 
else
 
CONFIG_INCLUDED:=1
 
endif
 

	
 
# tests if makefile.config contains the new needed SDL-CONFIG
 
# it updates makefile.config if needed. Flags like ENABLE_NETWORK are remembered
 
ifndef SDL-CONFIG
 
ifdef WITH_SDL
 
ifndef MANUAL_CONFIG
 
#network is enabled by default
 
ENABLE_NETWORK:=1
 
UPDATECONFIG:=upgradeconf
 
CONFIG_INCLUDED:=
 
else
 
# this should define SDL-CONFIG for manual configuration
 
ifeq ($(shell uname),FreeBSD)
 
SDL-CONFIG:=sdl11-config
 
else
 
SDL-CONFIG:=sdl-config
 
endif
 
endif
 
endif
 
endif
 

	
 
ifndef CONFIG_INCLUDED
 
-include $(LIB_DETECTION)
 
endif
 

	
 
ifdef DISPLAY_WARNINGS
 
WARNING_DISPLAY:=-fstrict-aliasing
 
else
 
WARNING_DISPLAY:=-fno-strict-aliasing
 
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
 

	
 

	
 
# Force SDL on UNIX platforms
 
ifndef WITH_SDL
 
ifdef UNIX
 
$(error You need to have SDL installed in order to run OpenTTD on UNIX.)
 
endif
 
endif
 

	
 

	
 

	
 
##############################################################################
 
#
 
# Compiler configuration
 
#
 

	
 
CC=gcc
 
CXX=g++
 

	
 
ifdef MORPHOS
 
CC += -noixemul -pipe
 
CXX += -noixemul -pipe
 
endif
 

	
 
# Executable file extension
 
ifdef WIN32
 
EXE=.exe
 
else
 
EXE=
 
endif
 

	
 
# Set output executable names
 
TTD=ttd$(EXE)
 
STRGEN=strgen/strgen$(EXE)
 
OSXAPP="OpenTTD.app"
 

	
 
# What revision are we compiling, if we have an idea?
 
REV_NUMBER := $(shell if test -d .svn; then svnversion . | tr -dc 0-9; fi)
 

	
 
ifdef RELEASE
 
REV:=$(RELEASE)
 
else
 
REV := $(shell if test -d .svn; then echo -n r; svnversion .; fi)
 
tmp_test:=$(shell echo "$(REV)" | grep "M" )
 
ifdef tmp_test
 
REV_NUMBER:=1
 
endif
 
endif
 

	
 
ifndef REV_NUMBER
 
REV_NUMBER:=0
 
endif
 

	
 
# MorphOS needs builddate
 
BUILDDATE=`date +%d.%m.%y`
 

	
 
# AMD64 needs a little more settings to work
 
ifeq ($(shell uname -m), x86_64)
 
endwarnings:=endwarnings
 
64_bit_warnings:=64_bit_warnings
 
BASECFLAGS += -m64 -D_LITTLE_ENDIAN
 
endif
 

	
 

	
 
# When calling the compiler, use these flags
 
# -g	debugging symbols
 
# -Wall	all warnings
 
# -s    automatic strip
 
#
 
# You may also want:
 
# -O	optimize or -O2 fully optimize (O's above 2 are not recommended)
 
# -pg	profile - generate profiling data.  See "man gprof" to use this.
 

	
 
CFLAGS=-Wall -Wno-multichar
 
CDEFS=-DWITH_REV
 
LDFLAGS=
 
LIBS=
 

	
 
ifdef DEBUG
 
# Debug mode
 
CDEFS += -D_DEBUG
 
BASECFLAGS += -g
 
else
 
ifdef PROFILE
 
BASECFLAGS += -pg
 
else
 
# Release mode
 
ifndef MORPHOS
 
# automatical strip breaks under morphos
 
BASECFLAGS += -s
 
LDFLAGS += -s
 
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 -mpowerpc-gpopt -force_cpusubtype_ALL $(WARNING_DISPLAY)
 
else
 
ifdef MORPHOS
 
BASECFLAGS += -O2 -funroll-loops -fexpensive-optimizations -mstring -mmultiple $(WARNING_DISPLAY)
 
else
 
BASECFLAGS += -O2 $(WARNING_DISPLAY)
 
endif
 
ifndef PROFILE
 
BASECFLAGS += -fomit-frame-pointer
 
endif
 
endif
 
endif
 

	
 
ifdef STATIC
 
ifndef OSX	# OSX can't build static if -static flag is used
 
LDFLAGS += -static
 
endif
 
endif
 

	
 
# If building on Cygwin/MingW don't link with Cygwin libs
 
ifdef WIN32
 
ifdef MINGW
 
ifdef CYGWIN
 
BASECFLAGS += -mno-cygwin
 
LDFLAGS += -mno-cygwin
 
endif
 
endif
 
endif
 

	
 
CFLAGS += $(BASECFLAGS)
 

	
 
ifdef UNIX
 
CDEFS += -DUNIX
 
endif
 

	
 
# SDL config
 
ifdef WITH_SDL
 
CDEFS += -DWITH_SDL
 
CFLAGS += `$(SDL-CONFIG) --cflags`
 
ifdef STATIC
 
LIBS += `$(SDL-CONFIG) --static-libs`
 
else
 
LIBS += `$(SDL-CONFIG) --libs`
 
endif
 
endif
 

	
 

	
 
# zlib config
 
ifdef WITH_ZLIB
 
	CDEFS +=  -DWITH_ZLIB
 
	ifdef STATIC
 
		ifdef OSX
 
# zlib is default on OSX, so everybody have it. No need for static linking
 
			LIBS += -lz
 
		else
 
			ifndef STATIC_ZLIB_PATH
 
				ifndef MANUAL_CONFIG
 
					# updates makefile.config with the zlib path
 
					UPDATECONFIG:=upgradeconf
 
				endif
 
				TEMP:=$(shell ls /lib 2>/dev/null | grep "zlib.a")$(shell ls /lib 2>/dev/null | grep "libz.a")
 
				ifdef TEMP
 
					STATIC_ZLIB_PATH:=/lib/$(TEMP)
 
				else
 
					TEMP:=$(shell ls /usr/lib 2>/dev/null | grep "zlib.a")$(shell ls /usr/lib 2>/dev/null | grep "libz.a")
 
					ifdef TEMP
 
						STATIC_ZLIB_PATH:=/usr/lib/$(TEMP)
 
					else
 
						TEMP:=$(shell ls /usr/local/lib 2>/dev/null | grep "zlib.a")$(shell ls /usr/local/lib 2>/dev/null | grep "libz.a")
 
						ifdef TEMP
 
							STATIC_ZLIB_PATH:=/usr/local/lib/$(TEMP)
 
						endif
 
					endif
 
				endif
 
			endif
 
			LIBS += $(STATIC_ZLIB_PATH)
 
		endif
 
	else
 
		LIBS += -lz
 
	endif
 
endif
 

	
 
# libpng config
 
ifdef WITH_PNG
 
CDEFS += -DWITH_PNG
 
# FreeBSD doesn't use libpng-config
 
ifdef FREEBSD
 
LIBS += -lpng
 
else
 
CFLAGS += `libpng-config --cflags`
 
ifdef OSX
 
ifdef STATIC
 
# Seems like we need a tiny hack for OSX static to work
 
LIBS += `libpng-config --prefix`/lib/libpng.a
 
else
 
LIBS += `libpng-config  --libs`
 
endif
 
else
 
# seems like older libpng versions are broken and need this
 
PNGCONFIG_FLAGS = --ldflags --libs
 
ifdef STATIC
 
LIBS += `libpng-config --static $(PNGCONFIG_FLAGS)`
 
else
 
LIBS += `libpng-config  $(PNGCONFIG_FLAGS)`
 
endif
 
endif
 
endif
 
endif
 

	
 

	
 
ifdef TRANSLATOR
 
STRGEN_FLAGS=-t
 
else
 
STRGEN_FLAGS=
 
endif
 

	
 
# file paths setup
 
ifdef GAME_DATA_DIR
 
CDEFS += -DGAME_DATA_DIR=\"$(GAME_DATA_DIR)\"
 
endif
 

	
 
ifdef PERSONAL_DIR
 
CDEFS += -DPERSONAL_DIR=\"$(PERSONAL_DIR)\"
 
endif
 

	
 
ifdef USE_HOMEDIR
 
CDEFS += -DUSE_HOMEDIR
 
endif
 

	
 
# MIDI setup
 
ifdef OSX
 
ifndef MIDI
 
MIDI:=$(OSXAPP)/contents/macos/track_starter
 
endif
 
endif
 

	
 
ifdef MIDI
 
CDEFS += -DEXTERNAL_PLAYER=\"$(MIDI)\"
 
ifdef MIDI_ARG
 
CDEFS += -DMIDI_ARG=\"$(MIDI_ARG)\"
 
endif
 
endif
 

	
 
# Experimental
 
ifdef WITH_NETWORK
 
CDEFS += -DENABLE_NETWORK
 
ifdef UNIX
 
ifndef OSX
 
ifndef MORPHOS
 
# this have caused problems on many platforms and disabling it didn't break anything
 
# now we test if disabling it as a general breaks it for anybody
 
#LIBS += -lresolv
 
endif
 
endif
 
endif
 
endif
 

	
 
ifdef WITH_DIRECTMUSIC
 
CDEFS += -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT
 
endif
 

	
 
ifdef WIN32
 
LIBS += -lws2_32 -lwinmm -lgdi32 -ldxguid -lole32 -lstdc++
 
TTDLDFLAGS += -Wl,--subsystem,windows
 
endif
 

	
 
# sets up the paths for use for make install
 
ifdef BINARY_DIR
 
BINARY_INSTALL:=$(BINARY_DIR)$(TTD)
 
else
 
BINARY_INSTALL:=$(INSTALL_DIR)$(TTD)
 
endif
 
ifdef DATA_DIR_PREFIX
 
DATA_DIR:=$(DATA_DIR_PREFIX)
 
else
 
DATA_DIR:=$(INSTALL_DIR)
 
endif
 

	
 
##############################################################################
 
#
 
# What to compile
 
# (users do not want to modify anything below)
 
#
 

	
 

	
 
### Sources
 

	
 
ttd_SOURCES = \
 
	ai.c aircraft_cmd.c aircraft_gui.c airport.c airport_gui.c bridge_gui.c \
 
	clear_cmd.c command.c disaster_cmd.c dock_gui.c dummy_land.c economy.c \
 
	engine.c engine_gui.c fileio.c gfx.c graph_gui.c industry_cmd.c \
 
	industry_gui.c intro_gui.c landscape.c main_gui.c misc.c misc_cmd.c \
 
	misc_gui.c music_gui.c namegen.c network.c news_gui.c oldloader.c \
 
	order_cmd.c order_gui.c pathfind.c player_gui.c players.c rail_cmd.c \
 
	rail_gui.c road_cmd.c road_gui.c roadveh_cmd.c roadveh_gui.c saveload.c \
 
	settings_gui.c ship_cmd.c ship_gui.c smallmap_gui.c sound.c \
 
	spritecache.c station_cmd.c station_gui.c strings.c subsidy_gui.c \
 
	texteff.c town_cmd.c town_gui.c train_cmd.c train_gui.c tree_cmd.c \
 
	ttd.c tunnelbridge_cmd.c unmovable_cmd.c vehicle.c viewport.c \
 
	water_cmd.c widget.c window.c minilzo.c screenshot.c settings.c rev.c \
 
	grfspecial.c terraform_gui.c network_gui.c
 

	
 
ifdef WITH_SDL
 
ttd_SOURCES += sdl.c
 
endif
 

	
 
ifdef WIN32
 
ttd_SOURCES += win32.c w32dm.c
 
else
 
ttd_SOURCES += extmidi.c unix.c
 
endif
 

	
 
ttd_OBJS = $(ttd_SOURCES:%.c=%.o)
 

	
 
ifdef WIN32
 
# Resource file
 
ttd_OBJS += winres.o
 
endif
 

	
 
ifdef WITH_DIRECTMUSIC
 
ttd_SOURCES += w32dm2.cpp
 
ttd_OBJS += w32dm2.o
 
endif
 

	
 
ttd_DEPS1 = $(foreach obj,$(ttd_OBJS),.deps/$(obj))
 
ttd_DEPS = $(ttd_DEPS1:%.o=%.P)
 

	
 
LANG_TXT = $(filter-out %.unfinished.txt,$(wildcard lang/*.txt))
 
LANGS = $(LANG_TXT:%.txt=%.lng)
 

	
 
C_COMPILE = $(CC) $(CFLAGS) $(CDEFS)
 
CXX_COMPILE = $(CXX) $(CFLAGS) $(CDEFS)
 

	
 
C_BUILD = $(C_COMPILE) -c
 
CXX_BUILD = $(CXX_COMPILE) -c
 

	
 
C_LINK = $(CC) $(LDFLAGS) -o
 

	
 

	
 
##############################################################################
 
#
 
# Targets
 
#
 

	
 

	
 
### Normal build rules
 

	
 

	
 
ifdef OSX
 
OSX:=OSX
 
endif
 

	
 

	
 
all: $(UPDATECONFIG) $(TTD) $(OSX) $(endwarnings)
 

	
 

	
 
$(TTD): table/strings.h $(ttd_OBJS) $(LANGS) $(MAKE_CONFIG)
 
	$(C_LINK) $@ $(TTDLDFLAGS) $(ttd_OBJS) $(LIBS)
 

	
 
$(OSX):
 
	@mkdir -p $(OSXAPP)/Contents/MacOS
 
	@mkdir -p $(OSXAPP)/Contents/Resources
 
	@echo "APPL????" > $(OSXAPP)/Contents/PkgInfo
 
	@cp os/macos/ttd.icns $(OSXAPP)/Contents/Resources/
 
	@os/macos/plistgen.sh $(OSXAPP) $(REV)
 
	@cp os/macos/track_starter $(OSXAPP)/contents/macos
 
	@ls os/macos | grep -q "\.class" || \
 
	javac os/macos/OpenTTDMidi.java
 
	@cp os/macos/OpenTTDMidi.class $(OSXAPP)/contents/macos
 
	@cp $(TTD) $(OSXAPP)/Contents/MacOS/ttd
 

	
 
$(endwarnings): $(64_bit_warnings)
 

	
 
$(64_bit_warnings):
 
	$(warning 64 bit CPUs will get some 64 bit specific bugs!)
 
	$(warning If you see any bugs, include in your bug report that you use a 64 bit CPU)
 

	
 
$(STRGEN): strgen/strgen.c rev.o
 
	$(CC) $(BASECFLAGS) $(CDEFS) -o $@ $^
 

	
 
lang/english.lng: lang/english.txt $(STRGEN)
 
	$(STRGEN)
 
	
 
table/strings.h: lang/english.lng
 

	
 
lang/%.lng: lang/%.txt $(STRGEN)
 
	$(STRGEN) $(STRGEN_FLAGS) $<
 

	
 
winres.o: ttd.rc
 
	windres -o $@ $<
 

	
 

	
 
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
 
	@echo 'const int _revision_number = $(REV_NUMBER);' >>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:
 
	rm -rf .deps *~ $(TTD) $(STRGEN) core table/strings.h $(LANGS) $(ttd_OBJS)
 

	
 
mrproper: clean
 
	rm -rf $(MAKE_CONFIG)
 

	
 
ifndef OSX
 
ifndef MORPHOS
 
install:
 
	@if [ "$(INSTALL)" == "" ]; then $(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\")
 
	@if [ "$(DATA_DIR)" == "" ]; then $(error no install path set - check makefile.config)
 
	mkdir -p $(DATA_DIR)/lang
 
	mkdir -p $(DATA_DIR)/data
 
	cp $(TTD) $(BINARY_INSTALL)
 
	cp lang/*.lng $(DATA_DIR)/lang
 
	cp data/*.grf $(DATA_DIR)/data
 
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 $(64_bit_warnings) $(endwarnings) love
 

	
 

	
 
### Automatic configuration
 
-include $(CONFIG_WRITER)
 
	
 

	
 
# Export all variables set to subprocesses (a bit dirty)
 
.EXPORT_ALL_VARIABLES:
 
upgradeconf: $(MAKE_CONFIG)
 
	rm $(MAKE_CONFIG)
 
	$(MAKE) $(MAKE_CONFIG)
 

	
 
.PHONY: upgradeconf
 

	
 

	
 
### Internal build rules
 

	
 
# This makes sure the .deps dir is always around.
 
DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
 

	
 
# Introduce the dependencies
 
-include $(ttd_DEPS)
 

	
 
# This compiles the object file as well as silently updating its dependencies
 
# list at the same time. It is not an issue that they aren't around during the
 
# first compilation round as we just build everything at that time anyway,
 
# therefore we do not need to watch deps.
 
%.o: %.c $(MAKE_CONFIG)
 
	@echo '$(C_BUILD) $<'; \
 
		$(C_BUILD) $< -Wp,-MD,.deps/$(*F).pp
 
	@-cp .deps/$(*F).pp .deps/$(*F).P; \
 
		tr ' ' '\012' < .deps/$(*F).pp \
 
		| sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
 
		>> .deps/$(*F).P; \
 
	rm .deps/$(*F).pp
 

	
 
# For DirectMusic build
 
%.o: %.cpp  $(MAKE_CONFIG)
 
	$(CXX_BUILD) $<
StdAfx.c
Show inline comments
 
new file 100644
 
// stdafx.cpp : source file that includes just the standard includes
 
//	ttd.pch will be the pre-compiled header
 
//	stdafx.obj will contain the pre-compiled type information
 

	
 
#include "stdafx.h"
 

	
 
// TODO: reference any additional headers you need in STDAFX.H
 
// and not in this file
ai.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "player.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"
 

	
 
// remove some day perhaps?
 
static Player *_cur_ai_player;
 

	
 
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, 0);
 
	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;
 
	
 
	v = p->ai.cur_veh == NULL ? _vehicles : p->ai.cur_veh+1;
 

	
 
	for (;v != endof(_vehicles); v++) {
 
		if (v->type == 0 || 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 != 0 &&
 
					_engines[v->engine_type].reliability < 35389) ||
 
					v->age >= v->max_age) {
 
				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;
 
}
 

	
 
// XXX
 
static const byte _rail_locos_count[3] = {
 
	27, 3, 5		
 
};
 
extern const byte _rail_engines_start[3];
 

	
 
static int AiChooseTrainToBuild(byte railtype, int32 money, byte flag)
 
{
 
	int best_veh_index = -1;
 
	byte best_veh_score = 0;
 
	int32 r;
 

	
 
	int i = _rail_engines_start[railtype];
 
	int end = i + _rail_locos_count[railtype];
 
	Engine *e = &_engines[i];
 
	do {
 
		assert(!(_rail_vehicle_info[i].flags & RVI_WAGON));
 
		
 
		if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D)
 
			continue;
 

	
 
		r = DoCommandByTile(0, i, 0, 0, CMD_BUILD_RAIL_VEHICLE);
 
		if (r != CMD_ERROR && 
 
					(!(_cmd_build_rail_veh_var1&1) || !(flag&1)) && 
 
					r <= money && 
 
				_cmd_build_rail_veh_score >= best_veh_score) {
 
			best_veh_score = _cmd_build_rail_veh_score;
 
			best_veh_index = i;
 
		}
 
	} while (++e, ++i != end);
 

	
 
	return best_veh_index;
 
}
 

	
 
static int AiChooseRoadVehToBuild(byte cargo, int32 money)
 
{
 
	int best_veh_index = -1;
 
	int32 best_veh_cost = 0;
 
	int32 r;
 

	
 
	int i = _cargoc.ai_roadveh_start[cargo];
 
	int end = i + _cargoc.ai_roadveh_count[cargo];
 
	Engine *e = &_engines[i];
 
	do {
 
		if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D)
 
			continue;
 

	
 
		r = DoCommandByTile(0, i, 0, 0, CMD_BUILD_ROAD_VEH);
 
		if (r != CMD_ERROR && r <= money && r >= best_veh_cost) {
 
			best_veh_cost = r;
 
			best_veh_index = i;
 
		}
 
	} while (++e, ++i != end);
 

	
 
	return best_veh_index;
 
}
 

	
 
static int AiChooseAircraftToBuild(int32 money, byte flag)
 
{
 
	int best_veh_index = -1;
 
	int32 best_veh_cost = 0;
 
	int32 r;
 

	
 
	int i = AIRCRAFT_ENGINES_INDEX;
 
	int end = i + NUM_AIRCRAFT_ENGINES;
 
	Engine *e = &_engines[i];
 
	do {
 
		if (!HASBIT(e->player_avail, _current_player) || e->reliability < 0x8A3D)
 
			continue;
 

	
 
		if (flag&1) {
 
			if (i<253) continue;
 
		} else {
 
			if (i>=253) continue;
 
		}
 

	
 
		r = DoCommandByTile(0, i, 0, 0, CMD_BUILD_AIRCRAFT);
 
		if (r != CMD_ERROR && r <= money && r >= best_veh_cost) {
 
			best_veh_cost = r;
 
			best_veh_index = i;
 
		}
 
	} while (++e, ++i != end);
 

	
 
	return best_veh_index;
 
}
 

	
 
static int32 AiGetBasePrice(Player *p)
 
{
 
	int32 base = _price.station_value;
 

	
 
	// adjust base price when more expensive vehicles are available
 
	if (p->ai.railtype_to_use == 1) base = (base * 3) >> 1;
 
	else if (p->ai.railtype_to_use == 2) base *= 2;
 
	
 
	return base;
 
}
 

	
 
#if 0
 
static int AiChooseShipToBuild(byte cargo, int32 money)
 
{
 
	// XXX: not done
 
	return 0;
 
}
 
#endif
 

	
 
static int AiChooseRoadVehToReplaceWith(Player *p, Vehicle *v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	return AiChooseRoadVehToBuild(v->cargo_type, avail_money);
 
}
 

	
 
static int AiChooseAircraftToReplaceWith(Player *p, Vehicle *v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	return AiChooseAircraftToBuild(avail_money, v->engine_type>=253?1:0);
 
}
 

	
 
static int AiChooseTrainToReplaceWith(Player *p, Vehicle *v)
 
{
 
	int32 avail_money = p->player_money + v->value;
 
	int num=0;
 
	Vehicle *u = v;
 

	
 
	while (++num, u->next != NULL) {
 
		u = u->next;
 
	}
 

	
 
	// XXX: check if a wagon
 
	return AiChooseTrainToBuild(v->u.rail.railtype, avail_money, 0);
 
}
 

	
 
static int AiChooseShipToReplaceWith(Player *p, Vehicle *v)
 
{
 
	error("!AiChooseShipToReplaceWith");
 

	
 
	/* maybe useless, but avoids compiler warning this way */
 
	return 0;
 
}
 

	
 
static void AiHandleGotoDepot(Player *p, int cmd)
 
{
 
	if ((p->ai.cur_veh->next_order & OT_MASK) != OT_GOTO_DEPOT)
 
		DoCommandByTile(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->next_order&OT_MASK) == OT_GOTO_DEPOT) {
 
		p->ai.cur_veh->next_order = OT_DUMMY;
 
		InvalidateWindow(WC_VEHICLE_VIEW, p->ai.cur_veh->index);
 
	}
 
} 
 

	
 
static void AiRestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak)
 
{
 
	uint16 *os = bak->order, ord;
 
	int ind = 0;
 
	while ((ord = *os++) != 0) {
 
		if (DoCommandByTile(0, v->index + (ind << 16), ord, DC_EXEC, CMD_INSERT_ORDER) == CMD_ERROR)
 
			break;
 
		ind++;
 
	}
 
}
 

	
 
static void AiHandleReplaceTrain(Player *p)
 
{
 
	Vehicle *v = p->ai.cur_veh;
 
	BackuppedOrders orderbak[1];
 
	int veh;
 
	uint tile;
 

	
 
	// wait until the vehicle reaches the depot.
 
	if (!IsTrainDepotTile(v->tile) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) {
 
		AiHandleGotoDepot(p, CMD_TRAIN_GOTO_DEPOT);
 
		return;
 
	}
 

	
 
	veh = AiChooseTrainToReplaceWith(p, v);
 
	if (veh != -1) {
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 
		
 
		if (DoCommandByTile(0, v->index, 2, DC_EXEC, CMD_SELL_RAIL_WAGON) != CMD_ERROR &&
 
			 DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE) != CMD_ERROR) {
 
			veh = _new_train_id;
 
			AiRestoreVehicleOrders(&_vehicles[veh], orderbak);
 
			DoCommandByTile(0, veh, 0, DC_EXEC, CMD_START_STOP_TRAIN);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceRoadVeh(Player *p)
 
{
 
	Vehicle *v = p->ai.cur_veh;
 
	BackuppedOrders orderbak[1];
 
	int veh;
 
	uint tile;
 

	
 
	if (!IsRoadDepotTile(v->tile) || v->u.road.state != 254 || !(v->vehstatus&VS_STOPPED)) {
 
		AiHandleGotoDepot(p, CMD_SEND_ROADVEH_TO_DEPOT);
 
		return;
 
	}
 

	
 
	veh = AiChooseRoadVehToReplaceWith(p, v);
 
	if (veh != -1) {
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 
		
 
		if (DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH) != CMD_ERROR &&
 
			 DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH) != CMD_ERROR) {
 
			veh = _new_roadveh_id;
 
			AiRestoreVehicleOrders(&_vehicles[veh], orderbak);
 
			DoCommandByTile(0, veh, 0, DC_EXEC, CMD_START_STOP_ROADVEH);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceAircraft(Player *p)
 
{
 
	Vehicle *v = p->ai.cur_veh;
 
	int veh;
 
	BackuppedOrders orderbak[1];
 
	uint tile;
 

	
 
	if (!IsAircraftHangarTile(v->tile) && !(v->vehstatus&VS_STOPPED)) {
 
		AiHandleGotoDepot(p, CMD_SEND_AIRCRAFT_TO_HANGAR);
 
		return;
 
	}
 

	
 
	veh = AiChooseAircraftToReplaceWith(p, v);
 
	if (veh != -1) {
 
		BackupVehicleOrders(v, orderbak);
 
		tile = v->tile;
 

	
 
		if (DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT) != CMD_ERROR &&
 
			 DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT) != CMD_ERROR) {
 
			veh = _new_aircraft_id;
 
			AiRestoreVehicleOrders(&_vehicles[veh], orderbak);
 
			DoCommandByTile(0, veh, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT);
 
		}
 
	}
 
}
 

	
 
static void AiHandleReplaceShip(Player *p)
 
{
 
	error("!AiHandleReplaceShip");
 
}
 

	
 
typedef int CheckReplaceProc(Player *p, 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)
 
{
 
	Vehicle *v = p->ai.cur_veh;
 

	
 
	if (v->type == 0 || v->owner != _current_player || v->type > VEH_Ship || _veh_check_replace_proc[v->type - VEH_Train](p, v) == -1) {
 
		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)
 
{
 
	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 (v->type == 0 || v->owner != _current_player)
 
		return;
 
	_veh_do_replace_proc[v->type - VEH_Train](p);
 
}
 

	
 
typedef struct FoundRoute {
 
	int distance;
 
	byte cargo;
 
	void *from;
 
	void *to;
 
} FoundRoute;
 

	
 
static Town *AiFindRandomTown() {
 
	Town *t = DEREF_TOWN(RandomRange(_total_towns));
 
	return (t->xy != 0) ? t : NULL;
 
}
 

	
 
static Industry *AiFindRandomIndustry() {
 
	Industry *i = DEREF_INDUSTRY(RandomRange(_total_industries));
 
	return (i->xy != 0) ? i : NULL;
 
}
 

	
 
static void AiFindSubsidyIndustryRoute(FoundRoute *fr)
 
{
 
	int i;
 
	byte cargo;
 
	Subsidy *s;
 
	Industry *from, *to_ind;
 
	Town *to_tow;
 
	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 == 0xFF || cargo == CT_PASSENGERS || cargo == CT_MAIL || s->age > 7)
 
		return;
 
	fr->cargo = cargo;
 

	
 
	fr->from = from = DEREF_INDUSTRY(s->from);
 

	
 
	if (cargo == CT_GOODS || cargo == CT_FOOD) {
 
		to_tow = DEREF_TOWN(s->to);
 
		if (to_tow->population < (uint32)(cargo == CT_FOOD ? 200 : 900))
 
			return; // error
 
		fr->to = to_tow;
 
		to_xy = to_tow->xy;
 
	} else {
 
		to_ind = DEREF_INDUSTRY(s->to);
 
		fr->to = to_ind;
 
		to_xy = to_ind->xy;
 
	}
 

	
 
	fr->distance = GetTileDist(from->xy, to_xy);
 
}
 

	
 
static void AiFindSubsidyPassengerRoute(FoundRoute *fr)
 
{
 
	uint i;
 
	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 = DEREF_TOWN(s->from);
 
	fr->to = to = DEREF_TOWN(s->to);
 

	
 
	// They must be big enough
 
	if (from->population < 400 || to->population < 400)
 
		return;
 

	
 
	fr->distance = GetTileDist(from->xy, to->xy);
 
}
 

	
 
static void AiFindRandomIndustryRoute(FoundRoute *fr)
 
{
 
	Industry *i,*i2;
 
	Town *t;
 
	uint32 r;
 
	byte 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] != 0xFF)
 
		cargo = i->produced_cargo[1];
 

	
 
	fr->cargo = cargo;
 

	
 
	// don't allow passengers
 
	if (cargo == 0xFF || cargo == CT_PASSENGERS)
 
		return;
 

	
 
	if (cargo != CT_GOODS && cargo != CT_FOOD) {
 
		// pick a dest, and see if it can receive
 
		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 = GetTileDist(i->xy, i2->xy);
 
	} else {
 
		// pick a dest town, and see if it's big enough
 
		t = AiFindRandomTown();
 
		if (t == NULL || t->population < (uint32)(cargo == CT_FOOD ? 200 : 900))
 
			return;
 
		
 
		fr->to = t;
 
		fr->distance = GetTileDist(i->xy, t->xy);
 
	}
 
}
 

	
 
static void AiFindRandomPassengerRoute(FoundRoute *fr)
 
{
 
	uint32 r;
 
	Town *source, *dest;
 

	
 
	// initially error
 
	fr->distance = -1;
 

	
 
	r = Random();
 

	
 
	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 = GetTileDist(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, cur;
 
	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) if (st->xy != 0 && st->owner == _current_player) {
 
		cur = GetTileDist1D(from_tile, st->xy);
 
		if (cur < dist) dist = cur;
 
		cur = GetTileDist1D(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) {
 
		if (((Town*)fr->from)->pct_pass_transported > 0x99 ||
 
				((Town*)fr->to)->pct_pass_transported > 0x99)
 
			return false;
 

	
 
		// Make sure it has a reasonably good rating
 
		if ( ((Town*)fr->from)->ratings[_current_player] < -100 ||
 
				((Town*)fr->to)->ratings[_current_player] < -100)
 
			return false;
 
	} else {
 
		Industry *i = (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 = (GET_TILE_X(a) < GET_TILE_X(b)) ? 1 : 0;
 
	if (GET_TILE_Y(a) >= GET_TILE_Y(b))	i ^= 3;
 
	return i;
 
}
 

	
 
static TileIndex AiGetPctTileBetween(TileIndex a, TileIndex b, byte pct)
 
{
 
	return TILE_XY(
 
			GET_TILE_X(a) + ((GET_TILE_X(b) - GET_TILE_X(a)) * pct >> 8),
 
			GET_TILE_Y(a) + ((GET_TILE_Y(b) - GET_TILE_Y(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;
 
	p->ai.railtype_to_use = p->max_railtype - 1;
 
	r = (uint16)Random();
 
	
 
	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 = (uint16)Random();
 

	
 
	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 = DEREF_INDUSTRY(RandomRange(lengthof(_industries)));
 
			if (in != NULL && in->type == IT_OIL_RIG) {
 
				if (GetTileDist(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) {
 
		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(;;) {
 
		byte dis;
 

	
 
		r = (uint16)Random();
 
		
 
		dis = _patches.ai_disable_veh;
 
		if ((dis & 0xF) == 0xF) return; // no available vehicles at all?
 

	
 
		if (r < 0x7626) {
 
			if (dis&DISABLE_TRAINS) continue;
 
			AiWantTrainRoute(p);
 
		} else if (r < 0xC4EA) {
 
			if (dis&DISABLE_ROADVEH) continue;
 
			AiWantRoadRoute(p);
 
		} else if (r < 0xD89B) {
 
			if (dis&DISABLE_AIRCRAFT) continue;
 
			AiWantAircraftRoute(p);
 
		} else {
 
			if (dis&DISABLE_SHIPS) 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 values[NUM_CARGO];
 
	int w,h;
 
	uint tile2;
 

	
 
	for(;p->mode != 4;p++) if (p->mode == 1) {
 
		tile2 = TILE_ADD(tile, p->tileoffs);
 

	
 
		w = ((p->attr>>1) & 7);
 
		h = ((p->attr>>4) & 7);
 
		if (p->attr&1) intswap(w, h);
 

	
 
		if (cargo & 0x80) {
 
			GetProductionAroundTiles(values, tile2, w, h);
 
			return values[cargo & 0x7F] != 0;
 
		} else {
 
			GetAcceptanceAroundTiles(values, tile2, w, h);
 
			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, byte flag)
 
{
 
	int32 r;
 
	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 + p->tileoffs);
 

	
 
		_cleared_town = NULL;
 

	
 
		if (p->mode < 2) {
 
			if (p->mode == 0) {
 
				// Depot
 
				r = DoCommandByTile(c, _cur_ai_player->ai.railtype_to_use, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT);
 
			} else {
 
				// Station
 
				r = DoCommandByTile(c, (p->attr&1) | (p->attr>>4)<<8 | (p->attr>>1&7)<<16, _cur_ai_player->ai.railtype_to_use, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION);
 
			}
 

	
 
			if (r == CMD_ERROR) return CMD_ERROR;
 
			total_cost += r;
 

	
 
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 (IS_TILETYPE(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;
 
					r = DoCommandByTile(c, _cur_ai_player->ai.railtype_to_use, i, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
 
					if (r == CMD_ERROR) return CMD_ERROR;
 
					total_cost += r;
 
				}
 
			}
 
			
 
			/* signals too? */
 
			if (j&3) {
 
				// Can't build signals on a road.
 
				if (IS_TILETYPE(c, MP_STREET))
 
					return CMD_ERROR;
 

	
 
				if (flag & DC_EXEC) {
 
					j = 4 - j;
 
					do {
 
						r = DoCommandByTile(c, k, 0, flag, CMD_BUILD_SIGNALS);
 
					} while (--j);
 
				} else {
 
					r = _price.build_signals;
 
				}
 
				if (r == CMD_ERROR) return CMD_ERROR;
 
				total_cost += r;
 
			}
 
		} else if (p->mode == 3) {
 
			//Clear stuff and then build single rail.
 
			if (GetTileSlope(c,NULL) != 0)
 
				return CMD_ERROR;
 
			r = DoCommandByTile(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
 
			if (r == CMD_ERROR) return CMD_ERROR;
 
			total_cost += r + _price.build_rail;
 

	
 
			if (flag & DC_EXEC) {
 
				DoCommandByTile(c, _cur_ai_player->ai.railtype_to_use, 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, 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, DC_NO_TOWN_RATING);
 
			if (*cost != CMD_ERROR && 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)
 
{
 
	byte old_player;
 
	uint32 r;
 
	uint slope;
 
	int h;
 

	
 
	old_player = _current_player;
 
	_current_player = OWNER_NONE;
 

	
 
	r = Random();
 

	
 
	unk &= (int)r;
 

	
 
	do {
 
		tile = TILE_MASK(tile + _tileoffs_by_dir[dir]);
 

	
 
		r >>= 2;
 
		if (r&2) {
 
			dir++;
 
			if (r&1)
 
				dir -= 2;
 
		}
 
		dir &= 3;
 
	} while (--unk >= 0);
 

	
 
	slope = GetTileSlope(tile, &h);
 
	
 
	if (slope != 0) {
 
		if (mode > 0 || (mode == 0 && !(r&0xC))) {
 
			// Terraform up
 
			DoCommandByTile(tile, _terraform_up_flags[slope-1], 1,
 
				DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
 
		} else if (h != 0) {
 
			// Terraform down
 
			DoCommandByTile(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)
 
{
 
	int i, 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
 
	i = 8;
 
	do {
 
		// 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, &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,
 
					DC_EXEC | DC_NO_TOWN_RATING
 
				);
 
				assert(r != CMD_ERROR);
 
			}
 
		} 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 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 + p->tileoffs - _tileoffs_by_dir[*dir = p->attr];
 
}
 

	
 
typedef struct AiRailPathFindData {
 
	TileIndex tile;
 
	TileIndex tile2;
 
	int count;
 
	bool flag;
 
} AiRailPathFindData;
 

	
 
static bool AiEnumFollowTrack(uint 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 (GetTileDist1D(tile, a->tile2) < 4)
 
		a->count++;
 

	
 
	return false;
 
}
 

	
 
static bool AiDoFollowTrack(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 + _tileoffs_by_dir[p->ai.cur_dir_a], 0x2000, 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;
 
	TileInfo ti;
 
} 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(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) {
 
	int 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 void FORCEINLINE AiCheckBuildRailBridgeHere(AiRailFinder *arf, TileIndex tile, const byte *p)
 
{
 
	TileIndex tile_new;
 
	bool flag;
 

	
 
	int dir2 = p[0] & 3;
 
	
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 

	
 
	if (arf->ti.tileh == _dir_table_1[dir2] || (arf->ti.tileh==0 && arf->ti.z!=0)) {
 
		tile_new = tile;
 
		// Allow bridges directly over bottom tiles
 
		flag = arf->ti.z == 0;
 
		for(;;) {
 
			if (tile_new < -_tileoffs_by_dir[dir2]) return; // Wraping around map, no bridge possible!
 
			tile_new = TILE_MASK(tile_new + _tileoffs_by_dir[dir2]);
 
			FindLandscapeHeightByTile(&arf->ti, tile_new);
 
			if (arf->ti.tileh != 0 || arf->ti.type == MP_CLEAR || arf->ti.type == MP_TREES) {
 
				if (!flag) return;
 
				break;
 
			}
 
			if (arf->ti.type != MP_WATER && arf->ti.type != MP_RAILWAY && arf->ti.type != MP_STREET)
 
				return;
 
			flag = true;
 
		}
 

	
 
		// Is building a (rail)bridge possible at this place (type doesn't matter)?
 
		if (DoCommandByTile(tile_new, tile, arf->player->ai.railtype_to_use<<8, 
 
			DC_AUTO, CMD_BUILD_BRIDGE) == CMD_ERROR)
 
				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 void FORCEINLINE AiCheckBuildRailTunnelHere(AiRailFinder *arf, TileIndex tile, const byte *p)
 
{
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 

	
 
	if (arf->ti.tileh == _dir_table_2[p[0]&3] && arf->ti.z!=0) { 
 
		int32 cost = DoCommandByTile(tile, arf->player->ai.railtype_to_use, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 

	
 
		if (cost != CMD_ERROR && 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 + _tileoffs_by_dir[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 = GetTileDist1Db(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.
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 
	if (arf->ti.z == 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]) &&
 
					DoCommandByTile(tile, arf->player->ai.railtype_to_use, p[0], DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL) != CMD_ERROR) {
 
				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 += _tileoffs_by_dir[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 what (rail)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 + (p->ai.railtype_to_use << 8); i != 0; i--) {
 
			if (CheckBridge_Stuff(i, bridge_len)) {
 
				int32 cost = DoCommandByTile(arf.bridge_end_tile, p->ai.cur_tile_a, i, DC_AUTO, CMD_BUILD_BRIDGE);
 
				if (cost != CMD_ERROR && cost < (p->player_money >> 5))
 
					break;
 
			}
 
		}
 

	
 
		// Build it
 
		DoCommandByTile(arf.bridge_end_tile, p->ai.cur_tile_a, i, 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
 
		DoCommandByTile(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];
 
		DoCommandByTile(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;
 
	uint tile = p->ai.cur_tile_a;
 
	int offs;
 
	TileIndex tilenew;
 

	
 
	if (IS_TILETYPE(tile, MP_TUNNELBRIDGE)) {
 
		if (!(_map5[tile] & 0x80)) {
 
			// Clear the tunnel and continue at the other side of it.
 
			if (DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR) == CMD_ERROR)
 
				return false;
 
			p->ai.cur_tile_a = TILE_MASK(_build_tunnel_endtile - _tileoffs_by_dir[p->ai.cur_dir_a]);
 
			return true;
 
		}
 

	
 
		if (!(_map5[tile] & 0x40)) {
 
			
 
			// Check if the bridge points in the right direction.
 
			// This is not really needed the first place AiRemoveTileAndGoForward is called.
 
			if ((_map5[tile]&1) != (p->ai.cur_dir_a&1))
 
				return false;
 

	
 
			// Find other side of bridge.
 
			offs = _tileoffs_by_dir[p->ai.cur_dir_a];
 
			do {
 
				tile = TILE_MASK(tile - offs);
 
			} while (_map5[tile] & 0x40);
 
			
 
			tilenew = TILE_MASK(tile - offs);
 
			// And clear the bridge.
 
			if (DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR) == CMD_ERROR)
 
				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 (IS_TILETYPE(tile, MP_RAILWAY) &&
 
			(_map5[tile]&0xC0) == 0x40) {
 
		DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_REMOVE_SIGNALS);
 
	}
 
	
 
	// And also remove the rail.
 
	if (DoCommandByTile(tile, 0, bit, DC_EXEC, CMD_REMOVE_SINGLE_RAIL) == CMD_ERROR)
 
		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 - _tileoffs_by_dir[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)
 
{
 
	int i;
 

	
 
	if (p->ai.state_mode < 1) {
 
		// Construct mode, build new rail.
 
		AiBuildRailConstruct(p);		
 
	} else if (p->ai.state_mode == 1) {
 
		
 
		// Destruct mode, destroy the rail currently built.
 
		AiBuildRailDestruct(p);
 
	} else if (p->ai.state_mode == 2) {
 

	
 
		// 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 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;
 
	DoCommandByTile(TILE_MASK(tile + _tileoffs_by_dir[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;
 
	DoCommandByTile(TILE_MASK(tile + _tileoffs_by_dir[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 int AiGetStationIdByDef(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _default_rail_track_data[id]->data;
 
	while (p->mode != 1) p++;
 
	return _map2[TILE_ADD(tile,p->tileoffs)];
 
}
 

	
 
static void AiStateBuildRailVeh(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	TileIndex tile;
 
	int i, veh;
 
	int cargo;
 
	int32 cost;
 
	Vehicle *v;
 
	uint 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, ptr->tileoffs);
 

	
 
	cargo = p->ai.cargo_type;
 
	for(i=0;;) {
 
		if (p->ai.wagon_list[i] == INVALID_VEHICLE) {
 
			veh = _cargoc.ai_railwagon[p->ai.railtype_to_use][cargo];
 
			cost = DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
 
			if (cost == CMD_ERROR) goto handle_nocash;
 
			p->ai.wagon_list[i] = _new_wagon_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);
 
	if (veh == -1) {
 
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 = DoCommandByTile(tile, p->ai.wagon_list[i], 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
 
				assert(cost != CMD_ERROR);
 
			}
 
			p->ai.state =	AIS_0;
 
		}
 
		return;
 
	}
 

	
 
	// Try to build the locomotive
 
	cost = DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
 
	assert(cost != CMD_ERROR);
 
	loco_id = _new_train_id;
 

	
 
	// Sell a vehicle if the train is double headed.
 
	v = &_vehicles[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;
 
		DoCommandByTile(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++) {
 
		DoCommandByTile(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++) {
 
		AiBuildRec *aib = (&p->ai.src) + p->ai.order_list_blocks[i];
 
		uint flags = (AiGetStationIdByDef(aib->use_tile, aib->cur_building_rule) << 8) + OT_GOTO_STATION;
 
		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));
 
		
 
		if (!is_pass && i == 1) flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) flags |= OF_FULL_LOAD;
 

	
 
		DoCommandByTile(0, loco_id + (i << 16),	flags, DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommandByTile(0, loco_id, 0, DC_EXEC, CMD_START_STOP_TRAIN);
 

	
 
	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)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	const AiDefaultBlockData *b;
 

	
 
	num = p->ai.num_build_rec;	
 
	aib = &p->ai.src;
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			b = _default_rail_track_data[aib->cur_building_rule]->data;
 
			while (b->mode != 4) {
 
				DoCommandByTile(TILE_ADD(aib->use_tile, b->tileoffs), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				b++;
 
			}
 
		}
 
	} while (++aib,--num);
 

	
 
	p->ai.state = AIS_0;
 
}
 

	
 
static bool AiCheckRoadResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
 
{
 
	uint values[NUM_CARGO];
 
	for(;;p++) {
 
		if (p->mode == 4) {
 
			return true;
 
		} else if (p->mode == 1) {
 
			uint tile2 = TILE_ADD(tile, p->tileoffs);
 
			if (cargo & 0x80) {
 
				GetProductionAroundTiles(values, tile2, 1, 1);
 
				return values[cargo & 0x7F] != 0;
 
			} else {
 
				GetAcceptanceAroundTiles(values, tile2, 1, 1);
 
				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 (*cost != CMD_ERROR && AiCheckRoadResources(tile, p->data, cargo))
 
				return i;
 
		}
 
	}
 

	
 
	return -1;
 
}
 

	
 
static int32 AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, byte flag)
 
{
 
	int32 r;
 
	int32 total_cost = 0;
 
	Town *t = NULL;
 
	int rating = 0;
 
	int roadflag = 0;
 

	
 
	for(;p->mode != 4;p++) {
 
		uint c = TILE_MASK(tile+ p->tileoffs);
 

	
 
		_cleared_town = NULL;
 
	
 
		if (p->mode == 2) {
 
			if (IS_TILETYPE(c, MP_STREET) &&
 
					(_map5[c]&0xF0)==0 &&
 
					(_map5[c]&p->attr)!=0) {
 
				roadflag |= 2;
 
				
 
				// all bits are already built?
 
				if ((_map5[c]&p->attr)==p->attr)
 
					continue;
 
			}
 

	
 
			r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
 
			if (r == CMD_ERROR)
 
				return CMD_ERROR;
 
			total_cost += r;
 

	
 
			continue;
 
		}
 

	
 
		if (p->mode == 0) {
 
			// Depot
 
			r = DoCommandByTile(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
 
				r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRUCK_STATION);
 
			} else {
 
				// Bus station
 
				r = DoCommandByTile(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_BUS_STATION);
 
			}
 
clear_town_stuff:;
 

	
 
			if (r == CMD_ERROR) return CMD_ERROR;
 
			total_cost += r;
 

	
 
			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) != 0)
 
				return CMD_ERROR;
 

	
 
			if (!(IS_TILETYPE(c, MP_STREET) && (_map5[c]&0xF0)==0)) {
 
				r = DoCommandByTile(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
 
				if (r == CMD_ERROR) 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)
 
{
 
	AiBuildRec *aib;
 
	int num;
 
	
 
	num = p->ai.num_build_rec;
 
	aib = &p->ai.src;	
 

	
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			if (GetTileDist(aib->use_tile, tile) < 9)
 
				return false;
 
		}
 
	} while (++aib, --num);
 

	
 
	return true;
 
}
 

	
 

	
 
static void AiStateBuildDefaultRoadBlocks(Player *p)
 
{
 
	int i, 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
 
	i = 8;
 
	do {
 
		// 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(r != CMD_ERROR);
 
			}
 
		} 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 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;
 
	TileInfo ti;
 
} 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(uint tile, AiRoadEnum *a, int track, uint length, byte *state)
 
{
 
	uint dist = GetTileDist(tile, a->dest);
 
	uint tile2;
 

	
 
	if (dist <= a->best_dist) {
 
		tile2 = TILE_MASK(tile + _tileoffs_by_dir[_dir_by_track[track]]);
 
		if (IS_TILETYPE(tile2, MP_STREET) &&
 
				(_map5[tile2]&0xF0) == 0) {
 
			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,
 
};
 

	
 
bool AiCheckRoadFinished(Player *p)
 
{
 
	AiRoadEnum are;
 
	uint 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 + _tileoffs_by_dir[dir]);
 

	
 
	bits = GetTileTrackStatus(tile, 2) & _ai_road_table_and[dir];
 
	if (bits == 0) {
 
		return false;
 
	}
 

	
 
	are.best_dist = (uint)-1;
 

	
 
	for_each_bit(i, bits) {
 
		FollowTrack(tile, 0x3002, _dir_by_track[i], (TPFEnumProc*)AiEnumFollowRoad, NULL, &are);
 
	}
 

	
 
	if (GetTileDist(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(uint tile, int flags, int type)
 
{
 
	static const byte _road_bits[] = {
 
		8+2,
 
		1+4,
 
		1+8,
 
		4+2,
 
		1+2,
 
		8+4,
 
	};
 
	return DoCommandByTile(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD) != CMD_ERROR;
 
}
 

	
 
static void FORCEINLINE AiCheckBuildRoadBridgeHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
 
{
 
	TileIndex tile_new;
 
	bool flag;
 

	
 
	int dir2 = p[0] & 3;
 
	
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 
	if (arf->ti.tileh == _dir_table_1[dir2] || (arf->ti.tileh==0 && arf->ti.z!=0)) {
 
		tile_new = tile;
 
		// Allow bridges directly over bottom tiles
 
		flag = arf->ti.z == 0;
 
		for(;;) {
 
			if (tile_new < -_tileoffs_by_dir[dir2]) return; // Wraping around map, no bridge possible!		
 
			tile_new = TILE_MASK(tile_new + _tileoffs_by_dir[dir2]);
 
			FindLandscapeHeightByTile(&arf->ti, tile_new);
 
			if (arf->ti.tileh != 0 || arf->ti.type == MP_CLEAR || arf->ti.type == MP_TREES) {
 
				// 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 (arf->ti.type != MP_WATER && arf->ti.type != MP_RAILWAY && arf->ti.type != MP_STREET)
 
				return;
 
			flag = true;
 
		}
 

	
 
		// Is building a (rail)bridge possible at this place (type doesn't matter)?
 
		if (DoCommandByTile(tile_new, tile, 0x8000, DC_AUTO, CMD_BUILD_BRIDGE) == CMD_ERROR)
 
			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 void FORCEINLINE AiCheckBuildRoadTunnelHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
 
{
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 

	
 
	if (arf->ti.tileh == _dir_table_2[p[0]&3] && arf->ti.z!=0) { 
 
		int32 cost = DoCommandByTile(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL);
 

	
 
		if (cost != CMD_ERROR && 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 + _tileoffs_by_dir[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 = GetTileDist1Db(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.
 
	FindLandscapeHeightByTile(&arf->ti, tile);
 
	if (arf->ti.z == 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--;
 
}
 

	
 
int sw;
 

	
 

	
 
static void AiBuildRoadConstruct(Player *p)
 
{
 
	AiRoadFinder arf;
 
	int i;
 
	uint 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 + _tileoffs_by_dir[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 + _tileoffs_by_dir[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 = DoCommandByTile(tile, p->ai.cur_tile_a, i + (0x80 << 8), DC_AUTO, CMD_BUILD_BRIDGE);
 
				if (cost != CMD_ERROR && cost < (p->player_money >> 5))
 
					break;
 
			}
 
		}
 

	
 
		// Build it
 
		DoCommandByTile(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
 
		DoCommandByTile(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)
 
{
 
	int i;
 

	
 
	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) {
 

	
 
		// 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, 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);
 

	
 
		sw ^= 1;
 
		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 int AiGetStationIdFromRoadBlock(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _road_default_block_data[id]->data;
 
	while (p->mode != 1) p++;
 
	return _map2[TILE_ADD(tile, p->tileoffs)];
 
}
 

	
 
static void AiStateBuildRoadVehicles(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	uint tile,loco_id;
 
	int veh, i;
 
	int32 cost;
 
	
 
	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, ptr->tileoffs);
 

	
 
	veh = AiChooseRoadVehToBuild(p->ai.cargo_type, p->player_money);
 
	if (veh == -1) {
 
		p->ai.state = AIS_0;
 
		return;
 
	}
 

	
 
	cost = DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH);
 
	if (cost == CMD_ERROR)
 
		return;
 

	
 
	loco_id = _new_roadveh_id;
 

	
 
	for(i=0; p->ai.order_list_blocks[i] != 0xFF; i++) {
 
		AiBuildRec *aib = (&p->ai.src) + p->ai.order_list_blocks[i];
 
		uint flags  = (AiGetStationIdFromRoadBlock(aib->use_tile, aib->cur_building_rule) << 8) + OT_GOTO_STATION;
 
		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));
 

	
 
		if (!is_pass && i == 1) flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) flags |= OF_FULL_LOAD;
 

	
 
		DoCommandByTile(0, loco_id + (i << 16),	flags, DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommandByTile(0, loco_id, 0, DC_EXEC, CMD_START_STOP_ROADVEH);
 

	
 
	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)
 
{
 
	int num;
 
	AiBuildRec *aib;
 
	const AiDefaultBlockData *b;
 

	
 
	num = p->ai.num_build_rec;	
 
	aib = &p->ai.src;
 
	do {
 
		if (aib->cur_building_rule != 255) {
 
			b = _road_default_block_data[aib->cur_building_rule]->data;
 
			while (b->mode != 4) {
 
				if (b->mode <= 1) {
 
					DoCommandByTile(TILE_ADD(aib->use_tile, b->tileoffs), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				}
 
				b++;
 
			}
 
		}
 
	} while (++aib,--num);
 

	
 
	p->ai.state = AIS_0;
 
}
 

	
 
static bool AiCheckIfHangar(Station *st)
 
{
 
	uint tile = st->airport_tile;
 
	// HANGAR of airports
 
	// 0x20 - hangar large airport (32)
 
	// 0x41 - hangar small airport (65)
 
	return (_map5[tile] == 32 || _map5[tile] == 65);
 
}
 

	
 
static void AiStateAirportStuff(Player *p)
 
{
 
	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) {
 
			// Dismiss ghost stations.
 
			if (st->xy == 0)
 
				continue;
 

	
 
			// 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 (GetTileDist1D(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
 
			 * AiCheckIfHangar() 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 = AiCheckIfHangar(st);
 
			}
 

	
 
			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, r;
 

	
 
	for(;p->mode == 0;p++) {
 
		if (!HASBIT(_avail_aircraft, p->attr))
 
			return CMD_ERROR;
 
		r = DoCommandByTile(TILE_MASK(tile + p->tileoffs), p->attr,0,flag | DC_AUTO | DC_NO_WATER,CMD_BUILD_AIRPORT);
 
		if (r == CMD_ERROR)
 
			return CMD_ERROR;
 
		total_cost += r;
 
	}
 

	
 
	return total_cost;
 
}
 

	
 
static bool AiCheckAirportResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
 
{
 
	uint values[NUM_CARGO];
 
	int w,h;
 
	uint tile2;
 

	
 
	for(;p->mode==0;p++) {
 
		tile2 = TILE_ADD(tile, p->tileoffs);
 
		w = _airport_size_x[p->attr];
 
		h = _airport_size_y[p->attr];
 
		if (cargo & 0x80) {
 
			GetProductionAroundTiles(values, tile2, w, h);
 
			return values[cargo & 0x7F] != 0;
 
		} else {
 
			GetAcceptanceAroundTiles(values, tile2, w, h);
 
			return values[cargo] >= 8;
 
		}
 
	}
 
	return true;
 
}
 

	
 
static int AiFindBestDefaultAirportBlock(TileIndex tile, byte cargo, byte heli, int32 *cost)
 
{
 
	int i;
 
	const AiDefaultBlockData *p;
 
	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 (*cost != CMD_ERROR && 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);
 

	
 
#if 0
 
			if (!IS_TILETYPE(aib->use_tile, MP_STREET) &&
 
					!IS_TILETYPE(aib->use_tile, MP_RAILWAY) &&
 
					!IS_TILETYPE(aib->use_tile, MP_STATION)
 
					) {
 
				
 
				_map_type_and_height[aib->use_tile] = 0xa1;
 
				_map5[aib->use_tile] = 0x80;
 
				MarkTileDirtyByTile(aib->use_tile);
 
			}
 
#endif
 
//			redsq_debug(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(r != CMD_ERROR);
 
			}
 
		} 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 int AiGetStationIdFromAircraftBlock(TileIndex tile, int id)
 
{
 
	const AiDefaultBlockData *p = _airport_default_block_data[id];
 
	while (p->mode != 1) p++;
 
	return _map2[TILE_ADD(tile, p->tileoffs)];
 
}
 

	
 
static void AiStateBuildAircraftVehicles(Player *p)
 
{
 
	const AiDefaultBlockData *ptr;
 
	uint tile;
 
	int veh;
 
	int32 cost;
 
	int i;
 
	uint 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, ptr->tileoffs);
 

	
 
	veh = AiChooseAircraftToBuild(p->player_money, p->ai.build_kind!=0 ? 1 : 0);
 
	if (veh == -1) {
 
		return;
 
	}
 

	
 
	cost = DoCommandByTile(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT);
 
	if (cost == CMD_ERROR)
 
		return;
 
	loco_id = _new_aircraft_id;
 

	
 
	for(i=0; p->ai.order_list_blocks[i] != 0xFF; i++) {
 
		AiBuildRec *aib = (&p->ai.src) + p->ai.order_list_blocks[i];
 
		uint flags = (AiGetStationIdFromAircraftBlock(aib->use_tile, aib->cur_building_rule) << 8) + OT_GOTO_STATION;
 
		bool is_pass = (p->ai.cargo_type == CT_PASSENGERS || p->ai.cargo_type == CT_MAIL);
 

	
 
		if (!is_pass && i == 1) flags |= OF_UNLOAD;
 
		if (p->ai.num_want_fullload != 0 && (is_pass || i == 0)) flags |= OF_FULL_LOAD;
 

	
 
		DoCommandByTile(0, loco_id + (i << 16), flags, DC_EXEC, CMD_INSERT_ORDER);
 
	}
 

	
 
	DoCommandByTile(0, loco_id, 0, DC_EXEC, CMD_START_STOP_AIRCRAFT);
 

	
 
	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 (!IsTrainDepotTile(v->tile) || v->u.rail.track != 0x80 || !(v->vehstatus&VS_STOPPED)) {
 
				if ((v->next_order & OT_MASK) != OT_GOTO_DEPOT)
 
					DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_TRAIN_GOTO_DEPOT);
 
				goto going_to_depot;
 
			}
 

	
 
			// Sell whole train
 
			DoCommandByTile(v->tile, v->index, 1, DC_EXEC, CMD_SELL_RAIL_WAGON);
 
			
 
		} else if (v->type == VEH_Road) {
 
			if (!IsRoadDepotTile(v->tile) || v->u.road.state != 254 || !(v->vehstatus&VS_STOPPED)) {
 
				if ((v->next_order & OT_MASK) != OT_GOTO_DEPOT)
 
					DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT);
 
				goto going_to_depot;
 
			}
 

	
 
			DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
 
		} else if (v->type == VEH_Aircraft) {
 
			if (!IsAircraftHangarTile(v->tile) && !(v->vehstatus&VS_STOPPED)) {
 
				if ((v->next_order & OT_MASK) != OT_GOTO_DEPOT)
 
					DoCommandByTile(0, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
 
				goto going_to_depot;
 
			}
 

	
 
			DoCommandByTile(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->next_order&OT_MASK) == OT_GOTO_DEPOT) {
 
		v->next_order = OT_DUMMY;
 
		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[256], *used;
 
	uint16 *ord;
 
	Station *st;
 
	uint 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
 
	memset(in_use, 0, sizeof(in_use));
 
	for(ord=_order_array; ord != _ptr_to_next_order; ord++) {
 
		if ((*ord & OT_MASK) == OT_GOTO_STATION)
 
			in_use[*ord >> 8] = 1;
 
	}
 

	
 
	// Go through all stations and delete those that aren't in use
 
	used=in_use;
 
	FOR_ALL_STATIONS(st) {
 
		if (st->xy != 0 && st->owner == _current_player && !*used &&
 
				((tile = st->bus_tile) != 0 ||
 
					(tile = st->lorry_tile) != 0 ||
 
					(tile = st->train_tile) != 0 ||
 
					(tile = st->dock_tile) != 0 ||
 
					(tile = st->airport_tile) != 0)) {
 
			DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
		used++;
 
	}
 

	
 
}
 

	
 
static void AiRemovePlayerRailOrRoad(Player *p, uint tile)
 
{
 
	byte m5;
 

	
 
	if (IS_TILETYPE(tile, MP_RAILWAY)) {
 
		if (_map_owner[tile] != _current_player)
 
			return;
 
		m5 = _map5[tile];
 
		if ((m5&~0x3) != 0xC0) {
 
is_rail_crossing:;
 
			m5 = GetRailTrackStatus(tile);
 
			
 
			if (m5 == 0xC || m5 == 0x30)
 
				return;
 

	
 
			if (m5&0x25) {
 
pos_0:
 
				if (!(GetRailTrackStatus(TILE_MASK(tile-TILE_XY(1,0)))&0x19)) {
 
					p->ai.cur_dir_a = 0;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (m5&0x2A) {
 
pos_1:
 
				if (!(GetRailTrackStatus(TILE_MASK(tile+TILE_XY(0,1)))&0x16)) {
 
					p->ai.cur_dir_a = 1;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (m5&0x19) {
 
pos_2:
 
				if (!(GetRailTrackStatus(TILE_MASK(tile+TILE_XY(1,0)))&0x25)) {
 
					p->ai.cur_dir_a = 2;
 
					p->ai.cur_tile_a = tile;
 
					p->ai.state = AIS_REMOVE_SINGLE_RAIL_TILE;
 
					return;
 
				}
 
			}
 

	
 
			if (m5&0x16) {
 
pos_3:
 
				if (!(GetRailTrackStatus(TILE_MASK(tile-TILE_XY(0,1)))&0x2A)) {
 
					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};
 

	
 
			m5 &= 3;
 
			if (GetRailTrackStatus(tile + _tileoffs_by_dir[m5]) & _depot_bits[m5])
 
				return;
 
			
 
			DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
		}
 
	} else if (IS_TILETYPE(tile, MP_STREET)) {
 
		if (_map_owner[tile] != _current_player)
 
			return;
 

	
 
		if ( (_map5[tile]&0xF0) == 0x10)
 
			goto is_rail_crossing;
 

	
 
		if ( (_map5[tile]&0xF0) == 0x20) {
 
			int dir;
 

	
 
			// Check if there are any stations around.
 
			if (IS_TILETYPE(tile + TILE_XY(-1,0), MP_STATION) &&
 
					_map_owner[tile + TILE_XY(-1,0)] == _current_player)
 
						return;
 

	
 
			if (IS_TILETYPE(tile + TILE_XY(1,0), MP_STATION) &&
 
					_map_owner[tile + TILE_XY(1,0)] == _current_player)
 
						return;
 

	
 
			if (IS_TILETYPE(tile + TILE_XY(0,-1), MP_STATION) &&
 
					_map_owner[tile + TILE_XY(0,-1)] == _current_player)
 
						return;
 

	
 
			if (IS_TILETYPE(tile + TILE_XY(0,1), MP_STATION) &&
 
					_map_owner[tile + TILE_XY(0,1)] == _current_player)
 
						return;
 

	
 
			dir = _map5[tile] & 3;
 

	
 
			DoCommandByTile(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
			DoCommandByTile(
 
				TILE_MASK(tile + _tileoffs_by_dir[dir]),
 
				8 >> (dir ^ 2),
 
				0,
 
				DC_EXEC,
 
				CMD_REMOVE_ROAD);
 
		}
 
	} else if (IS_TILETYPE(tile, MP_TUNNELBRIDGE)) {
 
		byte b;
 

	
 
		if (_map_owner[tile] != _current_player || (_map5[tile] & 0xC6) != 0x80)
 
			return;
 
		
 
		m5 = 0;
 

	
 
		b = _map5[tile] & 0x21;
 
		if (b == 0) goto pos_0;
 
		if (b == 1) goto pos_3;
 
		if (b == 0x20) goto pos_2;
 
		goto pos_1;
 
	}
 
}
 

	
 
static void AiStateRemoveTrack(Player *p)
 
{
 
	int num = 1000;
 
	
 
	do {
 
		uint tile = ++p->ai.state_counter;
 

	
 
		// Iterated all tiles?
 
		if (tile == 0) {
 
			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) {
 
		if ((p->bankrupt_timeout-=8) > 0)
 
			return;
 
		p->bankrupt_timeout = 0;
 
		DeleteWindowById(WC_BUY_COMPANY, _current_player);
 
		if (_current_player == _local_player) {
 
			AskExitToGameMenu();
 
			return;
 
		}
 
		if (IS_HUMAN_PLAYER(_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 (IS_HUMAN_PLAYER(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;
 
			DoCommandByTile(0, old_p, 0, DC_EXEC, CMD_BUY_COMPANY);
 
			_current_player = old_p;
 
		}
 
	}
 
}
 

	
 
static void AiAdjustLoan(Player *p)
 
{
 
	int32 base = AiGetBasePrice(p);
 

	
 
	if (p->player_money > base * 1400) {
 
		// Decrease loan
 
		if (p->current_loan != 0) {
 
			DoCommandByTile(0, _current_player, 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) {
 
			DoCommandByTile(0, _current_player, 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);
 
		DoCommandByTile(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
 
	}
 
}
 

	
 

	
 
void AiDoGameLoop(Player *p) 
 
{
 
	_cur_ai_player = p;
 
	
 
	if (p->bankrupt_asked != 0) {
 
		AiHandleTakeover(p);
 
		return;
 
	}
 

	
 
	if (IS_HUMAN_PLAYER(_current_player))
 
		return;
 

	
 
	AiAdjustLoan(p);
 
	AiBuildCompanyHQ(p);
 

	
 
	if (_opt.diff.competitor_speed == 4) {
 
		/* ultraspeed */
 
		_ai_actions[p->ai.state](p);
 
		if (p->bankrupt_asked != 0)
 
			return;
 
	} else if (_opt.diff.competitor_speed != 3) {
 
		p->ai.tick++;
 
		if (!(p->ai.tick&1))
 
			return;
 
		if (_opt.diff.competitor_speed != 2) {
 
			if (!(p->ai.tick&2))
 
				return;
 
			if (_opt.diff.competitor_speed == 0) {
 
				if (!(p->ai.tick&4))
 
					return;
 
			}
 
		}
 
	} 
 
#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);
 
}
aircraft_cmd.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "vehicle.h"
 
#include "engine.h"
 
#include "command.h"
 
#include "station.h"
 
#include "news.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "airport.h"
 

	
 
static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport);
 
static bool AirportSetBlocks(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *Airport);
 
static bool AirportHasBlock(Vehicle *v, AirportFTA *current_pos, const AirportFTAClass *Airport);
 
static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *Airport);
 
static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *Airport);
 
static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *Airport);
 

	
 
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
 
};
 

	
 
int GetAircraftImage(Vehicle *v, byte direction)
 
{
 
	int spritenum = v->spritenum;
 

	
 
#ifdef AIRCRAFT_CUSTOM_SPRITES // TODO --pasky
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleSprite(v, direction);
 

	
 
		if (sprite) return sprite;
 
		spritenum = _engine_original_sprites[v->engine_type];
 
	}
 
#endif
 
	return direction + _aircraft_sprite[spritenum];
 
}
 

	
 
const byte _aircraft_cost_table[NUM_AIRCRAFT_ENGINES] = {
 
	14, 15, 16, 75, 15, 18, 17, 18,
 
	19, 20, 16, 18, 17, 30, 18, 19,
 
	27, 25, 20, 19, 18, 26, 16, 17,
 
	16, 16, 17, 18, 20, 21, 19, 24,
 
	80, 13, 18, 25, 32, 80, 15, 17,
 
	15
 
};
 

	
 
const byte _aircraft_speed[NUM_AIRCRAFT_ENGINES] = {
 
	37, 37, 74, 181, 37, 74, 74, 74,
 
	74, 74, 74, 74, 74, 74, 74, 74,
 
	74, 74, 74, 74, 74, 74, 74, 74,
 
	74, 74, 74, 74, 74, 74, 181, 74,
 
	181, 37, 37, 74, 74, 181, 25, 40,
 
	25
 
};
 

	
 
const byte _aircraft_running_cost[NUM_AIRCRAFT_ENGINES] = {
 
	85, 100, 130, 250, 98, 240, 150, 245,
 
	192, 190, 135, 240, 155, 253, 210, 220,
 
	230, 225, 235, 220, 170, 210, 125, 145,
 
	130, 149, 170, 210, 230, 220, 160, 248,
 
	251, 85, 100, 140, 220, 255, 81, 77,
 
	80
 
};
 

	
 
const byte _aircraft_num_mail[NUM_AIRCRAFT_ENGINES] = {
 
	 4,  8, 10, 20,  6, 30, 15, 30,
 
	40, 25, 10, 35, 15, 50, 25, 25,
 
	40, 35, 30, 25, 20, 20, 10, 10,
 
	10, 10, 18, 25, 60, 65, 45, 80,
 
	45,  5,  9, 12, 40, 30, 15, 20,
 
	10
 
};
 

	
 
const uint16 _aircraft_num_pass[NUM_AIRCRAFT_ENGINES] = {
 
	 25, 65, 90,100, 30,200,100,150,
 
	220,230, 95,170,110,300,200,240,
 
	260,240,260,210,160,220, 80, 85,
 
	 75, 85, 65,110,180,150, 85,400,
 
	130, 25, 60, 90,200,100, 40, 55,
 
	 40
 
};
 

	
 
const byte _aircraft_sounds[NUM_AIRCRAFT_ENGINES] = {
 
	 6,  6,  7, 59,  6,  7,  7,  7,
 
	 7,  7,  7,  7,  7, 61,  7,  7,
 
	 7,  7,  7,  7,  7,  7,  7,  7,
 
	 7,  7,  7,  7,  7,  7,  7, 61,
 
	59, 69, 70,  7, 61, 59,  7,  7,
 
	 7
 
};
 

	
 
const byte _aircraft_table_3[NUM_AIRCRAFT_ENGINES] = {
 
	 1,  0,  2,  8,  5,  6,  2,  2,
 
	 3,  3,  2,  2,  4,  7,  4,  4,
 
	 4,  3,  4,  4,  4,  4,  6,  2,
 
	11, 10, 15, 12, 13, 14, 16, 17,
 
	18, 20, 21, 22, 23, 24,  9, 19,
 
	25
 
};
 

	
 
// &1 = regular aircraft
 
// &2 = crashes easily on small airports
 
const byte _aircraft_subtype[NUM_AIRCRAFT_ENGINES] = {
 
	1,  1,  3,  3,  1,  3,  1,  3,
 
	3,  3,  3,  3,  3,  3,  3,  3,
 
	3,  3,  3,  3,  3,  3,  1,  1,
 
	3,  3,  3,  3,  3,  3,  3,  3,
 
	3,  1,  1,  1,  3,  3,  0,  0,
 
	0
 
};
 

	
 
const byte _aircraft_acceleration[NUM_AIRCRAFT_ENGINES] = {
 
	18, 20, 35, 50, 20, 40, 35, 40,
 
	40, 40, 35, 40, 40, 40, 40, 40,
 
	40, 40, 40, 40, 40, 40, 50, 40,
 
	40, 40, 40, 40, 40, 40, 40, 40,
 
	50, 18, 20, 40, 40, 50, 20, 20,
 
	20,
 
};
 

	
 
void DrawAircraftEngine(int x, int y, int engine, uint32 image_ormod)
 
{
 
	DrawSprite((6 + _aircraft_sprite[_aircraft_table_3[engine - AIRCRAFT_ENGINES_INDEX]]) | image_ormod, x, y);
 

	
 
	if ((_aircraft_subtype[engine - AIRCRAFT_ENGINES_INDEX]&1) == 0)
 
		DrawSprite(0xF3D, x, y-5);
 
}
 

	
 
void DrawAircraftEngineInfo(int engine, int x, int y, int maxw)
 
{
 
	engine -= AIRCRAFT_ENGINES_INDEX;
 

	
 
	SET_DPARAM32(0, ((_price.aircraft_base >> 3) * _aircraft_cost_table[engine]) >> 5);
 
	SET_DPARAM16(1, _aircraft_speed[engine] << 3);
 
	SET_DPARAM16(2, _aircraft_num_pass[engine]);
 
	SET_DPARAM16(3, _aircraft_num_mail[engine]);
 
	SET_DPARAM32(4, _aircraft_running_cost[engine] * _price.aircraft_running >> 8);
 

	
 
	DrawStringMultiCenter(x, y, STR_A02E_COST_MAX_SPEED_CAPACITY, maxw);
 
}
 

	
 
/* Allocate many vehicles */
 
bool AllocateVehicles(Vehicle **vl, int num)
 
{
 
	int i;
 
	Vehicle *v;
 
	bool success = true;
 

	
 
	for(i=0; i!=num; i++) {
 
		vl[i] = v = AllocateVehicle();
 
		if (v == NULL) {
 
			success = false;
 
			break;
 
		}
 
		v->type = 1;
 
	}
 

	
 
	while (--i >= 0) {
 
		vl[i]->type = 0;
 
	}
 

	
 
	return success;
 
}
 

	
 
static int32 EstimateAircraftCost(uint16 engine_type)
 
{
 
	return _aircraft_cost_table[engine_type - AIRCRAFT_ENGINES_INDEX] * (_price.aircraft_base>>3)>>5;
 
}
 

	
 

	
 
/* p1 = engine */
 
int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	int32 value;
 
	Vehicle *vl[3], *v, *u, *w;
 
	byte unit_num;
 
	uint tile = TILE_FROM_XY(x,y);
 
	Engine *e;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	value = EstimateAircraftCost(p1);
 

	
 
	if (flags & DC_QUERY_COST)
 
		return value;
 

	
 
	// allocate 2 or 3 vehicle structs, depending on type
 
	if (!AllocateVehicles(vl, (_aircraft_subtype[p1 - AIRCRAFT_ENGINES_INDEX]&1) == 0 ? 3 : 2) ||
 
			_ptr_to_next_order >= endof(_order_array))
 
					return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	unit_num = GetFreeUnitNumber(VEH_Aircraft);
 
	if (unit_num > _patches.max_aircraft)
 
		return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
	if (flags & DC_EXEC) {
 
		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 = GET_TILE_X(tile)*16 + 5;
 
		y = GET_TILE_Y(tile)*16 + 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_DISASTER;
 

	
 
		v->spritenum = _aircraft_table_3[p1 - AIRCRAFT_ENGINES_INDEX];
 
//		v->cargo_count = u->number_of_pieces = 0;
 

	
 
		v->cargo_cap = _aircraft_num_pass[p1 - AIRCRAFT_ENGINES_INDEX];
 
		u->cargo_cap = _aircraft_num_mail[p1 - AIRCRAFT_ENGINES_INDEX];
 

	
 
		v->cargo_type = CT_PASSENGERS;
 
		u->cargo_type = CT_MAIL;
 

	
 
		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 = 0xFF;
 
//		v->destination_coords = 0;
 

	
 
		v->max_speed = _aircraft_speed[p1 - AIRCRAFT_ENGINES_INDEX];
 
		v->acceleration = _aircraft_acceleration[p1 - AIRCRAFT_ENGINES_INDEX];
 
		v->engine_type = (byte)p1;
 

	
 
		v->subtype = (_aircraft_subtype[p1 - AIRCRAFT_ENGINES_INDEX]&1) == 0 ? 0 : 2;
 
		v->value = value;
 

	
 
		u->subtype = 4;
 

	
 
		e = &_engines[p1];
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->max_age = e->lifelength * 366;
 

	
 
		_new_aircraft_id = v->index;
 

	
 
		*(v->schedule_ptr = _ptr_to_next_order++) = 0;
 
		// the AI doesn't click on a tile to build airplanes, so the below code will
 
		// never work. Therefore just assume the AI's planes always come from Hangar0
 
		v->u.air.pos = (_is_ai_player) ? 0:MAX_ELEMENTS;
 

	
 
		/* When we click on hangar we know the tile (it is in var '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 depots, it is simple
 
		*/
 
		{
 
			Station *st;
 
			const AirportFTAClass *Airport;
 
			const uint16 *cur_depot;
 
			byte i = 0;
 
			st = DEREF_STATION(_map2[tile]);
 
			Airport = GetAirport(st->airport_type);
 
			// first element of depot array contains #of depots on the airport
 
			for (cur_depot=&Airport->airport_depots[1]; i != Airport->airport_depots[0]; cur_depot++) {
 
				if ((uint)(st->airport_tile + *cur_depot) == tile) {
 
					assert(Airport->layout[i].heading == HANGAR);
 
					v->u.air.pos = Airport->layout[i].position;
 
					break;
 
				}
 
				i++;
 
			}
 
			// 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 = _map2[tile];
 
		v->next = u;
 

	
 
		v->service_interval = _patches.servint_aircraft;
 

	
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 

	
 
		v->cur_image = u->cur_image = 0xEA0;
 

	
 
		VehiclePositionChanged(v);
 
		VehiclePositionChanged(u);
 

	
 
		// Aircraft with 3 vehicles?
 
		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->subtype = 6;
 
			w->cur_image = 0xF3D;
 
			VehiclePositionChanged(w);
 
		}
 

	
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
	}
 

	
 
	return value;
 
}
 

	
 
bool IsAircraftHangarTile(TileIndex tile)
 
{
 
	// 0x56 - hangar facing other way international airport (86)
 
	// 0x20 - hangar large airport (32)
 
	// 0x41 - hangar small airport (65)
 
	return IS_TILETYPE(tile, MP_STATION) &&
 
				(_map5[tile] == 32 || _map5[tile] == 65 || _map5[tile] == 86);
 
}
 

	
 
static bool CheckStoppedInHangar(Vehicle *v)
 
{
 
	if (!(v->vehstatus&VS_STOPPED) ||
 
			!IsAircraftHangarTile(v->tile)) {
 
		_error_message = STR_A01B_AIRCRAFT_MUST_BE_STOPPED;
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
void DoDeleteAircraft(Vehicle *v)
 
{
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 
	InvalidateWindow(WC_COMPANY, v->owner);
 
	DeleteVehicleChain(v);
 
}
 

	
 
// p1 = vehicle
 
int32 CmdSellAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	v = &_vehicles[p1];
 

	
 
	if (!CheckOwnership(v->owner) || !CheckStoppedInHangar(v))
 
		return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		// Invalidate depot
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 

	
 
		DoDeleteAircraft(v);
 

	
 
	}
 

	
 
	return -(int32)v->value;
 
}
 

	
 
// p1 = vehicle
 
int32 CmdStartStopAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	v = &_vehicles[p1];
 

	
 
	if (!CheckOwnership(v->owner))
 
		return CMD_ERROR;
 

	
 
	// cannot stop airplane when in flight, or when taking off / landing
 
	if (v->u.air.state >= STARTTAKEOFF) {
 
		return_cmd_error(STR_A017_AIRCRAFT_IS_IN_FLIGHT);
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	return 0;
 
}
 

	
 
// p1 = vehicle
 
int32 CmdSendAircraftToHangar(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	Station *st;
 

	
 
	v = &_vehicles[p1];
 

	
 
	if (!CheckOwnership(v->owner))
 
		return CMD_ERROR;
 

	
 
	if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT) {
 
		if (flags & DC_EXEC) {
 
 			if (v->next_order&OF_UNLOAD)
 
 				{ v->cur_order_index++; }
 
			v->next_order = OT_DUMMY;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
		}
 
	} else {
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		// If an airport doesn't have terminals (so no landing space for airports),
 
		// it surely doesn't have any hangars
 
		if (st->xy == 0 || st->airport_tile == 0 || GetAirport(st->airport_type)->nofterminals == 0)
 
					return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			v->next_order = OF_NON_STOP | OF_FULL_LOAD | OT_GOTO_DEPOT;
 
			v->next_order_param = v->u.air.targetairport;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
// p1 = vehicle
 
// p2 = new service int
 
int32 CmdChangeAircraftServiceInt(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	v = &_vehicles[p1];
 

	
 
	if (!CheckOwnership(v->owner))
 
		return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		v->service_interval = (uint16)p2;
 
		InvalidateWindowWidget(WC_VEHICLE_DETAILS, v->index, 7);
 
	}
 

	
 
	return 0;
 
}
 

	
 
// p1 = vehicle
 
// p2 = new cargo type
 
int32 CmdRefitAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v,*u;
 
	int pass, mail;
 
	int32 cost;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_RUN);
 

	
 
	v = &_vehicles[p1];
 
	if (!CheckOwnership(v->owner) || !CheckStoppedInHangar(v))
 
		return CMD_ERROR;
 

	
 
	pass = _aircraft_num_pass[v->engine_type - AIRCRAFT_ENGINES_INDEX];
 
	if (p2 != 0) {
 
		pass >>= 1;
 
		if (p2 != 5)
 
			pass >>= 1;
 
	}
 
	_aircraft_refit_capacity = pass;
 

	
 
	cost = 0;
 
	if (IS_HUMAN_PLAYER(v->owner) && (byte)p2 != v->cargo_type) {
 
		cost = _price.aircraft_base >> 7;
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		v->cargo_cap = pass;
 

	
 
		u = v->next;
 
		mail = _aircraft_num_mail[v->engine_type - AIRCRAFT_ENGINES_INDEX];
 
		if (p2 != 0) {
 
			mail = 0;
 
		}
 
		u->cargo_cap = mail;
 
		v->cargo_count = u->cargo_count = 0;
 
		v->cargo_type = (byte)p2;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	}
 

	
 
	return cost;
 
}
 

	
 
void HandleClickOnAircraft(Vehicle *v)
 
{
 
	ShowAircraftViewWindow(v);
 
}
 

	
 
static void CheckIfAircraftNeedsService(Vehicle *v)
 
{
 
	Station *st;
 

	
 
	if (v->date_of_last_service + v->service_interval > _date)
 
		return;
 

	
 
	if (v->vehstatus & VS_STOPPED)
 
		return;
 

	
 
	if ((v->next_order & (OT_MASK | OF_FULL_LOAD)) == (OT_GOTO_DEPOT | OF_FULL_LOAD))
 
		return;
 

	
 
 	if (_patches.gotodepot && IS_HUMAN_PLAYER(v->owner) && ScheduleHasDepotOrders(v->schedule_ptr))
 
 		return;
 
 
 
	st = DEREF_STATION(v->next_order_param);
 
	// only goto depot if the target airport has terminals (eg. it is airport)
 
	if (st->xy != 0 && st->airport_tile != 0 && GetAirport(st->airport_type)->nofterminals != 0) {
 
//		printf("targetairport = %d, st->index = %d\n", v->u.air.targetairport, st->index);
 
//		v->u.air.targetairport = st->index;
 
		v->next_order = OF_NON_STOP | OT_GOTO_DEPOT;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
	} else if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT) {
 
		v->next_order = OT_DUMMY;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
	}
 
}
 

	
 
void OnNewDay_Aircraft(Vehicle *v)
 
{
 
	int32 cost;
 

	
 
	if (v->subtype > 2)
 
		return;
 

	
 
	if ((++v->day_counter & 7) == 0)
 
		DecreaseVehicleValue(v);
 

	
 
	CheckVehicleBreakdown(v);
 
	AgeVehicle(v);
 
	CheckIfAircraftNeedsService(v);
 

	
 
	if (v->vehstatus & VS_STOPPED)
 
		return;
 

	
 
	cost = _aircraft_running_cost[v->engine_type - AIRCRAFT_ENGINES_INDEX] * _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);
 
	InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 
}
 

	
 
void AircraftYearlyLoop()
 
{
 
	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++;
 
	} while ( (v=v->next) != NULL );
 
}
 

	
 
static void HelicopterTickHandler(Vehicle *v)
 
{
 
	Vehicle *u;
 
	int tick,spd;
 
	uint16 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->next_order&OT_MASK) == OT_LOADING || (v->vehstatus&VS_STOPPED)) {
 
		if (u->cur_speed != 0) {
 
			u->cur_speed++;
 
			if (u->cur_speed >= 0x80 && u->cur_image == 0xF40) {
 
				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) {
 
		img = 0xF3D;
 
		if (u->cur_image == img)
 
			return;
 
	} else if (tick >= spd) {
 
		u->tick_counter = 0;
 
		img = u->cur_image + 1;
 
		if (img > 0xF40)
 
			img = 0xF3E;
 
	} 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 yt;
 

	
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	v->z_pos = z;
 

	
 
	v->cur_image = GetAircraftImage(v, v->direction);
 

	
 
	BeginVehicleMove(v);
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 

	
 
	u = v->next;
 

	
 
	yt = y - ((v->z_pos-GetSlopeZ(x, y-1)) >> 3);
 
	u->x_pos = x;
 
	u->y_pos = yt;
 
	u->z_pos = GetSlopeZ(x,yt);
 
	u->cur_image = v->cur_image;
 

	
 
	BeginVehicleMove(u);
 
	VehiclePositionChanged(u);
 
	EndVehicleMove(u);
 

	
 
	if ((u=u->next) != NULL) {
 
		u->x_pos = x;
 
		u->y_pos = y;
 
		u->z_pos = z + 5;
 

	
 
		BeginVehicleMove(u);
 
		VehiclePositionChanged(u);
 
		EndVehicleMove(u);
 
	}
 
}
 

	
 
static void ServiceAircraft(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	v->progress = 0;
 
	v->vehstatus |= VS_HIDDEN;
 

	
 
	u = v->next;
 
	u->vehstatus |= VS_HIDDEN;
 
	if ((u=u->next) != NULL) {
 
		u->vehstatus |= VS_HIDDEN;
 
		u->cur_speed = 0;
 
	}
 

	
 
	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 

	
 
	v->date_of_last_service = _date;
 
	v->breakdowns_since_last_service = 0;
 
	v->reliability = _engines[v->engine_type].reliability;
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
}
 

	
 
static void PlayAircraftSound(Vehicle *v)
 
{
 
	SndPlayVehicleFx(_aircraft_sounds[v->engine_type - AIRCRAFT_ENGINES_INDEX], 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, 4);
 
	}
 

	
 
	if (!(v->direction & 1)) {
 
		spd = spd * 3 >> 2;
 
	}
 

	
 
	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)
 
{
 
	byte maxz = 162;
 
	if (v->max_speed != 37) {
 
		maxz = 171;
 
		if (v->max_speed != 74) {maxz = 180;}
 
	}
 
	return maxz;
 
}
 

	
 
static bool Aircraft_5(Vehicle *v)
 
{
 
	Station *st;
 
	const AirportMovingData *amd;
 
	Vehicle *u;
 
	byte z,dirdiff,newdir,maxz,curz;
 
	GetNewVehiclePosResult gp;
 
	uint dist;
 
	int x,y;
 

	
 
	st = DEREF_STATION(v->u.air.targetairport);
 

	
 
	// prevent going to 0,0 if airport is deleted.
 
	{
 
		uint tile = st->airport_tile;
 
		if (tile == 0) tile = st->xy;
 
		// xy of destination
 
		x = GET_TILE_X(tile)*16;
 
		y = GET_TILE_Y(tile)*16;
 
	}
 

	
 
	// get airport moving data
 
	assert(v->u.air.pos < GetAirport(st->airport_type)->nofelements);
 
	amd = &_airport_moving_datas[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(0x16, 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 - Aircraft_5 -> 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 <= (uint)((amd->flag&AMED_SLOWTURN)?8:4))
 
		return true;
 

	
 
	// At final pos?
 
	if (dist == 0) {
 

	
 
		// Clamp speed to 12.
 
		if (v->cur_speed > 12)
 
			v->cur_speed = 12;
 

	
 
		// Change direction smoothly to final direction.
 
		dirdiff = 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 == 0) {
 
			v->cur_speed = 0;
 
			return true;
 
		}
 

	
 
		if (!UpdateAircraftSpeed(v))
 
			return false;
 

	
 
		v->direction = (v->direction+((dirdiff&7)<5?1:-1)) & 7;
 
		v->cur_speed >>= 1;
 

	
 
		SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
		return false;
 
	}
 

	
 
	// Clamp speed?
 
	if (!(amd->flag & AMED_NOSPDCLAMP) && v->cur_speed > 12)
 
		v->cur_speed = 12;
 

	
 
	if (!UpdateAircraftSpeed(v))
 
		return false;
 

	
 
	// Decrease animation counter.
 
	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;
 
		// Determine running altitude
 
		maxz = GetAircraftFlyingAltitude(v);
 
		if (z > maxz)
 
			z = maxz;
 
	}
 

	
 
	if (amd->flag & AMED_LAND) {
 
		if (st->airport_tile == 0) {
 
			//FIXME -- FIXED - Aircraft_5 -> if station no longer exists, do not land
 
			// * 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);
 
			// 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 const int8 _crashed_aircraft_moddir[4] = {
 
	-1,0,0,1
 
};
 

	
 
static void HandleCrashedAircraft(Vehicle *v)
 
{
 
	uint32 r;
 
	Station *st;
 

	
 
	v->u.air.crashed_counter++;
 

	
 
	if (v->u.air.crashed_counter < 650) {
 
		if (CHANCE16R(1,32,r)) {
 
			v->direction = (v->direction+_crashed_aircraft_moddir[(r >> 16)&3]) & 7;
 
			SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
			r = Random();
 
			CreateEffectVehicleRel(v,
 
				4 + (r&0xF),
 
				4 + ((r>>4)&0xF),
 
				((r>>8)&0xF),
 
				EV_DEMOLISH);
 
		}
 
	} else if (v->u.air.crashed_counter >= 10000) {
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		// 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);
 

	
 
		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 const int8 _aircraft_smoke_xy[16] = {
 
	5,6,5,0,-5,-6,-5,0, /* x coordinates */
 
	5,0,-5,-6,-5,0,5,6, /* y coordinate */
 
};
 

	
 
static void HandleAircraftSmoke(Vehicle *v)
 
{
 
	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,
 
			_aircraft_smoke_xy[v->direction],
 
			_aircraft_smoke_xy[v->direction + 8],
 
			2,
 
			EV_SMOKE_3
 
		);
 
	}
 
}
 

	
 
static void ProcessAircraftOrder(Vehicle *v)
 
{
 
	uint order;
 

	
 
	// OT_GOTO_DEPOT, OT_LOADING
 
 	if ((v->next_order & OT_MASK) >= OT_GOTO_DEPOT && (v->next_order & OT_MASK) <= OT_LOADING) {
 
 		if ((v->next_order & (OT_MASK|OF_UNLOAD)) != (OT_GOTO_DEPOT|OF_UNLOAD))
 
 			return;
 
 	}
 
 
 
 	if ((v->next_order & (OT_MASK|OF_UNLOAD|OF_FULL_LOAD)) == (OT_GOTO_DEPOT|OF_UNLOAD|OF_FULL_LOAD) &&
 
 		v->date_of_last_service+v->service_interval > _date) {
 
 		v->cur_order_index++;
 
 	}
 

	
 
	if (v->cur_order_index >= v->num_orders)
 
		v->cur_order_index = 0;
 

	
 
	order = v->schedule_ptr[v->cur_order_index];
 

	
 
	if (order == 0) {
 
		v->next_order = OT_NOTHING;
 
		return;
 
	}
 

	
 
	if (order == (uint)((v->next_order | (v->next_order_param<<8))))
 
		return;
 

	
 
	v->next_order = (byte)order;
 
	v->next_order_param = (byte)(order >> 8);
 

	
 
	// orders are changed in flight, ensure going to the right station
 
	if ((order & OT_MASK) == OT_GOTO_STATION && v->u.air.state == FLYING) {
 
		AircraftNextAirportPos_and_Order(v);
 
		v->u.air.targetairport = order >> 8;
 
	}
 

	
 
	InvalidateVehicleOrderWidget(v);
 
}
 

	
 
static void HandleAircraftLoading(Vehicle *v, int mode)
 
{
 
	if (v->next_order == OT_NOTHING)
 
		return;
 

	
 
	if (v->next_order != OT_DUMMY) {
 
		if ((v->next_order&OT_MASK) != OT_LOADING)
 
			return;
 

	
 
		if (mode != 0)
 
			return;
 

	
 
		if (--v->load_unload_time_rem)
 
			return;
 

	
 
		if (v->next_order&OF_FULL_LOAD && CanFillVehicle(v)) {
 
			SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
 
			LoadUnloadVehicle(v);
 
			return;
 
		}
 

	
 
		{
 
			byte b = v->next_order;
 
			v->next_order = OT_NOTHING;
 
			if (!(b & OF_NON_STOP))
 
				return;
 
		}
 
	}
 
	v->cur_order_index++;
 
	InvalidateVehicleOrderWidget(v);
 
}
 

	
 
static void MaybeCrashAirplane(Vehicle *v)
 
{
 
	Station *st;
 
	uint16 prob;
 
	int i;
 
	uint16 amt;
 

	
 
	st = DEREF_STATION(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 && (_aircraft_subtype[v->engine_type - AIRCRAFT_ENGINES_INDEX]&2)) {
 
		prob = 0x10000 / 20;
 
	}
 

	
 
	if ((uint16)Random() > prob)
 
		return;
 

	
 
	// Crash the airplane. Remove all goods stored at the station.
 
	for(i=0; i!=NUM_CARGO; i++) {
 
		st->goods[i].rating = 1;
 
		st->goods[i].waiting_acceptance &= ~0xFFF;
 
	}
 

	
 
	v->vehstatus |= VS_CRASHED;
 
	v->u.air.crashed_counter = 0;
 

	
 
	CreateEffectVehicleRel(v, 4, 4, 8, EV_CRASHED_SMOKE);
 

	
 
	InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 

	
 
	amt = 2;
 
	if (v->cargo_type == CT_PASSENGERS) amt += v->cargo_count;
 
	SET_DPARAM16(0, amt);
 

	
 
	v->cargo_count = 0;
 
	v->next->cargo_count = 0,
 

	
 
	SET_DPARAM16(1, st->index);
 
	AddNewsItem(STR_A034_PLANE_CRASH_DIE_IN_FIREBALL,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0);
 

	
 
	ModifyStationRatingAround(TILE_FROM_XY(v->x_pos, v->y_pos), v->owner, -160, 30);
 
	SndPlayVehicleFx(16, v);
 
}
 

	
 
// we've landed and just arrived at a terminal
 
static void AircraftEntersTerminal(Vehicle *v)
 
{
 
	Station *st;
 
	byte old_order;
 

	
 
	if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT)
 
		return;
 

	
 
	st = DEREF_STATION(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;
 
		SET_DPARAM16(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->next_order;
 
	v->next_order = OT_LOADING;
 

	
 
	if ((old_order & OT_MASK) == OT_GOTO_STATION &&
 
			v->next_order_param == v->last_station_visited) {
 
		v->next_order = OT_LOADING | (old_order & (OF_UNLOAD|OF_FULL_LOAD)) | OF_NON_STOP;
 
	}
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_AIRCRAFT_INC);
 
	LoadUnloadVehicle(v);
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, 4);
 
}
 

	
 
static void AircraftEnterHangar(Vehicle *v)
 
{
 
	byte old_order;
 

	
 
	ServiceAircraft(v);
 
	
 
	MaybeRenewVehicle(v, EstimateAircraftCost(v->engine_type));
 

	
 
	if ((v->next_order & OT_MASK) == OT_GOTO_DEPOT) {
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 

	
 
		old_order = v->next_order;
 
		v->next_order = OT_NOTHING;
 

	
 
 			if (old_order & OF_UNLOAD) { v->cur_order_index++; }
 
 
 
 			else if (old_order & OF_FULL_LOAD) { // force depot visit
 
			v->vehstatus |= VS_STOPPED;
 

	
 
			if (v->owner == _local_player) {
 
				SET_DPARAM16(0, v->unitnumber);
 
				AddNewsItem(
 
					STR_A014_AIRCRAFT_IS_WAITING_IN,
 
					NEWS_FLAGS(NM_SMALL, NF_VIEWPORT|NF_VEHICLE, NT_ADVICE, 0),
 
					v->index,
 
					0);
 
			}
 
		}
 
	}
 
}
 

	
 
static void AircraftLand(Vehicle *v)
 
{
 
	v->sprite_width = v->sprite_height = 2;
 
}
 

	
 
static void AircraftLandAirplane(Vehicle *v)
 
{
 
	AircraftLand(v);
 
	SndPlayVehicleFx(0x15, v);
 
	MaybeCrashAirplane(v);
 
}
 

	
 
// set the right pos when heading to other airports after takeoff
 
static void AircraftNextAirportPos_and_Order(Vehicle *v)
 
{
 
	Station *st;
 
	const AirportFTAClass *Airport;
 

	
 
	if ((v->next_order&OT_MASK) == OT_GOTO_STATION ||
 
			(v->next_order&OT_MASK) == OT_GOTO_DEPOT)
 
			v->u.air.targetairport = v->next_order_param;
 

	
 
	st = DEREF_STATION(v->u.air.targetairport);
 
	Airport = GetAirport(st->airport_type);
 
	v->u.air.pos = v->u.air.previous_pos = Airport->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
 
		if ((u=u->next) != NULL) {
 
			u->vehstatus &= ~VS_HIDDEN;
 
			u->cur_speed = 80;
 
		}
 
	}
 

	
 
	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
}
 

	
 

	
 
////////////////////////////////////////////////////////////////////////////////
 
///////////////////   AIRCRAFT MOVEMENT SCHEME  ////////////////////////////////
 
////////////////////////////////////////////////////////////////////////////////
 
static void AircraftEventHandler_EnterTerminal(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AircraftEntersTerminal(v);
 
	v->u.air.state = Airport->layout[v->u.air.pos].heading;
 
}
 

	
 
static void AircraftEventHandler_EnterHangar(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AircraftEnterHangar(v);
 
	v->u.air.state = Airport->layout[v->u.air.pos].heading;
 
}
 

	
 
// In an Airport Hangar
 
static void AircraftEventHandler_InHangar(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// if we just arrived, execute EnterHangar first
 
	if (v->u.air.previous_pos != v->u.air.pos) {
 
		AircraftEventHandler_EnterHangar(v, Airport);
 
		return;
 
	}
 

	
 
	if ((v->next_order&OT_MASK) == OT_GOTO_DEPOT && (v->vehstatus&VS_STOPPED)) { // if we were sent to the depot, stay there
 
		v->next_order = OT_NOTHING;
 
		return;
 
	}
 

	
 
	if ((v->next_order&OT_MASK) != OT_GOTO_STATION && (v->next_order&OT_MASK) != OT_GOTO_DEPOT)
 
		return;
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {return;}
 

	
 
	// We are already at the target airport, we need to find a terminal
 
	if (v->next_order_param == 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, Airport)) {return;}} // airplane
 
		else {if(!AirportFindFreeHelipad(v, Airport)) {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, Airport);
 
}
 

	
 
// At one of the Airport's Terminals
 
static void AircraftEventHandler_AtTerminal(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// if we just arrived, execute EnterTerminal first
 
	if (v->u.air.previous_pos != v->u.air.pos) {
 
		AircraftEventHandler_EnterTerminal(v, Airport);
 
		// 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 && Airport->nofhelipads > 0) {
 
				// an exerpt of ServiceAircraft, without the invisibility stuff
 
				v->date_of_last_service = _date;
 
				v->breakdowns_since_last_service = 0;
 
				v->reliability = _engines[v->engine_type].reliability;
 
				InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
			}
 
		}
 
		return;
 
	}
 

	
 
	// removed &0x1F
 
	if (v->next_order == OT_NOTHING) {return;}
 

	
 
	// if the block of the next position is busy, stay put
 
	if (AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {
 
		return;
 
	}
 

	
 
	// airport-road is free. We either have to go to another airport, or to the hangar
 
	// ---> start moving
 

	
 
	switch (v->next_order&OT_MASK) {
 
		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->next_order_param == 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->next_order  = OT_NOTHING;
 
			v->u.air.state = HANGAR;
 
	}
 
	AirportMove(v, Airport);
 
}
 

	
 
static void AircraftEventHandler_General(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	printf("OK, you shouldn't be here, check your Airport Scheme!\n");
 
	assert(1 == v->u.air.state); // when here state is 0, so this always fails
 
}
 

	
 
static void AircraftEventHandler_TakeOff(Vehicle *v, const AirportFTAClass *Airport) {
 
	PlayAircraftSound(v); // play takeoffsound for airplanes
 
	v->u.air.state = STARTTAKEOFF;
 
}
 

	
 
static void AircraftEventHandler_StartTakeOff(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	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 *Airport)
 
{
 
	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 *Airport)
 
{
 
	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);
 
}
 

	
 
static void AircraftEventHandler_Flying(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Station *st;
 
	byte landingtype;
 
	AirportFTA *current;
 
	uint16 tcur_speed, tsubspeed;
 

	
 
	st = DEREF_STATION(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 == Airport->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 = Airport->layout[v->u.air.pos].next_in_chain;
 
		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, Airport)) {
 
					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, Airport->layout[v->u.air.pos].block);
 
					return;
 
				}
 
				v->cur_speed = tcur_speed;
 
				v->subspeed = tsubspeed;
 
			}
 
			current = current->next_in_chain;
 
		}
 
	}
 
	v->u.air.state = FLYING;
 
	v->u.air.pos = Airport->layout[v->u.air.pos].next_position;
 
}
 

	
 
static void AircraftEventHandler_Landing(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AircraftLandAirplane(v);  // maybe crash airplane
 
	v->u.air.state = ENDLANDING;
 
}
 

	
 
static void AircraftEventHandler_HeliLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AircraftLand(v); // helicopters don't crash
 
	v->u.air.state = HELIENDLANDING;
 
}
 

	
 
static void AircraftEventHandler_EndLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if(AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {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->next_order&OT_MASK) == OT_GOTO_STATION) {
 
		if (AirportFindFreeTerminal(v, Airport)) {return;}
 
	}
 
	v->u.air.state = HANGAR;
 

	
 
}
 

	
 
static void AircraftEventHandler_HeliEndLanding(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// next block busy, don't do a thing, just wait
 
	if(AirportHasBlock(v, &Airport->layout[v->u.air.pos], Airport)) {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->next_order&OT_MASK) == OT_GOTO_STATION) {
 
		if (AirportFindFreeHelipad(v, Airport)) {return;}
 
	}
 
	v->u.air.state = (Airport->nofterminals != 0) ? HANGAR : HELITAKEOFF;
 
}
 

	
 
typedef void AircraftStateHandler(Vehicle *v, const AirportFTAClass *Airport);
 
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
 
};
 

	
 
static void AirportClearBlock(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	Station *st;
 
	// we have left the previous block, and entered the new one. Free the previous block
 
	if (Airport->layout[v->u.air.previous_pos].block != Airport->layout[v->u.air.pos].block) {
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		CLRBITS(st->airport_flags, Airport->layout[v->u.air.previous_pos].block);
 
	}
 
}
 

	
 
static void AirportGoToNextPosition(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	// if aircraft is not in position, wait until it is
 
	if (!Aircraft_5(v)) {return;}
 

	
 
	AirportClearBlock(v, Airport);
 
	AirportMove(v, Airport); // move aircraft to next position
 
}
 

	
 
// gets pos from vehicle and next orders
 
static bool AirportMove(Vehicle *v, const AirportFTAClass *Airport)
 
{
 
	AirportFTA *current;
 
	byte prev_pos;
 
	bool retval = false;
 

	
 
	// error handling
 
	if (v->u.air.pos >= Airport->nofelements) {
 
		printf("position %d is not valid for current airport. Max position is %d\n", v->u.air.pos, Airport->nofelements-1);
 
		assert(v->u.air.pos < Airport->nofelements);
 
	}
 

	
 
	current = &Airport->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, Airport);
 
		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_in_chain == NULL) {
 
		if (AirportSetBlocks(v, current, Airport)) {
 
			v->u.air.pos = current->next_position;
 
		} // move to next position
 
		return retval;
 
	}
 

	
 
	// 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, Airport)) {
 
						v->u.air.pos = current->next_position;
 
					} // move to next position
 
					return retval;
 
		}
 
		current = current->next_in_chain;
 
	} while (current != NULL);
 

	
 
	printf("Cannot move further on Airport...! pos:%d state:%d\n", v->u.air.pos, v->u.air.state);
 
	printf("Airport entry point: %d, Vehicle: %d\n", Airport->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, AirportFTA *current_pos, const AirportFTAClass *Airport)
 
{
 
	Station *st;
 
	uint32 airport_flags;
 
	AirportFTA *next, *reference;
 
	reference = &Airport->layout[v->u.air.pos];
 
	next = &Airport->layout[current_pos->next_position];
 

	
 
	// same block, then of course we can move
 
	if (Airport->layout[current_pos->position].block != next->block) {
 
		airport_flags = next->block;
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		// 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 *Airport)
 
{
 
	Station *st;
 
	uint32 airport_flags;
 
	AirportFTA *current, *reference, *next;
 
	next = &Airport->layout[current_pos->next_position];
 
	reference = &Airport->layout[v->u.air.pos];
 

	
 
	// if the next position is in another block, check it and wait until it is free
 
	if (Airport->layout[current_pos->position].block != next->block) {
 
		airport_flags = next->block;
 
		st = DEREF_STATION(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
 
		current = current_pos;
 
		if (current == reference) { current = current->next_in_chain;}
 
		while (current != NULL) {
 
			if (current->heading == current_pos->heading && current->block != 0) {
 
				airport_flags |= current->block;
 
				break;
 
			}
 
			current = current->next_in_chain;
 
		};
 

	
 
		// 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 = DEREF_STATION(v->u.air.targetairport);
 
	for (; i < last_terminal; i++) {
 
		if (!HASBIT(st->airport_flags, i)) {
 
			// TERMINAL# HELIPAD#
 
			v->u.air.state = i + TERM1; // start moving to that terminal/helipad
 
			SETBIT(st->airport_flags, i); // occupy terminal/helipad
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
static bool AirportFindFreeTerminal(Vehicle *v, const AirportFTAClass *Airport) 
 
{
 
	byte nofterminalspergroup, i;
 
	AirportFTA *temp;
 
	Station *st;
 

	
 
	/* 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 (Airport->nofterminalgroups > 1) {
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		nofterminalspergroup = Airport->nofterminals / Airport->nofterminalgroups;
 
		temp = Airport->layout[v->u.air.pos].next_in_chain;
 
		while (temp != NULL) {
 
			if (temp->heading == 255) {
 
				if (!HASBITS(st->airport_flags, temp->block)) {
 
					i = temp->next_position * nofterminalspergroup; // next_position denotes the group to check
 
					// only that group will be checked (eg 6 terms, 2 groups)
 
					// with i = 0 terms 1 - 3 and
 
					// with i = 1 terms 4 - 6
 
					if (FreeTerminal(v, i, i + nofterminalspergroup)) {return true;}
 
				}
 
			}
 
			else {return false;} // once the heading isn't 255, we've exhausted the possible blocks. So we cannot move
 
			temp = temp->next_in_chain;
 
		}
 
	}
 

	
 
	// if there is only 1 terminalgroup, all terminals are checked (starting from 0 to max)
 
	return FreeTerminal(v, 0, Airport->nofterminals);
 
}
 

	
 
static bool AirportFindFreeHelipad(Vehicle *v, const AirportFTAClass *Airport) 
 
{
 
  Station *st;
 
  byte nofhelipadspergroup,  i;
 
  AirportFTA *temp;
 

	
 
	// if an airport doesn't have helipads, use terminals
 
	if (Airport->nofhelipads == 0) {return AirportFindFreeTerminal(v, Airport);}
 

	
 
	// if there are more helicoptergroups, pick one, just as in AirportFindFreeTerminal()
 
	if (Airport->nofhelipadgroups > 1) {
 
		st = DEREF_STATION(v->u.air.targetairport);
 
		nofhelipadspergroup = Airport->nofhelipads / Airport->nofhelipadgroups;
 
		temp = Airport->layout[v->u.air.pos].next_in_chain;
 
		while (temp != NULL) {
 
			if (temp->heading == 255) {
 
				if (!HASBITS(st->airport_flags, temp->block)) {
 
					i = temp->next_position * nofhelipadspergroup; // next position is the group to check
 
					// heliports start from after TERMINALS, so MAX_TERMINALS needs to be added
 
					if (FreeTerminal(v, i + MAX_TERMINALS, i + MAX_TERMINALS + nofhelipadspergroup)) {return true;}
 
				}
 
			}
 
			else {return false;} // once the heading isn't 255, we've exhausted the possible blocks. So we cannot move
 
			temp = temp->next_in_chain;
 
		}
 
	}
 
	// only 1 helicoptergroup, check all helipads
 
	// The blocks for helipads start after the last terminal (MAX_TERMINALS)
 
	else {return FreeTerminal(v, MAX_TERMINALS, Airport->nofhelipads + 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;
 
	}
 

	
 
	/* exit if aircraft is stopped */
 
	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->next_order&OT_MASK) >= 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(DEREF_STATION(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;
 
	}
 
}
 

	
 
// need to be called to load aircraft from old version
 
void UpdateOldAircraft()
 
{
 
	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 Aircraft_5. 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;
 
						}
 
					}
 
				}
 
			}
 
		}
 
	}
 
}
aircraft_gui.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "vehicle.h"
 
#include "gfx.h"
 
#include "station.h"
 
#include "command.h"
 
#include "engine.h"
 
#include "viewport.h"
 
#include "player.h"
 

	
 
extern const byte _aircraft_cost_table[NUM_AIRCRAFT_ENGINES];
 
extern const byte _aircraft_speed[NUM_AIRCRAFT_ENGINES];
 
extern const uint16 _aircraft_num_pass[NUM_AIRCRAFT_ENGINES];
 
extern const byte _aircraft_num_mail[NUM_AIRCRAFT_ENGINES];
 
extern const byte _aircraft_running_cost[NUM_AIRCRAFT_ENGINES];
 

	
 

	
 
static void DrawAircraftImage(Vehicle *v, int x, int y, VehicleID selection)
 
{
 
	int image = GetAircraftImage(v, 6);
 
	uint32 ormod = SPRITE_PALETTE(PLAYER_SPRITE_COLOR(v->owner));
 
	if (v->vehstatus & VS_CRASHED) ormod = 0x3248000;
 
	DrawSprite(image | ormod, x+25, y+10);
 
	if (v->subtype == 0)
 
		DrawSprite(0xF3D, x+25, y+5);
 
	if (v->index == selection) {
 
		DrawFrameRect(x-1, y-1, x+58, y+21, 0xF, 0x10);
 
	}
 
}
 

	
 
static void CcBuildAircraft(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (success) {
 
		v = &_vehicles[_new_aircraft_id];
 
		if (v->tile == _backup_orders_tile) {
 
			_backup_orders_tile = 0;
 
			RestoreVehicleOrders(v, _backup_orders_data);
 
		}
 
		ShowAircraftViewWindow(v);
 
	}
 
}
 

	
 

	
 
static void NewAircraftWndProc(Window *w, WindowEvent *e)
 
{
 
	YearMonthDay ymd;
 

	
 
	switch(e->event) {
 

	
 
	case WE_PAINT: {
 
		if (w->window_number == 0)
 
			SETBIT(w->disabled_state, 5);
 

	
 
		{
 
			int count = 0;
 
			int num = NUM_AIRCRAFT_ENGINES;
 
			Engine *e = &_engines[AIRCRAFT_ENGINES_INDEX];
 
			do {
 
				if (HASBIT(e->player_avail, _local_player))
 
					count++;
 
			} while (++e,--num);
 
			SetVScrollCount(w, count);
 
		}
 

	
 
		DrawWindowWidgets(w);
 

	
 
		{
 
			int num = NUM_AIRCRAFT_ENGINES;
 
			Engine *e = &_engines[AIRCRAFT_ENGINES_INDEX];
 
			int x = 2;
 
			int y = 15;
 
			int sel = WP(w,buildtrain_d).sel_index;
 
			int pos = w->vscroll.pos;
 
			int engine_id = AIRCRAFT_ENGINES_INDEX;
 
			int selected_id = -1;
 

	
 
			do {
 
				if (HASBIT(e->player_avail, _local_player)) {
 
					if (sel==0) selected_id = engine_id;
 
					if (IS_INT_INSIDE(--pos, -4, 0)) {
 
						DrawString(x+62, y+7, GetCustomEngineName(engine_id), sel==0 ? 0xC : 0x10);
 
						DrawAircraftEngine(x+29, y+10, engine_id, SPRITE_PALETTE(PLAYER_SPRITE_COLOR(_local_player)));
 
						y += 24;
 
					}
 
					sel--;
 
				}
 
			} while (++engine_id, ++e,--num);
 

	
 
			WP(w,buildtrain_d).sel_engine = selected_id;
 

	
 
			if (selected_id != -1) {
 
				Engine *e;
 

	
 
				SET_DPARAM32(0, _aircraft_cost_table[selected_id - AIRCRAFT_ENGINES_INDEX] * (_price.aircraft_base>>3)>>5);
 
				SET_DPARAM16(1, _aircraft_speed[selected_id - AIRCRAFT_ENGINES_INDEX] * 8);
 
				SET_DPARAM16(2, _aircraft_num_pass[selected_id - AIRCRAFT_ENGINES_INDEX]);
 
				SET_DPARAM16(3, _aircraft_num_mail[selected_id - AIRCRAFT_ENGINES_INDEX]);
 
				SET_DPARAM32(4,_aircraft_running_cost[selected_id - AIRCRAFT_ENGINES_INDEX] * _price.aircraft_running >> 8);
 

	
 
				e = &_engines[selected_id];
 
				SET_DPARAM16(6, e->lifelength);
 
				SET_DPARAM8(7, e->reliability * 100 >> 16);
 
				ConvertDayToYMD(&ymd, e->intro_date);
 
				SET_DPARAM16(5, ymd.year + 1920);
 

	
 
				DrawString(2, 111, STR_A007_COST_SPEED_CAPACITY_PASSENGERS, 0);
 
			}
 
		}
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch(e->click.widget) {
 
		case 2: { /* listbox */
 
			uint i = (e->click.pt.y - 14) / 24;
 
			if (i < 4) {
 
				WP(w,buildtrain_d).sel_index = i + w->vscroll.pos;
 
				SetWindowDirty(w);
 
			}
 
		} break;
 

	
 
		case 5: { /* build */
 
			int sel_eng = WP(w,buildtrain_d).sel_engine;
 
			if (sel_eng != -1)
 
				DoCommandP(w->window_number, sel_eng, 0, CcBuildAircraft, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT));
 
		} break;
 

	
 
		case 6:	/* rename */
 
			WP(w,buildtrain_d).rename_engine = WP(w,buildtrain_d).sel_engine;
 
			ShowQueryString(
 
				GetCustomEngineName(WP(w,buildtrain_d).sel_engine),
 
				STR_A039_RENAME_AIRCRAFT_TYPE,
 
				31,
 
				160,
 
				w->window_class,
 
				w->window_number);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_4:
 
		if (w->window_number != 0 && !FindWindowById(WC_VEHICLE_DEPOT, w->window_number)) {
 
			DeleteWindow(w);
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		byte *b = e->edittext.str;
 
		if (*b == 0)
 
			return;
 
		memcpy(_decode_parameters, b, 32);
 
		DoCommandP(0, WP(w,buildtrain_d).rename_engine, 0, NULL, CMD_RENAME_ENGINE | CMD_MSG(STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE));
 
	} break;
 
	}
 
}
 

	
 
static const Widget _new_aircraft_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   239,     0,    13, STR_A005_NEW_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,    14,     0,   228,    14,   109, 0x401, STR_A025_AIRCRAFT_SELECTION_LIST},
 
{  WWT_SCROLLBAR,    14,   229,   239,    14,   109, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{     WWT_IMGBTN,    14,     0,   239,   110,   161, 0x0, 0},
 
{ WWT_PUSHTXTBTN,    14,     0,   119,   162,   173, STR_A006_BUILD_AIRCRAFT, STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT},
 
{ WWT_PUSHTXTBTN,    14,   120,   239,   162,   173, STR_A037_RENAME, STR_A038_RENAME_AIRCRAFT_TYPE},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _new_aircraft_desc = {
 
	-1, -1, 240, 174,
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_new_aircraft_widgets,
 
	NewAircraftWndProc
 
};
 

	
 
static void ShowBuildAircraftWindow(uint tile)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 

	
 
	w = AllocateWindowDesc(&_new_aircraft_desc);
 
	w->window_number = tile;
 
	w->vscroll.cap = 4;
 

	
 
	if (tile != 0) {
 
		w->caption_color = _map_owner[tile];
 
	} else {
 
		w->caption_color = _local_player;
 
	}
 
}
 

	
 
const byte _aircraft_refit_normal[] = { 0,1,4,5,6,7,8,9,10,0xFF };
 
const byte _aircraft_refit_arctic[] = { 0,1,4,5,6,7,9,11,10,0xFF };
 
const byte _aircraft_refit_desert[] = { 0,4,5,8,6,7,9,10,0xFF };
 
const byte _aircraft_refit_candy[] = { 0,1,3,5,7,8,9,6,4,10,11,0xFF };
 

	
 
const byte * const _aircraft_refit_types[4] = {
 
	_aircraft_refit_normal, _aircraft_refit_arctic, _aircraft_refit_desert, _aircraft_refit_candy
 
};
 

	
 
static void AircraftRefitWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		Vehicle *v = &_vehicles[w->window_number];
 
		const byte *b;
 
		int sel;
 
		int x,y;
 
		byte color;
 
		int cargo;
 

	
 
		SET_DPARAM16(0, v->string_id);
 
		SET_DPARAM16(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		DrawString(1, 15, STR_A040_SELECT_CARGO_TYPE_TO_CARRY, 0);
 

	
 
		cargo = -1;
 
		x = 6;
 
		y = 25;
 
		sel = WP(w,refit_d).sel;
 
		b = _aircraft_refit_types[_opt.landscape];
 
		do {
 
			color = 16;
 
			if (sel == 0) {
 
				cargo = *b;
 
				color = 12;
 
			}
 
			sel--;
 
			DrawString(x,y,_cargoc.names_s[*b], color);
 
			y += 10;
 
		} while (*++b != 0xFF);
 

	
 
		WP(w,refit_d).cargo = cargo;
 

	
 
		if (cargo != -1) {
 
			int32 cost = DoCommandByTile(v->tile, v->index, cargo, DC_QUERY_COST, CMD_REFIT_AIRCRAFT);
 
			if (cost != CMD_ERROR) {
 
				SET_DPARAM32(2, cost);
 
				SET_DPARAM16(0, _cargoc.names_long_p[cargo]);
 
				SET_DPARAM16(1, _aircraft_refit_capacity);
 
				DrawString(1, 137, STR_A041_NEW_CAPACITY_COST_OF_REFIT, 0);
 
			}
 
		}
 

	
 
		break;
 
	}
 

	
 
	case WE_CLICK:
 
		switch(e->click.widget) {
 
		case 2: { /* listbox */
 
				int y = e->click.pt.y - 25;
 
				if (y >= 0) {
 
					WP(w,refit_d).sel = y / 10;
 
					SetWindowDirty(w);
 
				}
 
			} break;
 
		case 4: /* refit button */
 
			if (WP(w,refit_d).cargo != 0xFF) {
 
				Vehicle *v = &_vehicles[w->window_number];
 
				if (DoCommandP(v->tile, v->index, WP(w,refit_d).cargo, NULL, CMD_REFIT_AIRCRAFT | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT)))
 
					DeleteWindow(w);
 
			}
 
		  break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _aircraft_refit_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   239,     0,    13, STR_A03C_REFIT, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,    14,     0,   239,    14,   135, 0x0, STR_A03E_SELECT_TYPE_OF_CARGO_FOR},
 
{     WWT_IMGBTN,    14,     0,   239,   136,   157, 0x0},
 
{ WWT_PUSHTXTBTN,    14,     0,   239,   158,   169, STR_A03D_REFIT_AIRCRAFT, STR_A03F_REFIT_AIRCRAFT_TO_CARRY},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _aircraft_refit_desc = {
 
	-1,-1, 240, 170,
 
	WC_VEHICLE_REFIT,WC_VEHICLE_VIEW,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_aircraft_refit_widgets,
 
	AircraftRefitWndProc
 
};
 

	
 
static void ShowAircraftRefitWindow(Vehicle *v)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_VEHICLE_REFIT, v->index);
 

	
 
	_alloc_wnd_parent_num = v->index;
 
	w = AllocateWindowDesc(&_aircraft_refit_desc);
 
	w->window_number = v->index;
 
	w->caption_color = v->owner;
 
	WP(w,refit_d).sel = -1;
 
}
 

	
 
static void AircraftDetailsWndProc(Window *w, WindowEvent *e)
 
{
 
	int mod;
 
	Vehicle *v = &_vehicles[w->window_number], *u;
 

	
 
	switch(e->event) {
 
	case WE_PAINT:
 
		w->disabled_state = v->owner == _local_player ? 0 : (1 << 2);
 
		SET_DPARAM16(0, v->string_id);
 
		SET_DPARAM16(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		/* Draw running cost */
 
		{
 
			int year = v->age / 366;
 
			StringID str;
 

	
 
			SET_DPARAM16(1, year);
 

	
 
			str = STR_0199_YEAR;
 
			if (year != 1) {
 
				str++;
 
				if (v->max_age - 366 < v->age)
 
					str++;
 
			}
 
			SET_DPARAM16(0, str);
 
			SET_DPARAM16(2, v->max_age / 366);
 
			SET_DPARAM32(3, _price.aircraft_running * _aircraft_running_cost[v->engine_type - AIRCRAFT_ENGINES_INDEX] >> 8);
 
			DrawString(2, 15, STR_A00D_AGE_RUNNING_COST_YR, 0);
 
		}
 

	
 
		/* Draw max speed */
 
		{
 
			SET_DPARAM16(0, v->max_speed * 8);
 
			DrawString(2, 25, STR_A00E_MAX_SPEED, 0);
 
		}
 

	
 
		/* Draw profit */
 
		{
 
			SET_DPARAM32(0, v->profit_this_year);
 
			SET_DPARAM32(1, v->profit_last_year);
 
			DrawString(2, 35, STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 
		}
 

	
 
		/* Draw breakdown & reliability */
 
		{
 
			SET_DPARAM8(0, v->reliability * 100 >> 16);
 
			SET_DPARAM16(1, v->breakdowns_since_last_service);
 
			DrawString(2, 45, STR_A010_RELIABILITY_BREAKDOWNS, 0);
 
		}
 

	
 
		/* Draw service interval text */
 
		{
 
			SET_DPARAM16(0, v->service_interval);
 
			SET_DPARAM16(1, v->date_of_last_service);
 
			DrawString(13, 103, STR_883C_SERVICING_INTERVAL_DAYS, 0);
 
		}
 

	
 
		DrawAircraftImage(v, 3, 57, INVALID_VEHICLE);
 

	
 
		{
 
			int y = 57;
 

	
 
			do {
 
				if (v->subtype <= 2) {
 
					SET_DPARAM16(0, GetCustomEngineName(v->engine_type));
 
					SET_DPARAM16(1, 1920 + v->build_year);
 
					SET_DPARAM32(2, v->value);
 
					DrawString(60, y, STR_A011_BUILT_VALUE, 0);
 
					y += 10;
 

	
 
					SET_DPARAM16(0, _cargoc.names_long_p[v->cargo_type]);
 
					SET_DPARAM16(1, v->cargo_cap);
 
					u = v->next;
 
					SET_DPARAM16(2, _cargoc.names_long_p[u->cargo_type]);
 
					SET_DPARAM16(3, u->cargo_cap);
 
					DrawString(60, y, STR_A019_CAPACITY + (u->cargo_cap == 0), 0);
 
					y += 14;
 
				}
 

	
 
				if (v->cargo_count != 0) {
 

	
 
					/* Cargo names (fix pluralness) */
 
					SET_DPARAM8(0, v->cargo_type);
 
					SET_DPARAM16(1, v->cargo_count);
 
					SET_DPARAM16(2, v->cargo_source);
 
					DrawString(60, y, STR_8813_FROM, 0);
 

	
 
					y += 10;
 
				}
 
			} while ( (v=v->next) != NULL);
 
		}
 
		break;
 

	
 
	case WE_CLICK:
 
		switch(e->click.widget) {
 
		case 2: /* rename */
 
			SET_DPARAM16(0, v->unitnumber);
 
			ShowQueryString(v->string_id, STR_A030_NAME_AIRCRAFT, 31, 150, w->window_class, w->window_number);
 
			break;
 

	
 
		case 5: /* increase int */
 
			mod = 10;
 
			goto change_int;
 
		case 6: /* decrease int */
 
			mod = -10;
 
change_int:
 
			mod += v->service_interval;
 
			if (!IS_INT_INSIDE(mod, 30, 800+1))
 
				return;
 
			DoCommandP(v->tile, v->index, mod, NULL,
 
				CMD_CHANGE_AIRCRAFT_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
 
			break;
 
		}
 
		break;
 

	
 
	case WE_4:
 
		if (FindWindowById(WC_VEHICLE_VIEW, w->window_number) == NULL)
 
			DeleteWindow(w);
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		byte *b = e->edittext.str;
 
		if (*b == 0)
 
			return;
 
		memcpy(_decode_parameters, b, 32);
 
		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_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   349,     0,    13, STR_A00C_DETAILS, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{ WWT_PUSHTXTBTN,    14,   350,   389,     0,    13, STR_01AA_NAME, STR_A032_NAME_AIRCRAFT},
 
{     WWT_IMGBTN,    14,     0,   389,    14,    55, 0x0},
 
{     WWT_IMGBTN,    14,     0,   389,    56,   101, 0x0},
 
{ WWT_PUSHTXTBTN,    14,     0,    10,   102,   107, STR_0188, STR_884D_INCREASE_SERVICING_INTERVAL},
 
{ WWT_PUSHTXTBTN,    14,     0,    10,   108,   113, STR_0189, STR_884E_DECREASE_SERVICING_INTERVAL},
 
{     WWT_IMGBTN,    14,    11,   389,   102,   113, 0x0},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _aircraft_details_desc = {
 
	-1,-1, 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(Vehicle *v)
 
{
 
	Window *w;
 
	VehicleID veh = v->index;
 

	
 
	DeleteWindowById(WC_VEHICLE_ORDERS, veh);
 
	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 

	
 
	_alloc_wnd_parent_num = veh;
 
	w = AllocateWindowDesc(&_aircraft_details_desc);
 
	w->window_number = veh;
 
	w->caption_color = v->owner;
 
//	w->vscroll.cap = 6;
 
//	w->traindetails_d.tab = 0;
 
}
 

	
 

	
 
static const Widget _aircraft_view_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   249,     0,    13, STR_A00A, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,    14,     0,   231,    14,   103, 0x0, 0},
 
{          WWT_6,    14,     2,   229,    16,   101, 0x0, 0},
 
{ WWT_PUSHIMGBTN,    14,     0,   249,   104,   115, 0x0, STR_A027_CURRENT_AIRCRAFT_ACTION},
 
{ WWT_PUSHIMGBTN,    14,   232,   249,    14,    31, 0x2AB, STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT},
 
{ WWT_PUSHIMGBTN,    14,   232,   249,    32,    49, 0x2AF, STR_A02A_SEND_AIRCRAFT_TO_HANGAR},
 
{ WWT_PUSHIMGBTN,    14,   232,   249,    50,    67, 0x2B4, STR_A03B_REFIT_AIRCRAFT_TO_CARRY},
 
{ WWT_PUSHIMGBTN,    14,   232,   249,    68,    85, 0x2B2, STR_A028_SHOW_AIRCRAFT_S_ORDERS},
 
{ WWT_PUSHIMGBTN,    14,   232,   249,    86,   103, 0x2B3, STR_A02B_SHOW_AIRCRAFT_DETAILS},
 
{      WWT_LAST},
 
};
 

	
 
static void AircraftViewWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		Vehicle *v = &_vehicles[w->window_number];
 
		uint32 disabled = 1<<7;
 
		StringID str;
 

	
 
		{
 
			uint tile = v->tile;
 
			if (IS_TILETYPE(tile, MP_STATION) &&
 
					(_map5[tile] == 32 || _map5[tile] == 65) &&
 
					v->vehstatus&VS_STOPPED)
 
						disabled = 0;
 
		}
 

	
 
		if (v->owner != _local_player)
 
			disabled |= 1<<7 | 1<<6;
 
		w->disabled_state = disabled;
 

	
 
		/* draw widgets & caption */
 
		SET_DPARAM16(0, v->string_id);
 
		SET_DPARAM16(1, v->unitnumber);
 
		DrawWindowWidgets(w);
 

	
 
		/* draw the flag */
 
		DrawSprite((v->vehstatus & VS_STOPPED) ? 0xC12  : 0xC13, 2, 105);
 

	
 
		if (v->vehstatus & VS_CRASHED) {
 
			str = STR_8863_CRASHED;
 
		} else if (v->vehstatus & VS_STOPPED) {
 
			str = STR_8861_STOPPED;
 
		} else {
 
			switch(v->next_order & OT_MASK) {
 
			case OT_GOTO_STATION: {
 
				SET_DPARAM16(0, v->next_order_param);
 
				SET_DPARAM16(1, v->cur_speed * 8);
 
				str = STR_HEADING_FOR_STATION + _patches.vehicle_speed;
 
			} break;
 

	
 
			case OT_GOTO_DEPOT: {
 
				SET_DPARAM16(0, v->next_order_param);
 
				SET_DPARAM16(1, v->cur_speed * 8);
 
				str = STR_HEADING_FOR_HANGAR + _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;
 
					SET_DPARAM16(0, v->cur_speed * 8);
 
				} else
 
					str = STR_EMPTY;
 
				break;
 
			}
 
		}
 

	
 
		DrawStringCentered(125, 105, str, 0);
 
		DrawWindowViewport(w);
 
	} break;
 

	
 
	case WE_CLICK: {
 
		Vehicle *v = &_vehicles[w->window_number];
 

	
 
		switch(e->click.widget) {
 
		case 4: /* start stop */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT));
 
			break;
 
		case 5: /* center main view */
 
			ScrollMainWindowTo(v->x_pos, v->y_pos);
 
			break;
 
		case 6: /* goto hangar */
 
			DoCommandP(v->tile, v->index, 0, NULL, CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO));
 
			break;
 
		case 7: /* refit */
 
			ShowAircraftRefitWindow(v);
 
			break;
 
		case 8: /* show orders */
 
			ShowOrdersWindow(v);
 
			break;
 
		case 9: /* show details */
 
			ShowAircraftDetailsWindow(v);
 
			break;
 
		}
 
	} 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;
 
	}
 
}
 

	
 

	
 
static const WindowDesc _aircraft_view_desc = {
 
	-1,-1, 250, 116,
 
	WC_VEHICLE_VIEW ,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_aircraft_view_widgets,
 
	AircraftViewWndProc
 
};
 

	
 

	
 
void ShowAircraftViewWindow(Vehicle *v)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDescFront(&_aircraft_view_desc, v->index);
 
	if (w) {
 
		w->caption_color = v->owner;
 
		AssignWindowViewport(w, 3, 17, 0xE2, 0x54, w->window_number | (1 << 31), 0);
 
	}
 
}
 

	
 
static void DrawAircraftDepotWindow(Window *w)
 
{
 
	uint tile;
 
	Vehicle *v;
 
	int num,x,y;
 

	
 
	tile = w->window_number;
 

	
 
	/* setup disabled buttons */
 
	w->disabled_state = (_map_owner[tile]==_local_player) ? 0 : ((1<<3)|(1<<5));
 

	
 
	/* determine amount of items for scroller */
 
	num = 0;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Aircraft && v->subtype <= 2 && v->vehstatus&VS_HIDDEN &&
 
				v->tile == (TileIndex)tile)
 
					num++;
 
	}
 
	SetVScrollCount(w, (num + 3) >> 2);
 

	
 
	SET_DPARAM16(0, _map2[tile]);
 
	DrawWindowWidgets(w);
 

	
 
	x = 2;
 
	y = 15;
 
	num = w->vscroll.pos * 4;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Aircraft &&
 
				v->subtype <= 2 &&
 
				v->vehstatus&VS_HIDDEN &&
 
				v->tile == (TileIndex)tile &&
 
				--num < 0 && num >=	-8) {
 

	
 
			DrawAircraftImage(v, x+12, y, WP(w,traindepot_d).sel);
 

	
 
			SET_DPARAM16(0, v->unitnumber);
 
			DrawString(x, y+2, (uint16)(v->max_age-366) >= v->age ? STR_00E2 : STR_00E3, 0);
 

	
 
			DrawSprite( (v->vehstatus & VS_STOPPED) ? 0xC12 : 0xC13, x, y+12);
 

	
 
			if ((x+=74) == 2+74*4) {
 
				x -= 74*4;
 
				y += 24;
 
			}
 
		}
 
	}
 
}
 

	
 
static int GetVehicleFromAircraftDepotWndPt(Window *w, int x, int y, Vehicle **veh) {
 
	uint xt,yt,xm,ym;
 
	Vehicle *v;
 
	uint tile;
 
	int pos;
 

	
 
	xt = x / 74;
 
	xm = x % 74;
 
	if (xt >= 4)
 
		return 1;
 

	
 
	yt = (y - 14) / 24;
 
	ym = (y - 14) % 24;
 
	if (yt >= 2)
 
		return 1;
 

	
 
	pos = (yt + w->vscroll.pos) * 4 + xt;
 

	
 
	tile = w->window_number;
 
	FOR_ALL_VEHICLES(v) {
 
		if (v->type == VEH_Aircraft &&
 
				v->subtype <= 2 &&
 
				v->vehstatus&VS_HIDDEN &&
 
				v->tile == (TileIndex)tile &&
 
				--pos < 0) {
 
					*veh = v;
 
					if (xm >= 12)
 
						return 0;
 

	
 
					if (ym <= 12)
 
						return -1; /* show window */
 

	
 
					return -2; /* start stop */
 
				}
 
	}
 
	return 1; /* outside */
 
}
 

	
 
static void AircraftDepotClickAircraft(Window *w, int x, int y)
 
{
 
	Vehicle *v;
 

	
 
	switch(GetVehicleFromAircraftDepotWndPt(w, x, y, &v)) {
 
	case 1:
 
		return;
 

	
 
	case 0:
 
		if (v != NULL) {
 
			WP(w,traindepot_d).sel = v->index;
 
			SetWindowDirty(w);
 
			SetObjectToPlaceWnd( SPRITE_PALETTE(PLAYER_SPRITE_COLOR(v->owner)) + GetAircraftImage(v, 6), 4, w);
 
		}
 
		break;
 

	
 
	case -1:
 
		ShowAircraftViewWindow(v);
 
		break;
 

	
 
	case -2:
 
		DoCommandP(v->tile, v->index, 0, NULL, CMD_START_STOP_AIRCRAFT | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT));
 
		break;
 

	
 
	default:
 
		NOT_REACHED();
 
	}
 
}
 

	
 
static const Widget _aircraft_depot_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   330,     0,    13, STR_A002_AIRCRAFT_HANGAR, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,    14,     0,   295,    14,    61, 0x204, STR_A021_AIRCRAFT_CLICK_ON_AIRCRAFT},
 
{     WWT_IMGBTN,    14,   296,   319,    14,    61, 0x2A9, STR_A023_DRAG_AIRCRAFT_TO_HERE_TO},
 
{  WWT_SCROLLBAR,    14,   320,   330,    14,    61, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHTXTBTN,    14,     0,   164,    62,    73, STR_A003_NEW_AIRCRAFT, STR_A022_BUILD_NEW_AIRCRAFT},
 
{ WWT_PUSHTXTBTN,    14,   165,   330,    62,    73, STR_00E4_LOCATION, STR_A024_CENTER_MAIN_VIEW_ON_HANGAR},
 
{      WWT_LAST},
 
};
 

	
 

	
 
static void AircraftDepotWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT:
 
		DrawAircraftDepotWindow(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		switch(e->click.widget) {
 
		case 2: /* click aircraft */
 
			AircraftDepotClickAircraft(w, e->click.pt.x, e->click.pt.y);
 
			break;
 
		case 5: /* show build aircraft window */
 
			ShowBuildAircraftWindow(w->window_number);
 
			break;
 
		case 6: /* scroll to tile */
 
			ScrollMainWindowToTile(w->window_number);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DESTROY:
 
		DeleteWindowById(WC_BUILD_VEHICLE, w->window_number);
 
		break;
 

	
 
	case WE_DRAGDROP: {
 
		switch(e->click.widget) {
 
		case 2: {
 
			Vehicle *v;
 
			VehicleID sel = WP(w,traindepot_d).sel;
 

	
 
			WP(w,traindepot_d).sel = INVALID_VEHICLE;
 
			SetWindowDirty(w);
 

	
 
			if (GetVehicleFromAircraftDepotWndPt(w, e->dragdrop.pt.x, e->dragdrop.pt.y, &v) == 0 &&
 
					v != NULL &&
 
					sel == v->index) {
 
				ShowAircraftViewWindow(v);
 
			}
 
		} break;
 

	
 
		case 3:
 
			if (!HASBIT(w->disabled_state, 3) &&
 
					WP(w,traindepot_d).sel != INVALID_VEHICLE)	{
 
				Vehicle *v;
 

	
 
				HandleButtonClick(w, 3);
 

	
 
				v = &_vehicles[WP(w,traindepot_d).sel];
 
				WP(w,traindepot_d).sel = INVALID_VEHICLE;
 

	
 
				_backup_orders_tile = v->tile;
 
				BackupVehicleOrders(v, _backup_orders_data);
 

	
 
				if (!DoCommandP(v->tile, v->index, 0, NULL,  CMD_SELL_AIRCRAFT | CMD_MSG(STR_A01C_CAN_T_SELL_AIRCRAFT)))
 
					_backup_orders_tile = 0;
 
			}
 
			break;
 
		default:
 
			WP(w,traindepot_d).sel = INVALID_VEHICLE;
 
			SetWindowDirty(w);
 
		}
 
		break;
 
	}
 
	break;
 
	}
 
}
 

	
 

	
 

	
 
static const WindowDesc _aircraft_depot_desc = {
 
	-1, -1, 331, 74,
 
	WC_VEHICLE_DEPOT,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_aircraft_depot_widgets,
 
	AircraftDepotWndProc
 
};
 

	
 

	
 
void ShowAircraftDepotWindow(uint tile)
 
{
 
	Window *w;
 

	
 
	w = AllocateWindowDescFront(&_aircraft_depot_desc, tile);
 
	if (w) {
 
		w->caption_color = _map_owner[tile];
 
		w->vscroll.cap = 2;
 
		WP(w,traindepot_d).sel = INVALID_VEHICLE;
 
		_backup_orders_tile = 0;
 
	}
 
}
 

	
 
static void DrawSmallSchedule(Vehicle *v, int x, int y) {
 
	uint16 *sched;
 
	int sel;
 
	uint ord;
 
	int i = 0;
 

	
 
	sched = v->schedule_ptr;
 
	sel = v->cur_order_index;
 

	
 
	while ((ord=*sched++) != 0) {
 
		if (sel == 0) {
 
			_stringwidth_base = 0xE0;
 
			DoDrawString( "\xAF", x-6, y, 16);
 
			_stringwidth_base = 0;
 
		}
 
		sel--;
 

	
 
		if ((ord & OT_MASK) == OT_GOTO_STATION) {
 
			SET_DPARAM16(0, ord >> 8);
 
			DrawString(x, y, STR_A036, 0);
 

	
 
			y += 6;
 
			if (++i == 4)
 
				break;
 
		}
 
	}
 
}
 

	
 
static void PlayerAircraftWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT:
 
		/* determine amount of items for scroller */
 
		{
 
			Vehicle *v;
 
			int num = 0;
 
			byte owner = (byte)w->window_number;
 

	
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner)
 
					num++;
 
			}
 

	
 
			SetVScrollCount(w, num);
 
		}
 

	
 
		/* draw the widgets */
 
		{
 
			Player *p = DEREF_PLAYER(w->window_number);
 
			SET_DPARAM16(0, p->name_1);
 
			SET_DPARAM32(1, p->name_2);
 
			DrawWindowWidgets(w);
 
		}
 

	
 
		/* draw the aircraft */
 
		{
 
			Vehicle *v;
 
			int pos = w->vscroll.pos;
 
			byte owner = (byte)w->window_number;
 
			int x = 2;
 
			int y = 15;
 

	
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner &&
 
						--pos < 0 && pos >= -4) {
 
					StringID str;
 

	
 
					DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE);
 
					DrawVehicleProfitButton(v, x, y+13);
 

	
 
					SET_DPARAM16(0, v->unitnumber);
 
					if (IsAircraftHangarTile(v->tile)) {
 
						str = STR_021F;
 
					} else {
 
						str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
 
					}
 
					DrawString(x, y+2, str, 0);
 

	
 
					SET_DPARAM32(0, v->profit_this_year);
 
					SET_DPARAM32(1, v->profit_last_year);
 
					DrawString(x+19, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
 

	
 
					if (v->string_id != STR_SV_AIRCRAFT_NAME) {
 
						SET_DPARAM16(0, v->string_id);
 
						DrawString(x+19, y, STR_01AB, 0);
 
					}
 

	
 
					DrawSmallSchedule(v, x+136, y);
 

	
 
					y += 36;
 
				}
 
			}
 
		}
 
		break;
 

	
 
	case WE_CLICK:
 
		switch(e->click.widget) {
 
		case 2: { /* click on aircraft */
 
			int sel;
 
			Vehicle *v;
 
			byte owner;
 

	
 
			sel = (e->click.pt.y - 14) / 36;
 

	
 
			if ((uint)sel >= 4)
 
				break;
 
			sel += w->vscroll.pos;
 
			owner = (byte)w->window_number;
 
			FOR_ALL_VEHICLES(v) {
 
				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner &&
 
						--sel < 0) {
 
					ShowAircraftViewWindow(v);
 
					break;
 
				}
 
			}
 
			break;
 
		}
 
		case 4: { /* build new */
 
			uint tile;
 

	
 
			tile = _last_built_aircraft_depot_tile;
 
			do {
 
				if (_map_owner[tile] == _local_player &&
 
						IsAircraftHangarTile(tile)) {
 
					ShowAircraftDepotWindow(tile);
 
					ShowBuildAircraftWindow(tile);
 
					return;
 
				}
 

	
 
				tile = TILE_MASK(tile + 1);
 
			} while(tile != _last_built_aircraft_depot_tile);
 
			ShowBuildAircraftWindow(0);
 
		} break;
 
		}
 
		break;
 
	}
 
}
 

	
 
static const Widget _player_aircraft_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
 
{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{ WWT_PUSHTXTBTN,    14,     0,   129,   158,   169, STR_A003_NEW_AIRCRAFT, STR_A020_BUILD_NEW_AIRCRAFT_REQUIRES},
 
{     WWT_IMGBTN,    14,   130,   259,   158,   169, 0x0, 0},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _player_aircraft_desc = {
 
	-1, -1, 260, 170,
 
	WC_AIRCRAFT_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_player_aircraft_widgets,
 
	PlayerAircraftWndProc
 
};
 

	
 
static const Widget _other_player_aircraft_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
 
{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _other_player_aircraft_desc = {
 
	-1, -1, 260, 158,
 
	WC_AIRCRAFT_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_other_player_aircraft_widgets,
 
	PlayerAircraftWndProc
 
};
 

	
 
void ShowPlayerAircraft(int player)
 
{
 
	Window *w;
 

	
 
	if ( player == _local_player) {
 
		w = AllocateWindowDescFront(&_player_aircraft_desc, player);
 
	} else  {
 
		w = AllocateWindowDescFront(&_other_player_aircraft_desc, player);
 
	}
 

	
 
	if (w) {
 
		w->caption_color = w->window_number;
 
		w->vscroll.cap = 4;
 
	}
 
}
airport.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "airport.h"
 

	
 
AirportFTAClass *CountryAirport;
 
AirportFTAClass *CityAirport;
 
AirportFTAClass *Heliport, *Oilrig;
 
AirportFTAClass *MetropolitanAirport;
 
AirportFTAClass *InternationalAirport;
 

	
 
static void AirportFTAClass_Constructor(AirportFTAClass *Airport,
 
																				const byte nofterminals, const byte nofterminalgroups,
 
																				const byte nofhelipads,  const byte nofhelipadgroups,
 
																				const byte entry_point,  const byte acc_planes,
 
																				const AirportFTAbuildup *FA,
 
																				const TileIndex *depots);
 
static void AirportFTAClass_Destructor(AirportFTAClass *Airport);
 

	
 
static uint16 AirportGetNofElements(const AirportFTAbuildup *FA);
 
static void AirportBuildAutomata(AirportFTAClass *Airport, const AirportFTAbuildup *FA);
 
static byte AirportTestFTA(const AirportFTAClass *Airport);
 
/*static void AirportPrintOut(const AirportFTAClass *Airport, const bool full_report);
 
static byte AirportBlockToString(uint32 block);*/
 

	
 
void InitializeAirports()
 
{
 
	// country airport
 
	CountryAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 
	AirportFTAClass_Constructor(CountryAirport, 2, 1, 0, 0, 16, ALL, _airport_fta_country, _airport_depots_country);
 

	
 
	// city airport
 
	CityAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 
	AirportFTAClass_Constructor(CityAirport, 3, 1, 0, 0, 19, ALL, _airport_fta_city, _airport_depots_city);
 

	
 
	// metropolitan airport
 
	MetropolitanAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 
	AirportFTAClass_Constructor(MetropolitanAirport, 3, 1, 0, 0, 20, ALL, _airport_fta_metropolitan, _airport_depots_metropolitan);
 

	
 
	// international airport
 
	InternationalAirport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 
	AirportFTAClass_Constructor(InternationalAirport, 6, 2, 2, 1, 37, ALL, _airport_fta_international, _airport_depots_international);
 

	
 
	// heliport, oilrig
 
	Heliport = (AirportFTAClass *)malloc(sizeof(AirportFTAClass));
 
	AirportFTAClass_Constructor(Heliport, 0, 0, 1, 1, 7, HELICOPTERS_ONLY, _airport_fta_heliport_oilrig, _airport_depots_heliport_oilrig);
 
	Oilrig = Heliport;  // exactly the same structure for heliport/oilrig, so share state machine
 
}
 

	
 
void UnInitializeAirports()
 
{
 
	AirportFTAClass_Destructor(CountryAirport);
 
	AirportFTAClass_Destructor(CityAirport);
 
	AirportFTAClass_Destructor(Heliport);
 
	AirportFTAClass_Destructor(MetropolitanAirport);
 
	AirportFTAClass_Destructor(InternationalAirport);
 
}
 

	
 
static void AirportFTAClass_Constructor(AirportFTAClass *Airport,
 
																				const byte nofterminals, const byte nofterminalgroups,
 
																				const byte nofhelipads, const byte nofhelipadgroups,
 
																				const byte entry_point, const byte acc_planes,
 
																				const AirportFTAbuildup *FA,
 
																				const TileIndex *depots)
 
{
 
	// if there are more terminals than 6, internal variables have to be changed, so don't allow that
 
	// same goes for helipads
 
	if (nofterminals > MAX_TERMINALS) { printf("Currently only maximum of %2d terminals are supported (you wanted %2d)\n", MAX_TERMINALS, nofterminals);}
 
	if (nofhelipads > MAX_HELIPADS) { printf("Currently only maximum of %2d helipads are supported (you wanted %2d)\n", MAX_HELIPADS, nofhelipads);}
 
	// terminals/helipads are divided into groups. Groups are computed by dividing the number
 
	// of terminals by the number of groups. Half in half. If #terminals is uneven, first group
 
	// will get the less # of terminals
 
	if (nofterminalgroups > nofterminals) { printf("# of terminalgroups (%2d) must be less or equal to terminals (%2d)", nofterminals, nofterminalgroups);}
 
	if (nofhelipadgroups > nofhelipads) { printf("# of helipadgroups (%2d) must be less or equal to helipads (%2d)", nofhelipads, nofhelipadgroups);}
 

	
 
	assert(nofterminals <= MAX_TERMINALS);
 
	assert(nofhelipads <= MAX_HELIPADS);
 
	assert(nofterminalgroups <= nofterminals);
 
	assert(nofhelipadgroups <= nofhelipads);
 

	
 
	Airport->nofelements = AirportGetNofElements(FA);
 
	// check
 
	if (entry_point >= Airport->nofelements) {printf("Entry point (%2d) must be within the airport positions (which is max %2d)\n", entry_point, Airport->nofelements);}
 
	assert(entry_point < Airport->nofelements);
 

	
 
	Airport->nofterminals = nofterminals;
 
	Airport->nofterminalgroups = nofterminalgroups;
 
	Airport->nofhelipads = nofhelipads;
 
	Airport->nofhelipadgroups = nofhelipadgroups;
 
	Airport->acc_planes = acc_planes;
 
	Airport->entry_point = entry_point;
 
	Airport->airport_depots = (uint16*)depots;
 

	
 

	
 
	// build the state machine
 
	AirportBuildAutomata(Airport, FA);
 
	//#ifdef _DEBUG
 
	//	{printf("#Elements %2d; #Terminals %2d in %d group(s); #Helipads %2d in %d group(s)\n", Airport->nofelements,
 
	//				  Airport->nofterminals, Airport->nofterminalgroups, Airport->nofhelipads, Airport->nofhelipadgroups);}
 
	//#endif
 

	
 

	
 
	{
 
		byte _retval = AirportTestFTA(Airport);
 
		if (_retval != MAX_ELEMENTS) {printf("ERROR with element: %d\n", _retval-1);}
 
		assert(_retval == MAX_ELEMENTS);
 
	}
 
	// print out full information
 
	// true  -- full info including heading, block, etc
 
	// false -- short info, only position and next position
 
	//AirportPrintOut(Airport, false);
 
}
 

	
 
static void AirportFTAClass_Destructor(AirportFTAClass *Airport)
 
{
 
	int i;
 
	AirportFTA *current, *next;
 

	
 
	for (i = 0; i < Airport->nofelements; i++) {
 
		current = Airport->layout[i].next_in_chain;
 
		while (current != NULL) {
 
			next = current->next_in_chain;
 
			free(current);
 
			current = next;
 
		};
 
	}
 
	free(Airport->layout);
 
	free(Airport);
 
}
 

	
 
static uint16 AirportGetNofElements(const AirportFTAbuildup *FA)
 
{
 
	int i;
 
	uint16 nofelements = 0;
 
	int temp = FA[0].position;
 
	for (i = 0; i < MAX_ELEMENTS; i++) {
 
		if (temp != FA[i].position) {
 
			nofelements++;
 
			temp = FA[i].position;
 
		}
 
		if (FA[i].position == MAX_ELEMENTS) {break;}
 
	}
 
	return nofelements;
 
}
 

	
 
static void AirportBuildAutomata(AirportFTAClass *Airport, const AirportFTAbuildup *FA)
 
{
 
	AirportFTA *FAutomata;
 
	AirportFTA *current;
 
	uint16 internalcounter, i;
 
	FAutomata = (AirportFTA *)malloc(sizeof(AirportFTA) * Airport->nofelements);
 
	Airport->layout = FAutomata;
 
	internalcounter = 0;
 

	
 
	for (i = 0; i < Airport->nofelements; i++) {
 
		current = &Airport->layout[i];
 
		current->position = FA[internalcounter].position;
 
		current->heading  = FA[internalcounter].heading;
 
		current->block    = FA[internalcounter].block;
 
		current->next_position = FA[internalcounter].next_in_chain;
 

	
 
		// outgoing nodes from the same position, create linked list
 
		while (current->position == FA[internalcounter+1].position) {
 
			AirportFTA *newNode = (AirportFTA *)malloc(sizeof(AirportFTA));
 
			newNode->position = FA[internalcounter+1].position;
 
			newNode->heading  = FA[internalcounter+1].heading;
 
			newNode->block    = FA[internalcounter+1].block;
 
			newNode->next_position = FA[internalcounter+1].next_in_chain;
 
			// create link
 
			current->next_in_chain = newNode;
 
			current = current->next_in_chain;
 
			internalcounter++;
 
		} // while
 
		current->next_in_chain = NULL;
 
		internalcounter++;
 
	}
 
}
 

	
 
static byte AirportTestFTA(const AirportFTAClass *Airport)
 
{
 
	byte position, i, next_element;
 
	AirportFTA *temp;
 
	next_element = 0;
 

	
 
	for (i = 0; i < Airport->nofelements; i++) {
 
		position = Airport->layout[i].position;
 
		if (position != next_element) {return i;}
 
		temp = &Airport->layout[i];
 

	
 
		do {
 
			if (temp->heading > MAX_HEADINGS && temp->heading != 255) {return i;}
 
			if (temp->heading == 0 && temp->next_in_chain != 0) {return i;}
 
			if (position != temp->position) {return i;}
 
			if (temp->next_position >= Airport->nofelements) {return i;}
 
			temp = temp->next_in_chain;
 
		} while (temp != NULL);
 
		next_element++;
 
	}
 
	return MAX_ELEMENTS;
 
}
 

	
 
static const char* const _airport_heading_strings[MAX_HEADINGS+2] = {
 
	"TO_ALL",
 
	"HANGAR",
 
	"TERM1",
 
	"TERM2",
 
	"TERM3",
 
	"TERM4",
 
	"TERM5",
 
	"TERM6",
 
	"HELIPAD1",
 
	"HELIPAD2",
 
	"TAKEOFF",
 
	"STARTTAKEOFF",
 
	"ENDTAKEOFF",
 
	"HELITAKEOFF",
 
	"FLYING",
 
	"LANDING",
 
	"ENDLANDING",
 
	"HELILANDING",
 
	"HELIENDLANDING",
 
	"DUMMY"	// extra heading for 255
 
};
 

	
 
/*
 
static void AirportPrintOut(const AirportFTAClass *Airport, const bool full_report)
 
{
 
	AirportFTA *temp;
 
	uint16 i;
 
	byte heading;
 

	
 
	printf("(P = Current Position; NP = Next Position)\n");
 
	for (i = 0; i < Airport->nofelements; i++) {
 
		temp = &Airport->layout[i];
 
		if (full_report) {
 
			heading = (temp->heading == 255) ? MAX_HEADINGS+1 : temp->heading;
 
			printf("Pos:%2d NPos:%2d Heading:%15s Block:%2d\n", temp->position, temp->next_position,
 
						 _airport_heading_strings[heading], AirportBlockToString(temp->block));
 
		}
 
		else { printf("P:%2d NP:%2d", temp->position, temp->next_position);}
 
		while (temp->next_in_chain != NULL) {
 
			temp = temp->next_in_chain;
 
			if (full_report) {
 
				heading = (temp->heading == 255) ? MAX_HEADINGS+1 : temp->heading;
 
				printf("Pos:%2d NPos:%2d Heading:%15s Block:%2d\n", temp->position, temp->next_position,
 
							_airport_heading_strings[heading], AirportBlockToString(temp->block));
 
			}
 
			else { printf("P:%2d NP:%2d", temp->position, temp->next_position);}
 
		}
 
		printf("\n");
 
	}
 
}
 

	
 

	
 
static byte AirportBlockToString(uint32 block)
 
{
 
	byte 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;
 
}*/
 

	
 
const AirportFTAClass* GetAirport(const byte airport_type)
 
{
 
	AirportFTAClass *Airport = NULL;
 
	//FIXME -- AircraftNextAirportPos_and_Order -> Needs something nicer, don't like this code
 
	// needs constant change if more airports are added
 
	switch (airport_type) {
 
		case AT_SMALL: Airport = CountryAirport; break;
 
		case AT_LARGE: Airport = CityAirport; break;
 
		case AT_METROPOLITAN: Airport = MetropolitanAirport; break;
 
		case AT_HELIPORT: Airport = Heliport; break;
 
		case AT_OILRIG: Airport = Oilrig; break;
 
		case AT_INTERNATIONAL: Airport = InternationalAirport; break;
 
		default:
 
			#ifdef DEBUG__
 
				printf("Airport AircraftNextAirportPos_and_Order not yet implemented\n");
 
			#endif
 
			assert(airport_type <= AT_INTERNATIONAL);
 
	}
 
	return Airport;
 
}
airport.h
Show inline comments
 
new file 100644
 
#ifndef AIRPORT_H
 
#define AIRPORT_H
 

	
 
#include "airport_movement.h"
 

	
 
enum {MAX_TERMINALS = 6};
 
enum {MAX_HELIPADS  = 2};
 

	
 
// Airport types
 
enum {
 
	AT_SMALL = 0,
 
	AT_LARGE = 1,
 
	AT_HELIPORT = 2,
 
	AT_METROPOLITAN = 3,
 
	AT_INTERNATIONAL = 4,
 
	AT_OILRIG = 5
 
};
 

	
 
// 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
 
};
 

	
 
// Finite sTate mAchine --> FTA
 
typedef struct AirportFTAClass {
 
	byte nofelements;						// number of positions the airport consists of
 
	byte nofterminals;					// number of terminals this airport has
 
	byte nofterminalgroups;			// terminals belong to so many groups (MAX is the nofterminals)
 
	byte nofhelipads;						// number of helipads this airport has
 
	byte nofhelipadgroups;			// helipads belong to so many groups (MAX is the nofhelipads)
 
	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
 
	uint16 *airport_depots;			// gives the position of the depots on the airports
 
	struct AirportFTA *layout;	// state machine for airport
 
} 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_in_chain;	// possible extra movement choices from this position
 
} AirportFTA;
 

	
 
void InitializeAirports();
 
void UnInitializeAirports();
 
const AirportFTAClass* GetAirport(const byte airport_type);
 

	
 
#endif /* AIRPORT_H */
airport_gui.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 

	
 
#include "window.h"
 
#include "gui.h"
 
#include "viewport.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "vehicle.h"
 
#include "station.h"
 
#include "airport.h"
 

	
 
static byte _selected_airport_type;
 

	
 
static void ShowBuildAirportPicker();
 

	
 

	
 
static void CcBuildAirport(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(0x1D, tile);
 
		ResetObjectToPlace();
 
	}
 
}
 

	
 
static void PlaceAirport(uint 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(uint tile)
 
{
 
	VpStartPlaceSizing(tile, 4);
 
}
 

	
 

	
 
static void BuildAirClick_Airport(Window *w)
 
{
 
	if (HandlePlacePushButton(w, 2, 0xAA4, 1, PlaceAirport)) ShowBuildAirportPicker();
 
}
 

	
 
static void BuildAirClick_Demolish(Window *w)
 
{
 
	HandlePlacePushButton(w, 3, ANIMCURSOR_DEMOLISH, 1, PlaceAir_DemolishArea);
 
}
 

	
 
static void BuildAirClick_Lower(Window *w)
 
{
 
	HandlePlacePushButton(w, 4, ANIMCURSOR_LOWERLAND, 2, PlaceProc_LowerLand);
 
}
 

	
 
static void BuildAirClick_Raise(Window *w)
 
{
 
	HandlePlacePushButton(w, 5, ANIMCURSOR_RAISELAND, 2, PlaceProc_RaiseLand);
 
}
 

	
 
static void BuildAirClick_Purchase(Window *w)
 
{
 
	HandlePlacePushButton(w, 6, 0x12B8, 1, PlaceProc_BuyLand);
 
}
 

	
 
typedef void OnButtonClick(Window *w);
 
static OnButtonClick * const _build_air_button_proc[] = {
 
	BuildAirClick_Airport,
 
	BuildAirClick_Demolish,
 
	BuildAirClick_Lower,
 
	BuildAirClick_Raise,
 
	BuildAirClick_Purchase,
 
};
 

	
 
static void BuildAirToolbWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT:
 
		DrawWindowWidgets(w);
 
		break;
 

	
 
	case WE_CLICK:
 
		if (e->click.widget-2 >= 0)
 
			_build_air_button_proc[e->click.widget - 2](w);		
 
		break;
 

	
 
	case WE_PLACE_OBJ:
 
		_place_proc(e->place.tile);
 
		break;	
 

	
 
	case WE_PLACE_DRAG: {
 
		VpSelectTilesWithMethod(e->place.pt.x, e->place.pt.y, e->place.userdata);
 
		return;
 
	}
 

	
 
	case WE_PLACE_MOUSEUP:
 
		if (e->place.pt.x != -1) {
 
			DoCommandP(e->place.tile, e->place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
 
		}
 
		break;
 

	
 
	case WE_ABORT_PLACE_OBJ:
 
		w->click_state = 0;
 
		SetWindowDirty(w);
 
		w = FindWindowById(WC_BUILD_STATION, 0);
 
		if (w != 0)
 
			WP(w,def_d).close = true;
 
		break;
 
	}
 
}
 

	
 
static const Widget _air_toolbar_widgets[] = {
 
{   WWT_CLOSEBOX,     7,     0,    10,     0,    13, STR_00C5,										STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,     7,    11,   129,     0,    13, STR_A000_AIRPORT_CONSTRUCT,	STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,     7,     0,    41,    14,    35, 0x2E8,												STR_A01E_BUILD_AIRPORT},
 
{      WWT_PANEL,     7,    42,    63,    14,    35, 0x2BF,												STR_018D_DEMOLISH_BUILDINGS_ETC},
 
{      WWT_PANEL,     7,    64,    85,    14,    35, 0x2B7,												STR_018E_LOWER_A_CORNER_OF_LAND},
 
{      WWT_PANEL,     7,    86,   107,    14,    35, 0x2B6,												STR_018F_RAISE_A_CORNER_OF_LAND},
 
{      WWT_PANEL,     7,   108,   129,    14,    35, 0x12B7,											STR_0329_PURCHASE_LAND_FOR_FUTURE},
 
{      WWT_LAST},
 
};
 

	
 

	
 
static const WindowDesc _air_toolbar_desc = {
 
	510, 22, 130, 36,
 
	WC_BUILD_TOOLBAR,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_air_toolbar_widgets,
 
	BuildAirToolbWndProc
 
};
 

	
 
void ShowBuildAirToolbar()
 
{
 
	DeleteWindowById(WC_BUILD_TOOLBAR, 0);
 
	AllocateWindowDescFront(&_air_toolbar_desc, 0);
 
}
 

	
 
static void BuildAirportPickerWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		int sel;
 

	
 
		if (WP(w,def_d).close)
 
			return;
 
		w->disabled_state = 0;
 

	
 
		sel = _selected_airport_type;
 
		// FIXME -- BuildAirportPickerWndProc - set availability of airports by year, instead of airplane
 
		if (!(_avail_aircraft & 1)) { w->disabled_state |= (1<<3); if (sel == AT_SMALL) sel = AT_LARGE; }
 
		if (!(_avail_aircraft & 2)) {	w->disabled_state |= (1<<4); if (sel == AT_LARGE) sel = AT_SMALL; }
 
		if (!(_avail_aircraft & 4)) {	w->disabled_state |= (1<<5); }	// heliport
 
		// 1980-1-1 is --> 21915
 
		// 1990-1-1 is --> 25568
 
		if (_date < 21915) {w->disabled_state |= (1<<6);}	// metropilitan airport 1980
 
		if (_date < 25568) {w->disabled_state |= (1<<7);}	// international airport 1990
 
		_selected_airport_type = sel;
 
		// select default the coverage area to 'Off' (8)
 
		w->click_state = ((1<<3) << sel) | ((1<<8) << _station_show_coverage);
 
		SetTileSelectSize(_airport_size_x[sel],_airport_size_y[sel]);
 
		if (_station_show_coverage)	SetTileSelectBigSize(-4, -4, 8, 8);
 

	
 
		DrawWindowWidgets(w);
 
    // strings such as 'Size' and 'Coverage Area'
 
		DrawStringCentered(74, 16, STR_305B_SIZE, 0);
 
		DrawStringCentered(74, 78, STR_3066_COVERAGE_AREA_HIGHLIGHT, 0);
 
		DrawStationCoverageAreaText(2, 104, (uint)-1);		
 
		break;
 
	}
 

	
 
	case WE_CLICK: {
 
		switch(e->click.widget) {
 
		case 0:
 
			ResetObjectToPlace();
 
			break;
 
		case 3: case 4: case 5: case 6: case 7:
 
			_selected_airport_type = e->click.widget - 3;
 
			SndPlayFx(0x13);
 
			SetWindowDirty(w);
 
			break;
 
		case 8: case 9:
 
			_station_show_coverage = e->click.widget - 8;
 
			SndPlayFx(0x13);
 
			SetWindowDirty(w);
 
			break;
 
		}
 
	} break;
 

	
 
	case WE_MOUSELOOP: {
 
		if (WP(w,def_d).close) {
 
			DeleteWindow(w);
 
			return;
 
		}
 

	
 
		CheckRedrawStationCoverage(w);
 
	} break;
 
	}
 
}
 

	
 
static const Widget _build_airport_picker_widgets[] = {
 
{   WWT_CLOSEBOX,     7,     0,    10,     0,    13, STR_00C5,										STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,     7,    11,   147,     0,    13, STR_3001_AIRPORT_SELECTION,	STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,     7,     0,   147,    14,   130, 0x0,													0},
 
{WWT_NODISTXTBTN,    14,     2,    73,    27,    38, STR_3059_SMALL,							STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{WWT_NODISTXTBTN,    14,    74,   145,    27,    38, STR_305A_LARGE,							STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{WWT_NODISTXTBTN,    14,     2,   145,    63,    74, STR_306B_HELIPORT,						STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{WWT_NODISTXTBTN,    14,     2,   145,    39,    50, STR_305AA_LARGE,	  					STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{WWT_NODISTXTBTN,    14,     2,   145,    51,    62, STR_305AB_LARGE,	  					STR_3058_SELECT_SIZE_TYPE_OF_AIRPORT},
 
{   WWT_CLOSEBOX,    14,    14,    73,    88,    98, STR_02DB_OFF,								STR_3065_DON_T_HIGHLIGHT_COVERAGE},
 
{   WWT_CLOSEBOX,    14,    74,   133,    88,    98, STR_02DA_ON,									STR_3064_HIGHLIGHT_COVERAGE_AREA},
 
{      WWT_LAST},
 
};
 

	
 
static const WindowDesc _build_airport_desc = {
 
	-1, -1, 148, 131, // height, 130+1
 
	WC_BUILD_STATION,WC_BUILD_TOOLBAR,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_build_airport_picker_widgets,
 
	BuildAirportPickerWndProc
 
};
 

	
 
static void ShowBuildAirportPicker()
 
{
 
	AllocateWindowDesc(&_build_airport_desc);
 
}
 

	
 
void InitializeAirportGui()
 
{
 
	_selected_airport_type = AT_SMALL;
 
	_last_built_aircraft_depot_tile = 0;
 
}

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)